2018-05-20 10:52:16 +00:00
--- WorldEditAdditions-ChatCommands
-- @module worldeditadditions_commands
-- @release 0.1
-- @copyright 2018 Starbeamrainbowlabs
-- @license Mozilla Public License, 2.0
-- @author Starbeamrainbowlabs
2018-10-13 23:19:32 +00:00
dofile ( minetest.get_modpath ( " worldeditadditions_commands " ) .. " /multi.lua " )
2019-06-02 12:57:28 +00:00
local safe_region , check_region , reset_pending = dofile ( minetest.get_modpath ( " worldeditadditions_commands " ) .. " /safe.lua " )
2018-05-20 13:46:54 +00:00
2018-06-09 12:05:09 +00:00
-- ███████ ██ ██████ ██████ ██████ ███████ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- █████ ██ ██ ██ ██ ██ ██ ██ █████ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ███████ ██████ ██████ ██████ ██ ██ ███████ ███████
2018-05-20 13:46:54 +00:00
local function parse_params_floodfill ( params_text )
2018-10-14 11:59:00 +00:00
if not params_text then params_text = " " end
2018-05-20 13:46:54 +00:00
local found , _ , replace_node , radius = params_text : find ( " ([a-z:_ \\ -]+)%s+([0-9]+) " )
if found == nil then
found , _ , replace_node = params_text : find ( " ([a-z:_ \\ -]+) " )
2018-06-09 12:05:09 +00:00
radius = 20
2018-05-20 13:46:54 +00:00
end
if found == nil then
replace_node = " default:water_source "
end
radius = tonumber ( radius )
replace_node = worldedit.normalize_nodename ( replace_node )
return replace_node , radius
end
2018-05-20 10:52:16 +00:00
minetest.register_chatcommand ( " /floodfill " , {
2018-05-22 21:20:26 +00:00
params = " [<replace_node> [<radius>]] " ,
2018-06-09 12:05:09 +00:00
description = " Floods all connected nodes of the same type starting at pos1 with <replace_node> (which defaults to `water_source`), in a sphere with a radius of <radius> (which defaults to 20). " ,
2018-05-20 10:52:16 +00:00
privs = { worldedit = true } ,
2018-05-20 13:46:54 +00:00
func = safe_region ( function ( name , params_text )
local replace_node , radius = parse_params_floodfill ( params_text )
2018-05-20 11:23:09 +00:00
2018-05-20 13:46:54 +00:00
if not replace_node then
worldedit.player_notify ( name , " Error: Invalid node name. " )
return false
2018-05-20 10:52:16 +00:00
end
2018-05-20 11:23:09 +00:00
2018-06-22 20:23:12 +00:00
if not worldedit.pos1 [ name ] then
worldedit.player_notify ( name , " Error: No pos1 defined. " )
return false
end
2018-05-20 13:19:43 +00:00
local start_time = os.clock ( )
2018-05-20 10:52:16 +00:00
local nodes_replaced = worldedit.floodfill ( worldedit.pos1 [ name ] , radius , replace_node )
2018-05-20 13:46:54 +00:00
local time_taken = os.clock ( ) - start_time
2018-05-20 10:52:16 +00:00
2018-06-22 20:23:12 +00:00
if nodes_replaced == false then
worldedit.player_notify ( name , " Error: The search node is the same as the replace node. " )
return false
end
2018-05-20 13:46:54 +00:00
worldedit.player_notify ( name , nodes_replaced .. " nodes replaced in " .. time_taken .. " s " )
2018-06-09 12:05:09 +00:00
minetest.log ( " action " , name .. " used //floodfill at " .. worldeditadditions.vector . tostring ( worldedit.pos1 [ name ] ) .. " , replacing " .. nodes_replaced .. " nodes in " .. time_taken .. " s " )
2018-05-20 13:46:54 +00:00
end , function ( name , params_text )
local replace_node , radius = parse_params_floodfill ( params_text )
2018-06-22 20:23:12 +00:00
if not worldedit.pos1 [ name ] then
return 0 -- The actual command will send the error message to the client
end
2018-05-22 21:20:26 +00:00
-- Volume of a hemisphere
return math.ceil ( ( ( 4 * math.pi * ( tonumber ( radius ) ^ 3 ) ) / 3 ) / 2 )
end )
} )
2018-06-09 12:05:09 +00:00
-- ██████ ██ ██ ███████ ██████ ██ █████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ █████ ██████ ██ ███████ ████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ████ ███████ ██ ██ ███████ ██ ██ ██
2018-05-22 21:20:26 +00:00
minetest.register_chatcommand ( " /overlay " , {
params = " <replace_node> " ,
description = " Places <replace_node> in the last contiguous air space encountered above the first non-air node. In other words, overlays all top-most nodes in the specified area with <replace_node>. " ,
privs = { worldedit = true } ,
func = safe_region ( function ( name , params_text )
local target_node = worldedit.normalize_nodename ( params_text )
if not target_node then
worldedit.player_notify ( name , " Error: Invalid node name. " )
return false
end
local start_time = os.clock ( )
local changes = worldedit.overlay ( worldedit.pos1 [ name ] , worldedit.pos2 [ name ] , target_node )
local time_taken = os.clock ( ) - start_time
worldedit.player_notify ( name , changes.updated .. " nodes replaced and " .. changes.skipped_columns .. " columns skipped in " .. time_taken .. " s " )
2018-06-09 12:05:09 +00:00
minetest.log ( " action " , name .. " used //overlay at " .. worldeditadditions.vector . tostring ( worldedit.pos1 [ name ] ) .. " , replacing " .. changes.updated .. " nodes and skipping " .. changes.skipped_columns .. " columns in " .. time_taken .. " s " )
2018-05-22 21:20:26 +00:00
end , function ( name , params_text )
if not worldedit.normalize_nodename ( params_text ) then
worldedit.player_notify ( name , " Error: Invalid node name ' " .. params_text .. " '. " )
2018-06-09 12:36:25 +00:00
return 0
2018-05-22 21:20:26 +00:00
end
local pos1 = worldedit.pos1 [ name ]
local pos2 = worldedit.pos2 [ name ]
pos1 , pos2 = worldedit.sort_pos ( pos1 , pos2 )
local vol = vector.subtract ( pos2 , pos1 )
2018-05-20 13:46:54 +00:00
2018-05-22 21:20:26 +00:00
return vol.x * vol.z
2018-05-20 13:46:54 +00:00
end )
2018-05-20 10:52:16 +00:00
} )
2018-06-09 12:05:09 +00:00
-- ███████ ██ ██ ██ ██████ ███████ ██████ ██ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- █████ ██ ██ ██ ██████ ███████ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ███████ ███████ ██ ██ ███████ ██████ ██ ██████
local function parse_params_ellipsoid ( params_text )
local found , _ , radius_x , radius_y , radius_z , replace_node = params_text : find ( " ([0-9]+)%s+([0-9]+)%s+([0-9]+)%s+([a-z:_ \\ -]+) " )
if found == nil then
return nil , nil
end
local radius = {
x = tonumber ( radius_x ) ,
y = tonumber ( radius_y ) ,
z = tonumber ( radius_z )
}
replace_node = worldedit.normalize_nodename ( replace_node )
return replace_node , radius
end
minetest.register_chatcommand ( " /ellipsoid " , {
params = " <rx> <ry> <rz> <replace_node> " ,
description = " Creates a 3D ellipsoid with a radius of (rx, ry, rz) at pos1, filled with <replace_node>. " ,
privs = { worldedit = true } ,
func = safe_region ( function ( name , params_text )
local target_node , radius = parse_params_ellipsoid ( params_text )
if not target_node then
worldedit.player_notify ( name , " Error: Invalid node name. " )
return false
end
if not radius then
worldedit.player_notify ( name , " Error: Invalid radius(es). " )
return false
end
local start_time = os.clock ( )
2018-06-09 19:02:30 +00:00
local replaced = worldedit.ellipsoid ( worldedit.pos1 [ name ] , radius , target_node , false )
2018-06-09 12:05:09 +00:00
local time_taken = os.clock ( ) - start_time
worldedit.player_notify ( name , replaced .. " nodes replaced in " .. time_taken .. " s " )
minetest.log ( " action " , name .. " used //ellipsoid at " .. worldeditadditions.vector . tostring ( worldedit.pos1 [ name ] ) .. " , replacing " .. replaced .. " nodes in " .. time_taken .. " s " )
end , function ( name , params_text )
local target_node , radius = parse_params_ellipsoid ( params_text )
2018-06-09 12:36:25 +00:00
if not target_node or not radius then
2018-06-09 12:05:09 +00:00
worldedit.player_notify ( name , " Error: Invalid input ' " .. params_text .. " '. Try '/help /ellipsoid' to learn how to use this command. " )
2018-06-09 12:36:25 +00:00
return 0
2018-06-09 12:05:09 +00:00
end
return math.ceil ( 4 / 3 * math.pi * radius.x * radius.y * radius.z )
end )
} )
2018-06-09 19:02:30 +00:00
-- TODO: This duplicates a lot of code. Perhaps we can trim it down a bit?
minetest.register_chatcommand ( " /hollowellipsoid " , {
params = " <rx> <ry> <rz> <replace_node> " ,
2018-06-10 12:48:11 +00:00
description = " Creates a 3D hollow ellipsoid with a radius of (rx, ry, rz) at pos1, made out of <replace_node>. " ,
2018-06-09 19:02:30 +00:00
privs = { worldedit = true } ,
func = safe_region ( function ( name , params_text )
local target_node , radius = parse_params_ellipsoid ( params_text )
if not target_node then
worldedit.player_notify ( name , " Error: Invalid node name. " )
return false
end
if not radius then
worldedit.player_notify ( name , " Error: Invalid radius(es). " )
return false
end
local start_time = os.clock ( )
local replaced = worldedit.ellipsoid ( worldedit.pos1 [ name ] , radius , target_node , true )
local time_taken = os.clock ( ) - start_time
worldedit.player_notify ( name , replaced .. " nodes replaced in " .. time_taken .. " s " )
minetest.log ( " action " , name .. " used //hollowellipsoid at " .. worldeditadditions.vector . tostring ( worldedit.pos1 [ name ] ) .. " , replacing " .. replaced .. " nodes in " .. time_taken .. " s " )
end , function ( name , params_text )
local target_node , radius = parse_params_ellipsoid ( params_text )
if not target_node or not radius then
2018-06-10 12:48:11 +00:00
worldedit.player_notify ( name , " Error: Invalid input ' " .. params_text .. " '. Try '/help /hollowellipsoid' to learn how to use this command. " )
2018-06-09 19:02:30 +00:00
return 0
end
return math.ceil ( 4 / 3 * math.pi * radius.x * radius.y * radius.z )
end )
} )
2018-06-10 12:48:11 +00:00
-- ████████ ██████ ██████ ██ ██ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██████ ██ ██ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██████ ██ ██ ██████ ███████
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:_ \\ -]+) " )
if found == nil then
return nil , nil
end
major_radius = tonumber ( major_radius )
minor_radius = tonumber ( minor_radius )
replace_node = worldedit.normalize_nodename ( replace_node )
return replace_node , major_radius , minor_radius
end
minetest.register_chatcommand ( " /torus " , {
params = " <major_radius> <minor_radius> <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>. " ,
privs = { worldedit = true } ,
func = safe_region ( function ( name , params_text )
local target_node , major_radius , minor_radius = parse_params_torus ( params_text )
if not target_node then
worldedit.player_notify ( name , " Error: Invalid node name. " )
return false
end
if not major_radius or not minor_radius then
worldedit.player_notify ( name , " Error: Invalid radius(es). " )
return false
end
local start_time = os.clock ( )
local replaced = worldedit.torus ( worldedit.pos1 [ name ] , major_radius , minor_radius , target_node , false )
local time_taken = os.clock ( ) - start_time
worldedit.player_notify ( name , replaced .. " nodes replaced in " .. time_taken .. " s " )
minetest.log ( " action " , name .. " used //torus at " .. worldeditadditions.vector . tostring ( worldedit.pos1 [ name ] ) .. " , replacing " .. replaced .. " nodes in " .. time_taken .. " s " )
end , function ( name , params_text )
local target_node , major_radius , minor_radius = parse_params_torus ( params_text )
if not target_node or not major_radius or not minor_radius then
worldedit.player_notify ( name , " Error: Invalid input ' " .. params_text .. " '. Try '/help /torus' to learn how to use this command. " )
return 0
end
return math.ceil ( 2 * math.pi * math.pi * major_radius * minor_radius * minor_radius )
end )
} )
-- TODO: This duplicates a lot of code. Perhaps we can trim it down a bit?
minetest.register_chatcommand ( " /hollowtorus " , {
params = " <major_radius> <minor_radius> <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>. " ,
privs = { worldedit = true } ,
func = safe_region ( function ( name , params_text )
local target_node , major_radius , minor_radius = parse_params_torus ( params_text )
if not target_node then
worldedit.player_notify ( name , " Error: Invalid node name. " )
return false
end
if not major_radius or not minor_radius then
worldedit.player_notify ( name , " Error: Invalid radius(es). " )
return false
end
local start_time = os.clock ( )
local replaced = worldedit.torus ( worldedit.pos1 [ name ] , major_radius , minor_radius , target_node , true )
local time_taken = os.clock ( ) - start_time
worldedit.player_notify ( name , replaced .. " nodes replaced in " .. time_taken .. " s " )
minetest.log ( " action " , name .. " used //hollowtorus at " .. worldeditadditions.vector . tostring ( worldedit.pos1 [ name ] ) .. " , replacing " .. replaced .. " nodes in " .. time_taken .. " s " )
end , function ( name , params_text )
local target_node , major_radius , minor_radius = parse_params_torus ( params_text )
if not target_node or not major_radius or not minor_radius then
worldedit.player_notify ( name , " Error: Invalid input ' " .. params_text .. " '. Try '/help /hollowtorus' to learn how to use this command. " )
return 0
end
return math.ceil ( 2 * math.pi * math.pi * major_radius * minor_radius * minor_radius )
end )
} )
2020-04-28 21:41:55 +00:00
-- ███ ███ █████ ███████ ███████
-- ████ ████ ██ ██ ███ ██
-- ██ ████ ██ ███████ ███ █████
-- ██ ██ ██ ██ ██ ███ ██
-- ██ ██ ██ ██ ███████ ███████
local function parse_params_maze ( params_text )
if not params_text then
return nil , nil , nil
end
local found , _ , replace_node , seed_text = params_text : find ( " ([a-z:_ \\ -]+)%s+([0-9]+) " )
local has_seed = true
if found == nil then
has_seed = false
replace_node = params_text
end
local seed = tonumber ( seed_text )
replace_node = worldedit.normalize_nodename ( replace_node )
return replace_node , seed , has_seed
end
minetest.register_chatcommand ( " /maze " , {
params = " <replace_node> [<seed>] " ,
2020-04-29 00:55:55 +00:00
description = " Generates a maze covering the currently selected area (must be at least 3x3 on the x,z axes) with replace_node as the walls. Optionally takes a (integer) seed. " ,
2020-04-28 21:41:55 +00:00
privs = { worldedit = true } ,
func = safe_region ( function ( name , params_text )
local replace_node , seed , has_seed = parse_params_maze ( params_text )
if not replace_node then
worldedit.player_notify ( name , " Error: Invalid node name. " )
return false
end
if not seed and has_seed then
worldedit.player_notify ( name , " Error: Invalid seed. " )
return false
end
2020-04-29 00:55:55 +00:00
if not seed then seed = os.time ( ) end
2020-04-28 21:41:55 +00:00
local start_time = os.clock ( )
local replaced = worldedit.maze ( worldedit.pos1 [ name ] , worldedit.pos2 [ name ] , replace_node , seed )
local time_taken = os.clock ( ) - start_time
worldedit.player_notify ( name , replaced .. " nodes replaced in " .. time_taken .. " s " )
minetest.log ( " action " , name .. " used //maze at " .. worldeditadditions.vector . tostring ( worldedit.pos1 [ name ] ) .. " , replacing " .. replaced .. " nodes in " .. time_taken .. " s " )
end , function ( name , params_text )
local replace_node , seed , has_seed = parse_params_maze ( params_text )
if not params_text then params_text = " " end
if not replace_node then
worldedit.player_notify ( name , " Error: Invalid input ' " .. params_text .. " ' (specifically the replace node). Try '/help /maze' to learn how to use this command. " )
return 0
end
if not seed and has_seed then
worldedit.player_notify ( name , " Error: Invalid input ' " .. params_text .. " ' (specifically the seed). Try '/help /maze' to learn how to use this command. " )
return 0
end
local pos1 = worldedit.pos1 [ name ]
local pos2 = worldedit.pos2 [ name ]
return ( pos2.x - pos1.x ) * ( pos2.y - pos1.y ) * ( pos1.z - pos2.z )
end )
} )
2020-04-29 00:55:55 +00:00
-- ███ ███ █████ ███████ ███████ ██████ ██████
-- ████ ████ ██ ██ ███ ██ ██ ██ ██
-- ██ ████ ██ ███████ ███ █████ █████ ██ ██
-- ██ ██ ██ ██ ██ ███ ██ ██ ██ ██
-- ██ ██ ██ ██ ███████ ███████ ██████ ██████
minetest.register_chatcommand ( " /maze3d " , {
params = " <replace_node> [<seed>] " ,
description = " Generates a 3d maze covering the currently selected area (must be at least 3x3x3) with replace_node as the walls. Optionally takes a (integer) seed. " ,
privs = { worldedit = true } ,
func = safe_region ( function ( name , params_text )
local replace_node , seed , has_seed = parse_params_maze ( params_text )
if not replace_node then
worldedit.player_notify ( name , " Error: Invalid node name. " )
return false
end
if not seed and has_seed then
worldedit.player_notify ( name , " Error: Invalid seed. " )
return false
end
if not seed then seed = os.time ( ) end
local start_time = os.clock ( )
local replaced = worldedit.maze3d ( worldedit.pos1 [ name ] , worldedit.pos2 [ name ] , replace_node , seed )
local time_taken = os.clock ( ) - start_time
worldedit.player_notify ( name , replaced .. " nodes replaced in " .. time_taken .. " s " )
minetest.log ( " action " , name .. " used //maze at " .. worldeditadditions.vector . tostring ( worldedit.pos1 [ name ] ) .. " , replacing " .. replaced .. " nodes in " .. time_taken .. " s " )
end , function ( name , params_text )
local replace_node , seed , has_seed = parse_params_maze ( params_text )
if not params_text then params_text = " " end
if not replace_node then
worldedit.player_notify ( name , " Error: Invalid input ' " .. params_text .. " ' (specifically the replace node). Try '/help /maze3d' to learn how to use this command. " )
return 0
end
if not seed and has_seed then
worldedit.player_notify ( name , " Error: Invalid input ' " .. params_text .. " ' (specifically the seed). Try '/help /maze3d' to learn how to use this command. " )
return 0
end
local pos1 = worldedit.pos1 [ name ]
local pos2 = worldedit.pos2 [ name ]
return ( pos2.x - pos1.x ) * ( pos2.y - pos1.y ) * ( pos1.z - pos2.z )
end )
} )