Add circle tool, mirror tool, screwdriver
This commit is contained in:
parent
9fa77d293f
commit
dacddb58a1
26
README.md
26
README.md
@ -17,13 +17,16 @@ This mod was inspired by the Fill Start and Fill End blocks in Manic Digger.
|
||||
## Items
|
||||
|
||||
| Name | Item ID | Image |
|
||||
| ------ | ----------- | ----------------------------- |
|
||||
| ----------- | ---------------- | ---------------------------------- |
|
||||
| Copy | edit:copy | ![](textures/edit_copy.png) |
|
||||
| Paste | edit:paste | ![](textures/edit_paste.png) |
|
||||
| Fill | edit:fill | ![](textures/edit_fill.png) |
|
||||
| Open | edit:open | ![](textures/edit_open.png) |
|
||||
| Save | edit:save | ![](textures/edit_save.png) |
|
||||
| Undo | edit:undo | ![](textures/edit_undo.png) |
|
||||
| Circle | edit:circle | ![](textures/edit_circle.png) |
|
||||
| Mirror | edit:mirror | ![](textures/edit_mirror.png) |
|
||||
| Screwdriver | edit:screwdriver | ![](textures/edit_screwdriver.png) |
|
||||
|
||||
|
||||
## Dependencies
|
||||
@ -54,7 +57,7 @@ Once a second fill node is placed, a dialog appears listing all items in the pla
|
||||
|
||||
### Open Tool
|
||||
|
||||
Right click with this tool to load .we or .mts schematics from the the world subfolder `schems` for pasting.
|
||||
Right click with this tool to load .we or .mts schematics from the world subfolder `schems` for pasting.
|
||||
Large .we files may fail to load.
|
||||
|
||||
|
||||
@ -71,7 +74,22 @@ Large .we files may fail to load.
|
||||
|
||||
Right click with this tool to undo a world modification like filling or pasting.
|
||||
Use a second time to redo the undo.
|
||||
Only the most resent world modification can be undone.
|
||||
Only the most resent edit operation can be undone.
|
||||
|
||||
|
||||
### Circle Tool
|
||||
|
||||
This tool is used to create round structures. Place the tool to activate circle mode. A center point marker is placed wherever the circle tool is placed. In circle mode, any node that is placed will be repeated in a circle around the center point. Node digging is also repeated in the same way. To place or dig a node without it repeating it in a circle, press the aux1 key (E) while placing or digging. To exit circle mode, punch the circle center marker.
|
||||
|
||||
|
||||
### Mirror Tool
|
||||
|
||||
This tool is used to mirror the placement or digging of nodes. Place the tool to activate mirror mode. A center point marker is placed wherever the mirror tool is placed. In mirror mode all placed or dig nodes are mirrored. To place or dig a node without mirroring, press the aux1 key (E) while placing or digging. The mirror tool supports four modes, X, Z, X and Z, and eighths. To switch modes, right click the center marker. To exit mirror mode, punch the center marker.
|
||||
|
||||
|
||||
### Screwdriver
|
||||
|
||||
This tool is used for rotating nodes that support rotation. Right clicking a node with the screwdriver rotates the node around the X or Z axis depending on the player's position. Left clicking a node with the screwdriver rotates the node clockwise around the Y axis. Param2 types `wallmounted`, `facedir`, and `degrotate` are supported. The node is rotated 90 degrees for all param2 types except `degrotate` where the node is rotated by either 1.5 or 15 degrees. If the aux1 key (E) is held while rotating a `degrotate` node, the rotation angle will be increased by 4x.
|
||||
|
||||
|
||||
## Settings
|
||||
@ -89,7 +107,7 @@ The maximum volume of any edit operation. Increase to allow larger operations.
|
||||
### edit_fast_node_fill_threshold
|
||||
|
||||
When the fill operation has a larger volume then the specified number, fast node fill will be used.
|
||||
To disable fast node placement, set the threshold to be equil to the max operation volume.
|
||||
To disable fast node placement, set the threshold to be equal to the max operation volume.
|
||||
To disable slow node placement, set the threshold to 0.
|
||||
With fast node placement, callbacks are not called so some nodes might be broken.
|
||||
|
||||
|
143
circle.lua
Normal file
143
circle.lua
Normal file
@ -0,0 +1,143 @@
|
||||
local function place_circle(player, pos, node)
|
||||
local player_data = edit.player_data[player]
|
||||
if
|
||||
not player or
|
||||
player:get_player_control().aux1 or
|
||||
not player_data or
|
||||
not player_data.circle_luaentity
|
||||
or player_data.ignore_node_placement
|
||||
then return end
|
||||
|
||||
local center = player_data.circle_luaentity._pos
|
||||
center.y = pos.y
|
||||
local radius = vector.distance(center, pos)
|
||||
|
||||
if radius < 1 then
|
||||
minetest.set_node(pos, node)
|
||||
return
|
||||
else
|
||||
minetest.remove_node(pos)
|
||||
end
|
||||
|
||||
local radius_rounded = math.ceil(radius) + 1
|
||||
local size = vector.new(radius_rounded * 2, 1, radius_rounded * 2)
|
||||
if size.x * size.y * size.z > edit.max_operation_volume then
|
||||
edit.display_size_error(player)
|
||||
return
|
||||
end
|
||||
player_data.undo_schematic = edit.schematic_from_map(
|
||||
vector.subtract(vector.round(center), vector.new(radius_rounded, 0, radius_rounded)),
|
||||
size
|
||||
)
|
||||
|
||||
player_data.ignore_node_placement = true -- Stop infinite recursion
|
||||
|
||||
-- Midpoint circle algorithm
|
||||
local x = radius
|
||||
local z = 0
|
||||
if center.z % 1 ~= 0 then -- Is the marker in the middle of a node?
|
||||
z = z + 0.5
|
||||
end
|
||||
while x >= z do
|
||||
for factor_x = -1, 1, 2 do
|
||||
for factor_z = -1, 1, 2 do
|
||||
local factor = vector.new(factor_x, 1, factor_z)
|
||||
local offset1 = vector.new(x, 0, z)
|
||||
offset1 = vector.new(
|
||||
offset1.x * factor.x,
|
||||
offset1.y * factor.y,
|
||||
offset1.z * factor.z )
|
||||
local pos1 = vector.add(center, offset1)
|
||||
edit.place_node_like_player(player, node, pos1)
|
||||
|
||||
local offset2 = vector.new(
|
||||
offset1.z,
|
||||
offset1.y,
|
||||
offset1.x
|
||||
)
|
||||
local pos2 = vector.add(center, offset2)
|
||||
edit.place_node_like_player(player, node, pos2)
|
||||
end
|
||||
end
|
||||
|
||||
z = z + 1
|
||||
while z * z + x * x > radius * radius do
|
||||
x = x - 1
|
||||
end
|
||||
end
|
||||
player_data.ignore_node_placement = false
|
||||
end
|
||||
|
||||
minetest.register_on_dignode(function(pos, oldnode, digger)
|
||||
if not digger or not digger:is_player() then return end
|
||||
local player_data = edit.player_data[digger]
|
||||
if player_data.ignore_node_placement then return end
|
||||
return place_circle(digger, pos, {name = "air"})
|
||||
end)
|
||||
|
||||
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
|
||||
if not placer then return end
|
||||
local player_data = edit.player_data[placer]
|
||||
if player_data.ignore_node_placement then return end
|
||||
return place_circle(placer, pos, newnode)
|
||||
end)
|
||||
|
||||
local function circle_tool_on_place(itemstack, player, pointed_thing)
|
||||
if not edit.on_place_checks(player) then return end
|
||||
|
||||
local d = edit.player_data[player]
|
||||
if d.circle_luaentity then
|
||||
d.circle_luaentity.object:remove()
|
||||
end
|
||||
|
||||
local pos = edit.get_half_node_pointed_pos(player)
|
||||
|
||||
d.circle_luaentity = edit.add_marker("edit:circle", pos, player)
|
||||
|
||||
d.circle_hud = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
text = "CIRCLE MODE\n\nPunch the circle center to exit.\nPress the aux1 key (E) while placing to bypass.",
|
||||
position = {x = 0.5, y = 0.8},
|
||||
z_index = 100,
|
||||
number = 0xffffff
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_tool("edit:circle",{
|
||||
description = "Edit Circle",
|
||||
tiles = {"edit_circle.png"},
|
||||
inventory_image = "edit_circle.png",
|
||||
range = 10,
|
||||
on_place = circle_tool_on_place,
|
||||
on_secondary_use = circle_tool_on_place,
|
||||
})
|
||||
|
||||
minetest.register_entity("edit:circle", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = { x = 1.1, y = 1.1},
|
||||
physical = false,
|
||||
collide_with_objects = false,
|
||||
static_save = false,
|
||||
use_texture_alpha = true,
|
||||
glow = -1,
|
||||
backface_culling = false,
|
||||
hp_max = 1,
|
||||
textures = {
|
||||
"edit_circle.png",
|
||||
"edit_circle.png",
|
||||
"edit_circle.png",
|
||||
"edit_circle.png",
|
||||
"edit_circle.png",
|
||||
"edit_circle.png",
|
||||
},
|
||||
},
|
||||
on_deactivate = function(self)
|
||||
local player_data = edit.player_data[self._placer]
|
||||
if player_data then
|
||||
player_data.circle_luaentity = nil
|
||||
self._placer:hud_remove(player_data.circle_hud)
|
||||
player_data.circle_hud = nil
|
||||
end
|
||||
end,
|
||||
})
|
7
copy.lua
7
copy.lua
@ -37,12 +37,7 @@ local function copy_on_place(itemstack, player, pointed_thing)
|
||||
player:get_player_name(),
|
||||
vector_to_string(start) .. " to " .. vector_to_string(_end) .. " copied." )
|
||||
else
|
||||
local obj_ref = minetest.add_entity(pos, "edit:copy")
|
||||
if not obj_ref then return end
|
||||
local luaentity = obj_ref:get_luaentity()
|
||||
luaentity._pos = pos
|
||||
luaentity._placer = player
|
||||
d.copy_luaentity1 = luaentity
|
||||
d.copy_luaentity1 = edit.add_marker("edit:copy", pos, player)
|
||||
end
|
||||
end
|
||||
|
||||
|
106
fill.lua
106
fill.lua
@ -5,23 +5,21 @@ local function fill_on_place(itemstack, player, pointed_thing)
|
||||
pointed_thing = edit.get_pointed_thing_node(player)
|
||||
end
|
||||
|
||||
local itemstack, pos = minetest.item_place_node(itemstack, player, pointed_thing)
|
||||
local pos = edit.pointed_thing_to_pos(pointed_thing)
|
||||
|
||||
local player_data = edit.player_data
|
||||
if player_data[player].fill1_pos and pos then
|
||||
local diff = vector.subtract(player_data[player].fill1_pos, pos)
|
||||
local player_data = edit.player_data[player]
|
||||
if player_data.fill1 and pos then
|
||||
player_data.fill2 = edit.add_marker("edit:fill", pos, player)
|
||||
if not player_data.fill2 then return end
|
||||
|
||||
local diff = vector.subtract(player_data.fill1._pos, pos)
|
||||
local size = vector.add(vector.apply(diff, math.abs), 1)
|
||||
if size.x * size.y * size.z > edit.max_operation_volume then
|
||||
edit.display_size_error(player)
|
||||
minetest.remove_node(player_data[player].fill1_pos)
|
||||
player_data[player].fill1_pos = nil
|
||||
minetest.remove_node(pos)
|
||||
player_data.fill1.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
player_data[player].fill2_pos = pos
|
||||
player_data[player].fill_pointed_thing = pointed_thing
|
||||
|
||||
local inv = minetest.get_inventory({type = "player", name = player:get_player_name()})
|
||||
local formspec = "size[8,6]label[2,0.5;Select item for filling]button_exit[7,0;1,1;quit;X]"
|
||||
for y = 1, 4 do
|
||||
@ -38,38 +36,53 @@ local function fill_on_place(itemstack, player, pointed_thing)
|
||||
end
|
||||
minetest.show_formspec(player:get_player_name(), "edit:fill", formspec)
|
||||
elseif pos then
|
||||
player_data[player].fill1_pos = pos
|
||||
player_data.fill1 = edit.add_marker("edit:fill", pos, player)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("edit:fill",{
|
||||
minetest.register_tool("edit:fill", {
|
||||
description = "Edit Fill",
|
||||
tiles = {"edit_fill.png"},
|
||||
inventory_image = "edit_fill.png",
|
||||
groups = {snappy = 2, oddly_breakable_by_hand = 3, dig_immediate = 3},
|
||||
range = 10,
|
||||
on_place = fill_on_place,
|
||||
on_secondary_use = fill_on_place,
|
||||
on_destruct = function(pos)
|
||||
for player, data in pairs(edit.player_data) do
|
||||
local p1 = data.fill1_pos
|
||||
local p2 = data.fill2_pos
|
||||
if p1 and vector.equals(p1, pos) then
|
||||
data.fill1_pos = nil
|
||||
data.fill2_pos = nil
|
||||
data.fill_pointed_thing = nil
|
||||
minetest.remove_node(p1)
|
||||
return
|
||||
end
|
||||
if p2 and vector.equals(p2, pos) then
|
||||
data.fill1_pos = nil
|
||||
data.fill2_pos = nil
|
||||
data.fill_pointed_thing = nil
|
||||
minetest.remove_node(p2)
|
||||
return
|
||||
})
|
||||
|
||||
minetest.register_entity("edit:fill", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = { x = 1.1, y = 1.1 },
|
||||
physical = false,
|
||||
collide_with_objects = false,
|
||||
static_save = false,
|
||||
use_texture_alpha = true,
|
||||
glow = -1,
|
||||
backface_culling = false,
|
||||
hp_max = 1,
|
||||
textures = {
|
||||
"edit_fill.png",
|
||||
"edit_fill.png",
|
||||
"edit_fill.png",
|
||||
"edit_fill.png",
|
||||
"edit_fill.png",
|
||||
"edit_fill.png",
|
||||
},
|
||||
},
|
||||
on_deactivate = function(self)
|
||||
local player_data = edit.player_data[self._placer]
|
||||
self.remove_called = true
|
||||
if player_data then
|
||||
if player_data.fill1 and not player_data.fill1.remove_called then
|
||||
player_data.fill1.object:remove()
|
||||
end
|
||||
if player_data.fill2 and not player_data.fill2.remove_called then
|
||||
player_data.fill2.object:remove()
|
||||
end
|
||||
player_data.fill1 = nil
|
||||
player_data.fill2 = nil
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
@ -78,21 +91,16 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
minetest.close_formspec(player:get_player_name(), "edit:fill")
|
||||
|
||||
local d = edit.player_data[player]
|
||||
local p1 = d.fill1_pos
|
||||
local p2 = d.fill2_pos
|
||||
local pointed_thing = d.fill_pointed_thing
|
||||
|
||||
if
|
||||
not p1 or not p2 or
|
||||
not pointed_thing or
|
||||
not d.fill1 or not d.fill2 or
|
||||
not edit.has_privilege(player)
|
||||
then return true end
|
||||
|
||||
d.fill1_pos = nil
|
||||
d.fill2_pos = nil
|
||||
d.fill_pointed_thing = nil
|
||||
minetest.remove_node(p1)
|
||||
minetest.remove_node(p2)
|
||||
local p1 = d.fill1._pos
|
||||
local p2 = d.fill2._pos
|
||||
|
||||
d.fill1.object:remove()
|
||||
|
||||
local name
|
||||
local def
|
||||
@ -117,7 +125,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
param2 = minetest.dir_to_wallmounted(player:get_look_dir(), true)
|
||||
end
|
||||
|
||||
local on_place = def.on_place
|
||||
local on_place = def.on_place or function() end
|
||||
|
||||
local start = vector.new(
|
||||
math.min(p1.x, p2.x),
|
||||
@ -155,25 +163,13 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
voxel_manip:write_to_map(true)
|
||||
voxel_manip:update_liquids()
|
||||
else
|
||||
local node = {name = name, param2 = param2}
|
||||
-- Work top to bottom so we can remove falling nodes
|
||||
for x = _end.x, start.x, -1 do
|
||||
for y = _end.y, start.y, -1 do
|
||||
for z = _end.z, start.z, -1 do
|
||||
local pos = vector.new(x, y, z)
|
||||
|
||||
if is_node then
|
||||
minetest.set_node(pos, {name = name, param2 = param2})
|
||||
else
|
||||
minetest.remove_node(pos)
|
||||
end
|
||||
|
||||
if on_place then
|
||||
local itemstack = ItemStack(name)
|
||||
pointed_thing.intersection_point = vector.new(x + 0.5, y, z + 0.5)
|
||||
pointed_thing.above = pos
|
||||
pointed_thing.under = pos
|
||||
on_place(itemstack, player, pointed_thing)
|
||||
end
|
||||
edit.place_node_like_player(player, node, pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
100
init.lua
100
init.lua
@ -44,7 +44,8 @@ end
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
edit.player_data[player] = {
|
||||
schematic_offset = vector.new(0, 0, 0)
|
||||
schematic_offset = vector.new(0, 0, 0),
|
||||
mirror_mode = "x",
|
||||
}
|
||||
end)
|
||||
|
||||
@ -60,6 +61,15 @@ minetest.register_on_leaveplayer(function(player)
|
||||
if d.copy_luaentity1 then
|
||||
d.copy_luaentity1.object:remove()
|
||||
end
|
||||
if d.circle_luaentity then
|
||||
d.circle_luaentity.object:remove()
|
||||
end
|
||||
if d.fill1 then
|
||||
d.fill1.object:remove()
|
||||
end
|
||||
if d.fill2 then
|
||||
d.fill2.object:remove()
|
||||
end
|
||||
edit.player_data[player] = nil
|
||||
end)
|
||||
|
||||
@ -76,7 +86,7 @@ function edit.get_pointed_thing_node(player)
|
||||
end
|
||||
end
|
||||
local pos = vector.round(pos2)
|
||||
return { type = "node", under = pos, above = pos }
|
||||
return { type = "node", under = pos, above = pos, intersection_point = pos}
|
||||
end
|
||||
|
||||
function edit.pointed_thing_to_pos(pointed_thing)
|
||||
@ -95,6 +105,89 @@ function edit.pointed_thing_to_pos(pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
function edit.get_half_node_pointed_pos(player)
|
||||
local intersection_point = edit.get_pointed_thing_node(player).intersection_point
|
||||
|
||||
local pos = vector.round(intersection_point)
|
||||
local pos_list = {
|
||||
pos,
|
||||
vector.add(pos, vector.new(0.5, 0, 0.5)),
|
||||
vector.add(pos, vector.new(-0.5, 0, -0.5)),
|
||||
vector.add(pos, vector.new(0.5, 0, -0.5)),
|
||||
vector.add(pos, vector.new(-0.5, 0, 0.5))
|
||||
}
|
||||
|
||||
local shortest_length = 1
|
||||
local pos
|
||||
for i, p in pairs(pos_list) do
|
||||
local length = vector.length(vector.subtract(intersection_point, p))
|
||||
if length < shortest_length then
|
||||
shortest_length = length
|
||||
pos = p
|
||||
end
|
||||
end
|
||||
return pos
|
||||
end
|
||||
|
||||
local old_register_on_dignode = minetest.register_on_dignode
|
||||
local registered_on_dignode = {}
|
||||
minetest.register_on_dignode = function(func)
|
||||
table.insert(registered_on_dignode, func)
|
||||
old_register_on_dignode(func)
|
||||
end
|
||||
|
||||
local old_register_on_placenode = minetest.register_on_placenode
|
||||
local registered_on_placenode = {}
|
||||
minetest.register_on_placenode = function(func)
|
||||
table.insert(registered_on_placenode, func)
|
||||
old_register_on_placenode(func)
|
||||
end
|
||||
|
||||
function edit.place_node_like_player(player, node, pos)
|
||||
local def = minetest.registered_items[node.name]
|
||||
local is_node = minetest.registered_nodes[node.name] ~= nil
|
||||
local itemstack = ItemStack(node.name)
|
||||
local pointed_thing = {
|
||||
type = "node",
|
||||
above = pos,
|
||||
under = pos,
|
||||
}
|
||||
minetest.remove_node(pos)
|
||||
def.on_place(itemstack, player, pointed_thing)
|
||||
|
||||
local new_node = minetest.get_node(pos)
|
||||
if
|
||||
is_node and new_node.name == "air"
|
||||
and minetest.get_item_group(node.name, "falling_node") == 0
|
||||
then
|
||||
new_node.name = node.name
|
||||
end
|
||||
|
||||
new_node.param2 = node.param2 or new_node.param2
|
||||
minetest.swap_node(pos, new_node)
|
||||
|
||||
if node.name == "air" then
|
||||
local oldnode = {name = "air"}
|
||||
for i, func in pairs(registered_on_dignode) do
|
||||
func(pos, oldnode, player)
|
||||
end
|
||||
elseif is_node then
|
||||
local oldnode = {name = "air"}
|
||||
for i, func in pairs(registered_on_placenode) do
|
||||
func(pos, node, player, oldnode, itemstack, pointed_thing)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function edit.add_marker(id, pos, player)
|
||||
local obj_ref = minetest.add_entity(pos, id)
|
||||
if not obj_ref then return end
|
||||
local luaentity = obj_ref:get_luaentity()
|
||||
luaentity._pos = pos
|
||||
luaentity._placer = player
|
||||
return luaentity
|
||||
end
|
||||
|
||||
edit.modpath = minetest.get_modpath("edit")
|
||||
dofile(edit.modpath .. "/copy.lua")
|
||||
dofile(edit.modpath .. "/fill.lua")
|
||||
@ -104,3 +197,6 @@ dofile(edit.modpath .. "/preview.lua")
|
||||
dofile(edit.modpath .. "/save.lua")
|
||||
dofile(edit.modpath .. "/schematic.lua")
|
||||
dofile(edit.modpath .. "/undo.lua")
|
||||
dofile(edit.modpath .. "/circle.lua")
|
||||
dofile(edit.modpath .. "/mirror.lua")
|
||||
dofile(edit.modpath .. "/screwdriver.lua")
|
||||
|
210
mirror.lua
Normal file
210
mirror.lua
Normal file
@ -0,0 +1,210 @@
|
||||
local function do_mirror(player, pos, node)
|
||||
local d = edit.player_data[player]
|
||||
|
||||
if
|
||||
not player or
|
||||
player:get_player_control().aux1 or
|
||||
not d or
|
||||
not d.mirror_luaentity
|
||||
or d.ignore_node_placement
|
||||
then return end
|
||||
|
||||
d.ignore_node_placement = true -- Stop infinite recursion
|
||||
|
||||
local center = d.mirror_luaentity._pos
|
||||
local offset = vector.subtract(pos, center)
|
||||
|
||||
if d.mirror_mode == "x" then
|
||||
offset.x = -offset.x
|
||||
edit.place_node_like_player(player, node, vector.add(center, offset))
|
||||
elseif d.mirror_mode == "z" then
|
||||
offset.z = -offset.z
|
||||
edit.place_node_like_player(player, node, vector.add(center, offset))
|
||||
elseif d.mirror_mode == "xz" then
|
||||
for i = 1, 4 do
|
||||
local axis = "x"
|
||||
if i % 2 == 0 then
|
||||
axis = "z"
|
||||
end
|
||||
offset[axis] = -offset[axis]
|
||||
edit.place_node_like_player(player, node, vector.add(center, offset))
|
||||
end
|
||||
elseif d.mirror_mode == "eighths" then
|
||||
for i = 1, 8 do
|
||||
local axis = "x"
|
||||
if i % 2 == 0 then
|
||||
axis = "z"
|
||||
end
|
||||
if i == 5 then
|
||||
offset = vector.new(offset.z, offset.y, offset.x)
|
||||
end
|
||||
offset[axis] = -offset[axis]
|
||||
edit.place_node_like_player(player, node, vector.add(center, offset))
|
||||
end
|
||||
end
|
||||
d.ignore_node_placement = nil
|
||||
end
|
||||
|
||||
minetest.register_on_dignode(function(pos, oldnode, digger)
|
||||
if not digger or not digger:is_player() then return end
|
||||
local player_data = edit.player_data[digger]
|
||||
if player_data.ignore_node_placement then return end
|
||||
return do_mirror(digger, pos, {name = "air"})
|
||||
end)
|
||||
|
||||
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
|
||||
if not placer or not placer:is_player() then return end
|
||||
local player_data = edit.player_data[placer]
|
||||
if player_data.ignore_node_placement then return end
|
||||
return do_mirror(placer, pos, newnode)
|
||||
end)
|
||||
|
||||
local function mirror_tool_on_place(itemstack, player, pointed_thing)
|
||||
if not edit.on_place_checks(player) or pointed_thing.type == "object" then return end
|
||||
|
||||
local d = edit.player_data[player]
|
||||
if d.mirror_luaentity then
|
||||
d.mirror_luaentity.object:remove()
|
||||
end
|
||||
|
||||
local pos = edit.get_half_node_pointed_pos(player)
|
||||
|
||||
local obj_ref = minetest.add_entity(pos, "edit:mirror")
|
||||
if not obj_ref then return end
|
||||
local luaentity = obj_ref:get_luaentity()
|
||||
luaentity._pos = pos
|
||||
luaentity._placer = player
|
||||
luaentity:_update_borders()
|
||||
d.mirror_luaentity = luaentity
|
||||
|
||||
d.mirror_hud = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
text = "MIRROR MODE\n\nPunch center indicator to exit.\n" ..
|
||||
"Right click the center indicator to switch modes.\n" ..
|
||||
"Press the aux1 key (E) while placing to bypass.",
|
||||
position = {x = 0.5, y = 0.8},
|
||||
z_index = 100,
|
||||
number = 0xffffff
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_tool("edit:mirror", {
|
||||
description = "Edit Mirror",
|
||||
tiles = {"edit_mirror.png"},
|
||||
inventory_image = "edit_mirror.png",
|
||||
range = 10,
|
||||
on_place = mirror_tool_on_place,
|
||||
on_secondary_use = mirror_tool_on_place,
|
||||
})
|
||||
|
||||
minetest.register_entity("edit:mirror_border", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = { x = 16, y = 16, z = 0},
|
||||
physical = false,
|
||||
collide_with_objects = false,
|
||||
static_save = false,
|
||||
use_texture_alpha = true,
|
||||
glow = -1,
|
||||
backface_culling = false,
|
||||
hp_max = 1,
|
||||
pointable = false,
|
||||
backface_culling = true,
|
||||
textures = {
|
||||
"edit_mirror_border.png",
|
||||
"edit_mirror_border.png",
|
||||
"edit_mirror_border.png",
|
||||
"edit_mirror_border.png",
|
||||
"edit_mirror_border.png",
|
||||
"edit_mirror_border.png",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_entity("edit:mirror", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = { x = 1.1, y = 1.1},
|
||||
physical = false,
|
||||
collide_with_objects = false,
|
||||
static_save = false,
|
||||
use_texture_alpha = true,
|
||||
glow = -1,
|
||||
backface_culling = false,
|
||||
hp_max = 1,
|
||||
textures = {
|
||||
"edit_mirror.png",
|
||||
"edit_mirror.png",
|
||||
"edit_mirror.png",
|
||||
"edit_mirror.png",
|
||||
"edit_mirror.png",
|
||||
"edit_mirror.png",
|
||||
},
|
||||
},
|
||||
on_deactivate = function(self)
|
||||
local player_data = edit.player_data[self._placer]
|
||||
if player_data then
|
||||
player_data.mirror_luaentity = nil
|
||||
self._placer:hud_remove(player_data.mirror_hud)
|
||||
player_data.mirror_hud = nil
|
||||
end
|
||||
for i, luaentity in pairs(self._borders) do
|
||||
luaentity.object:remove()
|
||||
end
|
||||
self._borders = {}
|
||||
end,
|
||||
on_rightclick = function(self, clicker)
|
||||
local player_data = edit.player_data[self._placer]
|
||||
if player_data.mirror_mode == "x" then
|
||||
player_data.mirror_mode = "z"
|
||||
elseif player_data.mirror_mode == "z" then
|
||||
player_data.mirror_mode = "xz"
|
||||
elseif player_data.mirror_mode == "xz" then
|
||||
player_data.mirror_mode = "eighths"
|
||||
elseif player_data.mirror_mode == "eighths" then
|
||||
player_data.mirror_mode = "x"
|
||||
end
|
||||
self:_update_borders()
|
||||
end,
|
||||
_borders = {},
|
||||
_update_borders = function(self)
|
||||
local function invert_tex(luaentity)
|
||||
local texs = luaentity.object:get_properties().textures
|
||||
for i, tex in pairs(texs) do
|
||||
texs[i] = tex .. "^[invert:rgb"
|
||||
end
|
||||
luaentity.object:set_properties({textures = texs})
|
||||
end
|
||||
local player_data = edit.player_data[self._placer]
|
||||
|
||||
for i, luaentity in pairs(self._borders) do
|
||||
luaentity.object:remove()
|
||||
end
|
||||
self._borders = {}
|
||||
|
||||
if player_data.mirror_mode:find("x") then
|
||||
local obj_ref = minetest.add_entity(self._pos, "edit:mirror_border")
|
||||
if not obj_ref then return end
|
||||
obj_ref:set_rotation(vector.new(0, math.pi / 2, 0))
|
||||
local luaentity = obj_ref:get_luaentity()
|
||||
table.insert(self._borders, luaentity)
|
||||
end
|
||||
if player_data.mirror_mode:find("z") then
|
||||
local obj_ref = minetest.add_entity(self._pos, "edit:mirror_border")
|
||||
if not obj_ref then return end
|
||||
local luaentity = obj_ref:get_luaentity()
|
||||
invert_tex(luaentity)
|
||||
table.insert(self._borders, luaentity)
|
||||
end
|
||||
if player_data.mirror_mode == "eighths" then
|
||||
for i = 0, 7 do
|
||||
local obj_ref = minetest.add_entity(self._pos, "edit:mirror_border")
|
||||
if not obj_ref then return end
|
||||
obj_ref:set_rotation(vector.new(0, math.pi / 4 * i, 0))
|
||||
local luaentity = obj_ref:get_luaentity()
|
||||
if i % 2 == 1 then invert_tex(luaentity) end
|
||||
table.insert(self._borders, luaentity)
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
8
open.lua
8
open.lua
@ -37,6 +37,7 @@ local function read_minetest_schematic(file_path)
|
||||
local schematic = minetest.read_schematic(file_path, {})
|
||||
if schematic then
|
||||
schematic._meta = {}
|
||||
schematic._timers = {}
|
||||
schematic._rotation = 0
|
||||
end
|
||||
return schematic
|
||||
@ -64,6 +65,7 @@ local function read_world_edit_schematic(file_path)
|
||||
|
||||
local schem_data = {}
|
||||
local meta = {}
|
||||
local timers = {}
|
||||
local size = vector.new(x_max + 1, y_max + 1, z_max + 1)
|
||||
|
||||
local start = vector.new(1, 1, 1)
|
||||
@ -80,6 +82,11 @@ local function read_world_edit_schematic(file_path)
|
||||
local key = minetest.hash_node_position(vector.new(x, y, z))
|
||||
meta[key] = node.meta
|
||||
end
|
||||
|
||||
if node.timer then
|
||||
local key = minetest.hash_node_position(vector.new(x, y, z))
|
||||
timers[key] = node.timer
|
||||
end
|
||||
end
|
||||
|
||||
-- Replace empty space with air nodes
|
||||
@ -93,6 +100,7 @@ local function read_world_edit_schematic(file_path)
|
||||
size = size,
|
||||
data = schem_data,
|
||||
_meta = meta,
|
||||
_timers = timers,
|
||||
_rotation = 0,
|
||||
}
|
||||
end
|
||||
|
18
preview.lua
18
preview.lua
@ -315,29 +315,35 @@ minetest.register_globalstep(function(dtime)
|
||||
local node1_pos
|
||||
local node2_pos
|
||||
local pointed_pos
|
||||
local use_under = false
|
||||
local fill_selected = item == "edit:fill"
|
||||
local copy_selected = item == "edit:copy"
|
||||
local circle_selected = item == "edit:circle"
|
||||
local mirror_selected = item == "edit:mirror"
|
||||
if fill_selected then
|
||||
node1_pos = d.fill1_pos
|
||||
node2_pos = d.fill2_pos
|
||||
if d.fill1 then
|
||||
node1_pos = d.fill1._pos
|
||||
end
|
||||
if d.fill2 then
|
||||
node2_pos = d.fill2._pos
|
||||
end
|
||||
elseif copy_selected then
|
||||
if d.copy_luaentity1 then
|
||||
node1_pos = d.copy_luaentity1._pos
|
||||
end
|
||||
use_under = true
|
||||
end
|
||||
|
||||
if not node2_pos or not node1_pos then
|
||||
local pointed_thing = edit.get_pointed_thing_node(player)
|
||||
if use_under then
|
||||
if circle_selected or mirror_selected then
|
||||
pointed_pos = edit.get_half_node_pointed_pos(player)
|
||||
elseif copy_selected then
|
||||
pointed_pos = pointed_thing.under
|
||||
else
|
||||
pointed_pos = edit.pointed_thing_to_pos(pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
if (fill_selected or copy_selected) and not node2_pos and pointed_pos then
|
||||
if (fill_selected or copy_selected or circle_selected or mirror_selected) and not node2_pos and pointed_pos then
|
||||
if not d.place_preview or not d.place_preview:get_pos() then
|
||||
d.place_preview = minetest.add_entity(player:get_pos(), "edit:place_preview")
|
||||
d.place_preview_shown = true
|
||||
|
6
save.lua
6
save.lua
@ -42,11 +42,12 @@ local function serialize_world_edit_schematic(schematic)
|
||||
local voxel_area = VoxelArea:new({MinEdge = start, MaxEdge = schematic.size})
|
||||
local data = schematic.data
|
||||
local meta = schematic._meta
|
||||
local timers = schematic._timers
|
||||
|
||||
for i in voxel_area:iterp(start, schematic.size) do
|
||||
local pos = voxel_area:position(i)
|
||||
local name = data[i].name
|
||||
local meta_key = minetest.hash_node_position(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if name ~= "air" then
|
||||
table.insert(we, {
|
||||
x = pos.x - 1,
|
||||
@ -54,7 +55,8 @@ local function serialize_world_edit_schematic(schematic)
|
||||
z = pos.z - 1,
|
||||
name = name,
|
||||
param2 = data[i].param2,
|
||||
meta = meta[meta_key]
|
||||
meta = meta[hash],
|
||||
timer = timers[hash]
|
||||
})
|
||||
end
|
||||
end
|
||||
|
@ -3,6 +3,8 @@ function edit.schematic_from_map(pos, size)
|
||||
schematic.size = size
|
||||
schematic._pos = pos
|
||||
schematic._meta = {}
|
||||
schematic._timers = {}
|
||||
schematic._rotation = 0
|
||||
|
||||
local start = vector.new(1, 1, 1)
|
||||
local voxel_area = VoxelArea:new({MinEdge = start, MaxEdge = size})
|
||||
@ -44,13 +46,20 @@ function edit.schematic_from_map(pos, size)
|
||||
local key = minetest.hash_node_position(offset)
|
||||
schematic._meta[key] = meta
|
||||
end
|
||||
|
||||
local timer = minetest.get_node_timer(node_pos)
|
||||
local timeout = timer:get_timeout()
|
||||
if timeout ~= 0 then
|
||||
local key = minetest.hash_node_position(offset)
|
||||
local elapsed = timer:get_elapsed()
|
||||
schematic._timers[key] = {timeout, elapsed}
|
||||
end
|
||||
end
|
||||
|
||||
return schematic
|
||||
end
|
||||
|
||||
function edit.set_schematic_rotation(schematic, angle)
|
||||
if not schematic._rotation then schematic._rotation = 0 end
|
||||
schematic._rotation = schematic._rotation + angle
|
||||
if schematic._rotation < 0 then
|
||||
schematic._rotation = schematic._rotation + 360
|
||||
@ -116,6 +125,25 @@ function edit.schematic_to_map(pos, schematic)
|
||||
end
|
||||
local node_pos = vector.add(pos, offset)
|
||||
local meta = minetest.get_meta(node_pos)
|
||||
if meta then
|
||||
meta:from_table(metadata)
|
||||
end
|
||||
end
|
||||
|
||||
for hash, timer_data in pairs(schematic._timers) do
|
||||
local offset = minetest.get_position_from_hash(hash)
|
||||
offset = vector.subtract(offset, 1)
|
||||
if schematic._rotation == 90 then
|
||||
offset = vector.new(offset.z, offset.y, size.x - offset.x - 1)
|
||||
elseif schematic._rotation == 180 then
|
||||
offset = vector.new(size.x - offset.x - 1, offset.y, size.z - offset.z - 1)
|
||||
elseif schematic._rotation == 270 then
|
||||
offset = vector.new(size.z - offset.z - 1, offset.y, offset.x)
|
||||
end
|
||||
local node_pos = vector.add(pos, offset)
|
||||
local timer = minetest.get_node_timer(node_pos)
|
||||
if timer then
|
||||
timer:set(timer_data[1], timer_data[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
195
screwdriver.lua
Normal file
195
screwdriver.lua
Normal file
@ -0,0 +1,195 @@
|
||||
-- param2 rotation table from:
|
||||
-- https://forum.minetest.net/viewtopic.php?p=73195&sid=1d2d2e4e76ce2ef9c84646481a4b84bc#p73195
|
||||
-- Axis * 4 + Rotation = Facedir
|
||||
-- First in pair is axis
|
||||
-- Second in pair is rotation
|
||||
local raw_facedir = {
|
||||
x = {
|
||||
{{3, 0}, {3, 1}, {3, 2}, {3, 3}},
|
||||
{{4, 0}, {4, 3}, {4, 2}, {4, 1}},
|
||||
{{0, 0}, {1, 0}, {5, 2}, {2, 0}},
|
||||
{{0, 1}, {1, 1}, {5, 3}, {2, 1}},
|
||||
{{0, 2}, {1, 2}, {5, 0}, {2, 2}},
|
||||
{{0, 3}, {1, 3}, {5, 1}, {2, 3}}
|
||||
},
|
||||
y = {
|
||||
{{0, 0}, {0, 1}, {0, 2}, {0, 3}},
|
||||
{{5, 0}, {5, 3}, {5, 2}, {5, 1}},
|
||||
{{1, 0}, {3, 1}, {2, 2}, {4, 3}},
|
||||
{{2, 0}, {4, 1}, {1, 2}, {3, 3}},
|
||||
{{3, 0}, {2, 1}, {4, 2}, {1, 3}},
|
||||
{{4, 0}, {1, 1}, {3, 2}, {2, 3}}
|
||||
},
|
||||
z = {
|
||||
{{1, 0}, {1, 1}, {1, 2}, {1, 3}},
|
||||
{{2, 0}, {2, 3}, {2, 2}, {2, 1}},
|
||||
{{0, 0}, {4, 0}, {5, 0}, {3, 0}},
|
||||
{{0, 1}, {4, 1}, {5, 1}, {3, 1}},
|
||||
{{0, 2}, {4, 2}, {5, 2}, {3, 2}},
|
||||
{{0, 3}, {4, 3}, {5, 3}, {3, 3}}
|
||||
}
|
||||
}
|
||||
|
||||
local facedir_rot = {}
|
||||
|
||||
local function pair_to_param2(pair)
|
||||
return pair[1] * 4 + pair[2]
|
||||
end
|
||||
|
||||
for axis, raw_rot in pairs(raw_facedir) do
|
||||
facedir_rot[axis] = {}
|
||||
for j, pair_list in pairs(raw_rot) do
|
||||
for i = 1, 4 do
|
||||
local back_index = i - 1
|
||||
if back_index == 0 then
|
||||
back_index = 4
|
||||
end
|
||||
local next_index = i + 1
|
||||
if next_index == 5 then
|
||||
next_index = 1
|
||||
end
|
||||
local back = pair_to_param2(pair_list[back_index])
|
||||
local next = pair_to_param2(pair_list[next_index])
|
||||
local current = pair_to_param2(pair_list[i])
|
||||
facedir_rot[axis][current] = {next, back}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local raw_wallmounted = {
|
||||
x = {4, 0, 5, 1},
|
||||
y = {2, 4, 3, 5},
|
||||
z = {3, 0, 2, 1},
|
||||
}
|
||||
|
||||
local wallmounted_rot = {}
|
||||
|
||||
for axis, rots in pairs(raw_wallmounted) do
|
||||
wallmounted_rot[axis] = {}
|
||||
local param2s_unused = {
|
||||
[0] = true, [1] = true, [2] = true,
|
||||
[3] = true, [4] = true, [5] = true}
|
||||
for i = 1, 4 do
|
||||
local back_index = i - 1
|
||||
if back_index == 0 then
|
||||
back_index = 4
|
||||
end
|
||||
local next_index = i + 1
|
||||
if next_index == 5 then
|
||||
next_index = 1
|
||||
end
|
||||
local current = rots[i]
|
||||
local back = rots[back_index]
|
||||
local next = rots[next_index]
|
||||
wallmounted_rot[axis][current] = {back, next}
|
||||
param2s_unused[current] = nil
|
||||
end
|
||||
|
||||
for param2, bool in pairs(param2s_unused) do
|
||||
wallmounted_rot[axis][param2] = {param2, param2}
|
||||
end
|
||||
end
|
||||
|
||||
function edit.rotate_param2(node, rot_vect)
|
||||
local def = minetest.registered_items[node.name]
|
||||
if not node.param2 or not def then return end
|
||||
local paramtype2 = def.paramtype2
|
||||
local is_wallmounted = paramtype2 == "wallmounted" or paramtype2 == "colorwallmounted"
|
||||
local is_facedir = paramtype2 == "facedir" or paramtype2 == "colorfacedir"
|
||||
local rot_table = is_facedir and facedir_rot or wallmounted_rot
|
||||
if is_facedir or is_wallmounted then
|
||||
local param2_rot = node.param2 % 32 -- Get first 5 bits
|
||||
if is_wallmounted then
|
||||
param2_rot = node.param2 % 8 -- Get first 3 bits
|
||||
end
|
||||
local param2_other = node.param2 - param2_rot
|
||||
for axis, target_rot in pairs(rot_vect) do
|
||||
if target_rot ~= 0 then
|
||||
local direction = math.sign(target_rot)
|
||||
for rot = direction, target_rot / (math.pi / 2), direction do
|
||||
if target_rot > 0 then
|
||||
param2_rot = rot_table[axis][param2_rot][1]
|
||||
else
|
||||
param2_rot = rot_table[axis][param2_rot][2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
node.param2 = param2_other + param2_rot
|
||||
elseif paramtype2 == "degrotate" or paramtype2 == "colordegrotate" then
|
||||
local param2_rot
|
||||
local deg_per_unit
|
||||
if paramtype2 == "degrotate" then
|
||||
param2_rot = node.param2
|
||||
deg_per_unit = 1.5
|
||||
else
|
||||
param2_rot = node.param2 % 32 -- Get first 5 bits
|
||||
deg_per_unit = 15
|
||||
end
|
||||
local param2_other = node.param2 - param2_rot
|
||||
local rot = param2_rot * deg_per_unit / 180 * math.pi
|
||||
|
||||
rot = rot + rot_vect.y
|
||||
rot = rot % (math.pi * 2)
|
||||
if rot < 0 then
|
||||
rot = rot + math.pi * 2
|
||||
end
|
||||
|
||||
param2_rot = math.round(rot / math.pi * 180 / deg_per_unit)
|
||||
node.param2 = param2_other + param2_rot
|
||||
end
|
||||
end
|
||||
|
||||
local function screwdriver_run(player, pointed_thing, rotate_y)
|
||||
if not edit.on_place_checks(player) then return end
|
||||
if pointed_thing.type ~= "node" then return end
|
||||
local pos = pointed_thing.under
|
||||
local node = minetest.get_node(pos)
|
||||
local def = minetest.registered_items[node.name]
|
||||
if not def then return end
|
||||
local rot = vector.new(0, 0, 0)
|
||||
local paramtype2 = def.paramtype2
|
||||
if paramtype2 == "degrotate" or paramtype2 == "colordegrotate" then
|
||||
if rotate_y then
|
||||
local deg = paramtype2 == "degrotate" and 1.5 or 15
|
||||
rot.y = deg / 180 * math.pi
|
||||
if player:get_player_control().aux1 then
|
||||
rot.y = rot.y * 4
|
||||
end
|
||||
end
|
||||
else
|
||||
if rotate_y then
|
||||
rot = vector.new(0, math.pi / 2, 0)
|
||||
else
|
||||
local player_pos = player:get_pos()
|
||||
local diff = vector.subtract(player_pos, pos)
|
||||
local abs_diff = vector.apply(diff, math.abs)
|
||||
if abs_diff.x > abs_diff.z then
|
||||
local sign = (diff.x > 0) and 1 or -1
|
||||
rot = vector.new(0, 0, math.pi / 2 * sign)
|
||||
else
|
||||
local sign = (diff.z < 0) and 1 or -1
|
||||
rot = vector.new(math.pi / 2 * sign, 0, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
local old_node = table.copy(node)
|
||||
edit.rotate_param2(node, rot)
|
||||
if def.on_rotate then
|
||||
def.on_rotate(pos, old_node, player, 1, node.param2)
|
||||
end
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
|
||||
minetest.register_tool("edit:screwdriver", {
|
||||
description = "Edit Screwdriver",
|
||||
inventory_image = "edit_screwdriver.png",
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
screwdriver_run(user, pointed_thing, true)
|
||||
return itemstack
|
||||
end,
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
screwdriver_run(user, pointed_thing, false)
|
||||
return itemstack
|
||||
end,
|
||||
})
|
BIN
textures/edit_circle.png
Normal file
BIN
textures/edit_circle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 163 B |
BIN
textures/edit_mirror.png
Normal file
BIN
textures/edit_mirror.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 156 B |
BIN
textures/edit_mirror_border.png
Normal file
BIN
textures/edit_mirror_border.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 252 B |
BIN
textures/edit_screwdriver.png
Normal file
BIN
textures/edit_screwdriver.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 149 B |
Loading…
x
Reference in New Issue
Block a user