mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-11-30 19:13:53 +01:00
Merge branch 'main' of https://github.com/sbrl/Minetest-WorldEditAdditions into main
This commit is contained in:
commit
d9069b3cfa
12
CHANGELOG.md
12
CHANGELOG.md
@ -7,10 +7,14 @@ Note to self: See the bottom of this file for the release template text.
|
|||||||
- Add `//spush`, `//spop`, and `//sstack`
|
- Add `//spush`, `//spop`, and `//sstack`
|
||||||
- `//overlay`: Don't place nodes above water
|
- `//overlay`: Don't place nodes above water
|
||||||
- `//multi`: Improve resilience by handling some edge cases
|
- `//multi`: Improve resilience by handling some edge cases
|
||||||
- Add `//srect` (_select rectangle_), `//scol` (_select column_), `//scube` (_select cube_) - thanks, @VorTechnix!
|
- Add `//srect` (_select rectangle_), `//scol` (_select column_), `//scube` (_select cube_) - thanks, @VorTechnix!
|
||||||
- Add `//scloud` (_select point cloud_), `//scentre` (_select centre node(s)_), `//srel` (_select relative_) - thanks, @VorTechnix!
|
- Add `//scloud` (_select point cloud_), `//scentre` (_select centre node(s)_), `//srel` (_select relative_) - thanks, @VorTechnix!
|
||||||
- Significantly refactored backend utility functions (more to come in future updates)
|
- Significantly refactored backend utility functions (more to come in future updates)
|
||||||
- `//bonemeal`: Try bonemealing everything that isn't an air block (#49)
|
- `//bonemeal`: Try bonemealing everything that isn't an air block (#49)
|
||||||
|
- Add new universal chance parsing
|
||||||
|
- Any `<chance>` can now either be a 1-in-N number (e.g. `4`, `10`), or a percentage chance (e.g. `50%`, `10%`).
|
||||||
|
- Caveat: Percentages are converted to a 1-in-N chance, but additionally that number is rounded down in some places
|
||||||
|
- `//torus`, `//hollowtorus`: Add optional new axes argument
|
||||||
|
|
||||||
|
|
||||||
## v1.11: The big data update (25th January 2021)
|
## v1.11: The big data update (25th January 2021)
|
||||||
|
@ -37,6 +37,7 @@ Note also that columns without any air nodes in them at all are also skipped, so
|
|||||||
//overlay grass_with_dirt 10 dirt
|
//overlay grass_with_dirt 10 dirt
|
||||||
//overlay grass_with_dirt 10 dirt 2 sand 1
|
//overlay grass_with_dirt 10 dirt 2 sand 1
|
||||||
//overlay sandstone dirt 2 sand 5
|
//overlay sandstone dirt 2 sand 5
|
||||||
|
//overlay dirt 90% stone 10%
|
||||||
```
|
```
|
||||||
|
|
||||||
## `//layers [<node_name_1> [<layer_count_1>]] [<node_name_2> [<layer_count_2>]] ...`
|
## `//layers [<node_name_1> [<layer_count_1>]] [<node_name_2> [<layer_count_2>]] ...`
|
||||||
@ -123,12 +124,15 @@ Creates a hollow ellipsoid at position 1 with the radius `(rx, ry, rz)`. Works t
|
|||||||
//hollowellipsoid 21 11 41 stone
|
//hollowellipsoid 21 11 41 stone
|
||||||
```
|
```
|
||||||
|
|
||||||
## `//torus <major_radius> <minor_radius> <node_name>`
|
## `//torus <major_radius> <minor_radius> <node_name> [<axes=xy>]`
|
||||||
Creates a solid torus at position 1 with the specified major and minor radii. The major radius is the distance from the centre of the torus to the centre of the circle bit, and the minor radius is the radius of the circle bit.
|
Creates a solid torus at position 1 with the specified major and minor radii. The major radius is the distance from the centre of the torus to the centre of the circle bit, and the minor radius is the radius of the circle bit.
|
||||||
|
|
||||||
|
The optional axes sets the axes upon which the torus will lay flat. Possible values: `xy` (the default), `xz`, `yz`.
|
||||||
|
|
||||||
```
|
```
|
||||||
//torus 15 5 stone
|
//torus 15 5 stone
|
||||||
//torus 5 3 meselamp
|
//torus 5 3 meselamp
|
||||||
|
//hollowtorus 10 6 sandstone xz
|
||||||
```
|
```
|
||||||
|
|
||||||
## `//hollowtorus <major_radius> <minor_radius> <node_name>`
|
## `//hollowtorus <major_radius> <minor_radius> <node_name>`
|
||||||
@ -137,6 +141,7 @@ Creates a hollow torus at position 1 with the radius major and minor radii. Work
|
|||||||
```
|
```
|
||||||
//hollowtorus 10 5 glass
|
//hollowtorus 10 5 glass
|
||||||
//hollowtorus 21 11 stone
|
//hollowtorus 21 11 stone
|
||||||
|
//hollowtorus 18 6 dirt xz
|
||||||
```
|
```
|
||||||
|
|
||||||
## `//line [<replace_node> [<radius>]]`
|
## `//line [<replace_node> [<radius>]]`
|
||||||
@ -212,12 +217,15 @@ Also optionally takes a chance number. This is the chance that an eligible node
|
|||||||
|
|
||||||
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%.
|
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%.
|
||||||
|
|
||||||
|
Since WorldEditAdditions v1.12, a percentage chance is also supported. This is denoted by suffixing a number with a percent sign (e.g. `//bonemeal 1 25%`).
|
||||||
|
|
||||||
```
|
```
|
||||||
//bonemeal
|
//bonemeal
|
||||||
//bonemeal 3 25
|
//bonemeal 3 25
|
||||||
//bonemeal 4
|
//bonemeal 4
|
||||||
//bonemeal 1 10
|
//bonemeal 1 10
|
||||||
//bonemeal 2 15
|
//bonemeal 2 15
|
||||||
|
//bonemeal 2 10%
|
||||||
```
|
```
|
||||||
|
|
||||||
## `//walls <replace_node>`
|
## `//walls <replace_node>`
|
||||||
@ -350,6 +358,14 @@ If we wanted to put all of the above features together into a single command, th
|
|||||||
|
|
||||||
The above replaces 1 in 3 `dirt` nodes with a mix of `sandstone`, `dry_dirt`, and `cobble` nodes in the ratio 10:1:2. Awesome!
|
The above replaces 1 in 3 `dirt` nodes with a mix of `sandstone`, `dry_dirt`, and `cobble` nodes in the ratio 10:1:2. Awesome!
|
||||||
|
|
||||||
|
Since WorldEditAdditions v1.12, you can also use percentages:
|
||||||
|
|
||||||
|
```
|
||||||
|
//replacemix dirt 33% sandstone 75% dry_dirt 10% cobble 15%
|
||||||
|
```
|
||||||
|
|
||||||
|
Note though that the percentages are internally converted to a 1-in-N chance and rounded down.
|
||||||
|
|
||||||
Here are all the above examples together:
|
Here are all the above examples together:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
--- Overlap command. Places a specified node on top of
|
--- Overlap command. Places a specified node on top of
|
||||||
-- @module worldeditadditions.overlay
|
-- @module worldeditadditions.overlay
|
||||||
|
|
||||||
function worldeditadditions.torus(position, major_radius, minor_radius, target_node, hollow)
|
--- Generates a torus shape at the given position with the given parameters.
|
||||||
|
-- @param position Vector The position at which to generate the torus.
|
||||||
|
-- @param major_radius number The major radius of the torus - i.e. the distance from the middle to the ring.
|
||||||
|
-- @param minor_radius number The minor radius of the torus - i.e. the radius fo the ring itself.
|
||||||
|
-- @param target_node string The name of the target node to generate the torus with.
|
||||||
|
-- @param axes=xz string|nil The axes upon which the torus should lay flat.
|
||||||
|
-- @param hollow=false boolean Whether the generated torus should be hollow or not.
|
||||||
|
function worldeditadditions.torus(position, major_radius, minor_radius, target_node, axes, hollow)
|
||||||
|
if type(axes) ~= "string" then axes = "xz" end
|
||||||
|
|
||||||
-- position = { x, y, z }
|
-- position = { x, y, z }
|
||||||
local total_radius = major_radius + minor_radius
|
local total_radius = major_radius + minor_radius
|
||||||
local inner_minor_radius = minor_radius - 2
|
local inner_minor_radius = minor_radius - 2
|
||||||
@ -11,6 +20,7 @@ function worldeditadditions.torus(position, major_radius, minor_radius, target_n
|
|||||||
|
|
||||||
-- Fetch the nodes in the specified area
|
-- Fetch the nodes in the specified area
|
||||||
-- OPTIMIZE: We should be able to calculate a more efficient box-area here
|
-- OPTIMIZE: We should be able to calculate a more efficient box-area here
|
||||||
|
-- This is complicated by the multiple possible axes though
|
||||||
local manip, area = worldedit.manip_helpers.init_radius(position, total_radius)
|
local manip, area = worldedit.manip_helpers.init_radius(position, total_radius)
|
||||||
local data = manip:get_data()
|
local data = manip:get_data()
|
||||||
|
|
||||||
@ -33,6 +43,15 @@ function worldeditadditions.torus(position, major_radius, minor_radius, target_n
|
|||||||
for x = -total_radius, total_radius do
|
for x = -total_radius, total_radius do
|
||||||
local x_sq = x*x
|
local x_sq = x*x
|
||||||
|
|
||||||
|
local sq = vector.new(x_sq, y_sq, z_sq)
|
||||||
|
|
||||||
|
-- Default: xy
|
||||||
|
if axes == "xz" then
|
||||||
|
sq.x, sq.y, sq.z = sq.x, sq.z, sq.y
|
||||||
|
elseif axes == "yz" then
|
||||||
|
sq.x, sq.y, sq.z = sq.y, sq.z, sq.x
|
||||||
|
end
|
||||||
|
|
||||||
-- (x^2+y^2+z^2-(a^2+b^2))^2-4 a b (b^2-z^2)
|
-- (x^2+y^2+z^2-(a^2+b^2))^2-4 a b (b^2-z^2)
|
||||||
-- Where:
|
-- Where:
|
||||||
-- (x, y, z) is the point
|
-- (x, y, z) is the point
|
||||||
@ -47,8 +66,8 @@ function worldeditadditions.torus(position, major_radius, minor_radius, target_n
|
|||||||
|
|
||||||
if not place_ok then
|
if not place_ok then
|
||||||
-- It must be hollow! Do some additional calculations.
|
-- It must be hollow! Do some additional calculations.
|
||||||
local inner_comp_a = (x_sq+y_sq+z_sq - (major_radius_sq+inner_minor_radius_sq))
|
local inner_comp_a = (sq.x+sq.y+sq.z - (major_radius_sq+inner_minor_radius_sq))
|
||||||
local inner_test_value = inner_comp_a*inner_comp_a - 4*major_radius*inner_minor_radius*(inner_minor_radius_sq-z_sq)
|
local inner_test_value = inner_comp_a*inner_comp_a - 4*major_radius*inner_minor_radius*(inner_minor_radius_sq-sq.z)
|
||||||
|
|
||||||
-- It's only ok to place it if it's outside our inner torus
|
-- It's only ok to place it if it's outside our inner torus
|
||||||
place_ok = inner_test_value >= 0
|
place_ok = inner_test_value >= 0
|
||||||
|
@ -27,7 +27,7 @@ worldedit.register_command("bonemeal", {
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #parts >= 2 then
|
if #parts >= 2 then
|
||||||
chance = tonumber(parts[2])
|
chance = worldeditadditions.parse.chance(parts[2])
|
||||||
if not chance then
|
if not chance then
|
||||||
return false, "Invalid chance value (must be a positive integer)"
|
return false, "Invalid chance value (must be a positive integer)"
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
local we_c = worldeditadditions_commands
|
local wea = worldeditadditions
|
||||||
|
|
||||||
-- ██████ ███████ ██████ ██ █████ ██████ ███████ ███ ███ ██ ██ ██
|
-- ██████ ███████ ██████ ██ █████ ██████ ███████ ███ ███ ██ ██ ██
|
||||||
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██
|
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██
|
||||||
@ -15,7 +15,7 @@ worldedit.register_command("replacemix", {
|
|||||||
return false, "Error: No arguments specified"
|
return false, "Error: No arguments specified"
|
||||||
end
|
end
|
||||||
|
|
||||||
local parts = worldeditadditions.split(params_text, "%s+", false)
|
local parts = wea.split(params_text, "%s+", false)
|
||||||
|
|
||||||
local target_node = nil
|
local target_node = nil
|
||||||
local target_node_chance = 1
|
local target_node_chance = 1
|
||||||
@ -30,16 +30,16 @@ worldedit.register_command("replacemix", {
|
|||||||
return false, "Error: Invalid target_node name"
|
return false, "Error: Invalid target_node name"
|
||||||
end
|
end
|
||||||
mode = "target_chance"
|
mode = "target_chance"
|
||||||
elseif mode == "target_chance" and tonumber(part) then
|
elseif mode == "target_chance" and wea.parse.chance(part) then
|
||||||
target_node_chance = tonumber(part)
|
target_node_chance = wea.parse.chance(part)
|
||||||
mode = "replace_node"
|
mode = "replace_node"
|
||||||
elseif (mode == "target_chance" and not tonumber(part)) or mode == "replace_node" then
|
elseif (mode == "target_chance" and not wea.parse.chance(part, "weight")) or mode == "replace_node" then
|
||||||
mode = "replace_node"
|
mode = "replace_node"
|
||||||
if tonumber(part) then
|
if wea.parse.chance(part, "weight") then
|
||||||
if not last_node_name then
|
if not last_node_name then
|
||||||
return false, "Error: No previous node name was found (this is a probably a bug)."
|
return false, "Error: No previous node name was found (this is a probably a bug)."
|
||||||
end
|
end
|
||||||
replace_nodes[last_node_name] = math.floor(tonumber(part))
|
replace_nodes[last_node_name] = math.floor(wea.parse.chance(part, "weight"))
|
||||||
else
|
else
|
||||||
if last_node_name and not replace_nodes[last_node_name] then
|
if last_node_name and not replace_nodes[last_node_name] then
|
||||||
replace_nodes[last_node_name] = 1
|
replace_nodes[last_node_name] = 1
|
||||||
|
@ -4,33 +4,53 @@
|
|||||||
-- ██ ██ ██ ██ ██ ██ ██ ██
|
-- ██ ██ ██ ██ ██ ██ ██ ██
|
||||||
-- ██ ██████ ██ ██ ██████ ███████
|
-- ██ ██████ ██ ██ ██████ ███████
|
||||||
local function parse_params_torus(params_text)
|
local function parse_params_torus(params_text)
|
||||||
local found, _, major_radius, minor_radius, replace_node = params_text:find("([0-9]+)%s+([0-9]+)%s+([a-z:_\\-]+)")
|
local parts = worldeditadditions.split(params_text, "%s+", false)
|
||||||
|
|
||||||
if found == nil then
|
if #parts < 1 then
|
||||||
return nil, nil
|
return false, "Error: No replace_node specified."
|
||||||
|
end
|
||||||
|
if #parts < 2 then
|
||||||
|
return false, "Error: No major radius specified (expected integer greater than 0)."
|
||||||
|
end
|
||||||
|
if #parts < 3 then
|
||||||
|
return false, "Error: No minor radius specified (expected integer greater than 0)."
|
||||||
end
|
end
|
||||||
|
|
||||||
major_radius = tonumber(major_radius)
|
local major_radius = tonumber(parts[1])
|
||||||
minor_radius = tonumber(minor_radius)
|
local minor_radius = tonumber(parts[2])
|
||||||
|
local replace_node = worldedit.normalize_nodename(parts[3])
|
||||||
replace_node = worldedit.normalize_nodename(replace_node)
|
local axes
|
||||||
|
if #parts > 3 then axes = parts[4] end
|
||||||
|
if not axes then axes = "xy" end
|
||||||
|
|
||||||
|
if major_radius < 1 then
|
||||||
|
return false, "Error: The major radius must be greater than 0."
|
||||||
|
end
|
||||||
|
if minor_radius < 1 then
|
||||||
|
return false, "Error: The minor radius must be greater than 0."
|
||||||
|
end
|
||||||
if not replace_node then
|
if not replace_node then
|
||||||
return false, "Error: Invalid replace_node."
|
return false, "Error: Invalid node name."
|
||||||
end
|
end
|
||||||
if not major_radius or major_radius < 1 then
|
if axes:find("[^xyz]") then
|
||||||
return false, "Error: Invalid major radius (expected integer greater than 0)"
|
return false, "Error: The axes may only contain the letters x, y, and z."
|
||||||
end
|
end
|
||||||
if not minor_radius or minor_radius < 1 then
|
if #axes ~= 2 then
|
||||||
return false, "Error: Invalid minor radius (expected integer greater than 0)"
|
return false, "Error: Exactly 2 axes must be specified. For example, 'xy' is valid, but 'xyy' is not (both of course without quotes)."
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, replace_node, major_radius, minor_radius
|
|
||||||
|
-- Sort the axis names (this is important to ensure we can identify the direction properly)
|
||||||
|
if axes == "yx" then axes = "xy" end
|
||||||
|
if axes == "zx" then axes = "xz" end
|
||||||
|
if axes == "zy" then axes = "yz" end
|
||||||
|
|
||||||
|
return true, replace_node, major_radius, minor_radius, axes
|
||||||
end
|
end
|
||||||
|
|
||||||
worldedit.register_command("torus", {
|
worldedit.register_command("torus", {
|
||||||
params = "<major_radius> <minor_radius> <replace_node>",
|
params = "<major_radius> <minor_radius> <replace_node> [<axes=xy>]",
|
||||||
description = "Creates a 3D torus with a major radius of <major_radius> and a minor radius of <minor_radius> at pos1, filled with <replace_node>.",
|
description = "Creates a 3D torus with a major radius of <major_radius> and a minor radius of <minor_radius> at pos1, filled with <replace_node>, on axes <axes> (i.e. 2 axis names: xz, zy, etc).",
|
||||||
privs = { worldedit = true },
|
privs = { worldedit = true },
|
||||||
require_pos = 1,
|
require_pos = 1,
|
||||||
parse = function(params_text)
|
parse = function(params_text)
|
||||||
@ -40,9 +60,9 @@ worldedit.register_command("torus", {
|
|||||||
nodes_needed = function(name, target_node, major_radius, minor_radius)
|
nodes_needed = function(name, target_node, major_radius, minor_radius)
|
||||||
return math.ceil(2 * math.pi*math.pi * major_radius * minor_radius*minor_radius)
|
return math.ceil(2 * math.pi*math.pi * major_radius * minor_radius*minor_radius)
|
||||||
end,
|
end,
|
||||||
func = function(name, target_node, major_radius, minor_radius)
|
func = function(name, target_node, major_radius, minor_radius, axes)
|
||||||
local start_time = worldeditadditions.get_ms_time()
|
local start_time = worldeditadditions.get_ms_time()
|
||||||
local replaced = worldeditadditions.torus(worldedit.pos1[name], major_radius, minor_radius, target_node, false)
|
local replaced = worldeditadditions.torus(worldedit.pos1[name], major_radius, minor_radius, target_node, axes, false)
|
||||||
local time_taken = worldeditadditions.get_ms_time() - start_time
|
local time_taken = worldeditadditions.get_ms_time() - start_time
|
||||||
|
|
||||||
minetest.log("action", name .. " used //torus at " .. worldeditadditions.vector.tostring(worldedit.pos1[name]) .. ", replacing " .. replaced .. " nodes in " .. time_taken .. "s")
|
minetest.log("action", name .. " used //torus at " .. worldeditadditions.vector.tostring(worldedit.pos1[name]) .. ", replacing " .. replaced .. " nodes in " .. time_taken .. "s")
|
||||||
@ -52,20 +72,20 @@ worldedit.register_command("torus", {
|
|||||||
|
|
||||||
-- TODO: This duplicates a lot of code. Perhaps we can trim it down a bit?
|
-- TODO: This duplicates a lot of code. Perhaps we can trim it down a bit?
|
||||||
worldedit.register_command("hollowtorus", {
|
worldedit.register_command("hollowtorus", {
|
||||||
params = "<major_radius> <minor_radius> <replace_node>",
|
params = "<major_radius> <minor_radius> <replace_node> [<axes=xy>]",
|
||||||
description = "Creates a 3D hollow torus with a major radius of <major_radius> and a minor radius of <minor_radius> at pos1, made out of <replace_node>.",
|
description = "Creates a 3D hollow torus with a major radius of <major_radius> and a minor radius of <minor_radius> at pos1, made out of <replace_node>, on axes <axes> (i.e. 2 axis names: xz, zy, etc).",
|
||||||
privs = { worldedit = true },
|
privs = { worldedit = true },
|
||||||
require_pos = 1,
|
require_pos = 1,
|
||||||
parse = function(params_text)
|
parse = function(params_text)
|
||||||
local values = {parse_params_torus(params_text)}
|
local values = {parse_params_torus(params_text)}
|
||||||
return unpack(values)
|
return unpack(values)
|
||||||
end,
|
end,
|
||||||
nodes_needed = function(name, target_node, major_radius, minor_radius)
|
nodes_needed = function(name, target_node, major_radius, minor_radius, axes)
|
||||||
return math.ceil(2 * math.pi*math.pi * major_radius * minor_radius*minor_radius)
|
return math.ceil(2 * math.pi*math.pi * major_radius * minor_radius*minor_radius)
|
||||||
end,
|
end,
|
||||||
func = function(name, target_node, major_radius, minor_radius)
|
func = function(name, target_node, major_radius, minor_radius)
|
||||||
local start_time = worldeditadditions.get_ms_time()
|
local start_time = worldeditadditions.get_ms_time()
|
||||||
local replaced = worldeditadditions.torus(worldedit.pos1[name], major_radius, minor_radius, target_node, true)
|
local replaced = worldeditadditions.torus(worldedit.pos1[name], major_radius, minor_radius, target_node, axes, true)
|
||||||
local time_taken = worldeditadditions.get_ms_time() - start_time
|
local time_taken = worldeditadditions.get_ms_time() - start_time
|
||||||
|
|
||||||
minetest.log("action", name .. " used //hollowtorus at " .. worldeditadditions.vector.tostring(worldedit.pos1[name]) .. ", replacing " .. replaced .. " nodes in " .. time_taken .. "s")
|
minetest.log("action", name .. " used //hollowtorus at " .. worldeditadditions.vector.tostring(worldedit.pos1[name]) .. ", replacing " .. replaced .. " nodes in " .. time_taken .. "s")
|
||||||
|
Loading…
Reference in New Issue
Block a user