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
|
## Items
|
||||||
|
|
||||||
| Name | Item ID | Image |
|
| Name | Item ID | Image |
|
||||||
| ------ | ----------- | ----------------------------- |
|
| ----------- | ---------------- | ---------------------------------- |
|
||||||
| Copy | edit:copy | ![](textures/edit_copy.png) |
|
| Copy | edit:copy | ![](textures/edit_copy.png) |
|
||||||
| Paste | edit:paste | ![](textures/edit_paste.png) |
|
| Paste | edit:paste | ![](textures/edit_paste.png) |
|
||||||
| Fill | edit:fill | ![](textures/edit_fill.png) |
|
| Fill | edit:fill | ![](textures/edit_fill.png) |
|
||||||
| Open | edit:open | ![](textures/edit_open.png) |
|
| Open | edit:open | ![](textures/edit_open.png) |
|
||||||
| Save | edit:save | ![](textures/edit_save.png) |
|
| Save | edit:save | ![](textures/edit_save.png) |
|
||||||
| Undo | edit:undo | ![](textures/edit_undo.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
|
## Dependencies
|
||||||
@ -54,7 +57,7 @@ Once a second fill node is placed, a dialog appears listing all items in the pla
|
|||||||
|
|
||||||
### Open Tool
|
### 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.
|
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.
|
Right click with this tool to undo a world modification like filling or pasting.
|
||||||
Use a second time to redo the undo.
|
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
|
## Settings
|
||||||
@ -89,7 +107,7 @@ The maximum volume of any edit operation. Increase to allow larger operations.
|
|||||||
### edit_fast_node_fill_threshold
|
### edit_fast_node_fill_threshold
|
||||||
|
|
||||||
When the fill operation has a larger volume then the specified number, fast node fill will be used.
|
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.
|
To disable slow node placement, set the threshold to 0.
|
||||||
With fast node placement, callbacks are not called so some nodes might be broken.
|
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(),
|
player:get_player_name(),
|
||||||
vector_to_string(start) .. " to " .. vector_to_string(_end) .. " copied." )
|
vector_to_string(start) .. " to " .. vector_to_string(_end) .. " copied." )
|
||||||
else
|
else
|
||||||
local obj_ref = minetest.add_entity(pos, "edit:copy")
|
d.copy_luaentity1 = edit.add_marker("edit:copy", pos, player)
|
||||||
if not obj_ref then return end
|
|
||||||
local luaentity = obj_ref:get_luaentity()
|
|
||||||
luaentity._pos = pos
|
|
||||||
luaentity._placer = player
|
|
||||||
d.copy_luaentity1 = luaentity
|
|
||||||
end
|
end
|
||||||
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)
|
pointed_thing = edit.get_pointed_thing_node(player)
|
||||||
end
|
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
|
local player_data = edit.player_data[player]
|
||||||
if player_data[player].fill1_pos and pos then
|
if player_data.fill1 and pos then
|
||||||
local diff = vector.subtract(player_data[player].fill1_pos, pos)
|
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)
|
local size = vector.add(vector.apply(diff, math.abs), 1)
|
||||||
if size.x * size.y * size.z > edit.max_operation_volume then
|
if size.x * size.y * size.z > edit.max_operation_volume then
|
||||||
edit.display_size_error(player)
|
edit.display_size_error(player)
|
||||||
minetest.remove_node(player_data[player].fill1_pos)
|
player_data.fill1.object:remove()
|
||||||
player_data[player].fill1_pos = nil
|
|
||||||
minetest.remove_node(pos)
|
|
||||||
return
|
return
|
||||||
end
|
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 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]"
|
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
|
for y = 1, 4 do
|
||||||
@ -38,38 +36,53 @@ local function fill_on_place(itemstack, player, pointed_thing)
|
|||||||
end
|
end
|
||||||
minetest.show_formspec(player:get_player_name(), "edit:fill", formspec)
|
minetest.show_formspec(player:get_player_name(), "edit:fill", formspec)
|
||||||
elseif pos then
|
elseif pos then
|
||||||
player_data[player].fill1_pos = pos
|
player_data.fill1 = edit.add_marker("edit:fill", pos, player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_node("edit:fill",{
|
minetest.register_tool("edit:fill", {
|
||||||
description = "Edit Fill",
|
description = "Edit Fill",
|
||||||
tiles = {"edit_fill.png"},
|
tiles = {"edit_fill.png"},
|
||||||
inventory_image = "edit_fill.png",
|
inventory_image = "edit_fill.png",
|
||||||
groups = {snappy = 2, oddly_breakable_by_hand = 3, dig_immediate = 3},
|
|
||||||
range = 10,
|
range = 10,
|
||||||
on_place = fill_on_place,
|
on_place = fill_on_place,
|
||||||
on_secondary_use = 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
|
minetest.register_entity("edit:fill", {
|
||||||
local p2 = data.fill2_pos
|
initial_properties = {
|
||||||
if p1 and vector.equals(p1, pos) then
|
visual = "cube",
|
||||||
data.fill1_pos = nil
|
visual_size = { x = 1.1, y = 1.1 },
|
||||||
data.fill2_pos = nil
|
physical = false,
|
||||||
data.fill_pointed_thing = nil
|
collide_with_objects = false,
|
||||||
minetest.remove_node(p1)
|
static_save = false,
|
||||||
return
|
use_texture_alpha = true,
|
||||||
end
|
glow = -1,
|
||||||
if p2 and vector.equals(p2, pos) then
|
backface_culling = false,
|
||||||
data.fill1_pos = nil
|
hp_max = 1,
|
||||||
data.fill2_pos = nil
|
textures = {
|
||||||
data.fill_pointed_thing = nil
|
"edit_fill.png",
|
||||||
minetest.remove_node(p2)
|
"edit_fill.png",
|
||||||
return
|
"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
|
end
|
||||||
|
if player_data.fill2 and not player_data.fill2.remove_called then
|
||||||
|
player_data.fill2.object:remove()
|
||||||
end
|
end
|
||||||
|
player_data.fill1 = nil
|
||||||
|
player_data.fill2 = nil
|
||||||
end
|
end
|
||||||
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
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")
|
minetest.close_formspec(player:get_player_name(), "edit:fill")
|
||||||
|
|
||||||
local d = edit.player_data[player]
|
local d = edit.player_data[player]
|
||||||
local p1 = d.fill1_pos
|
|
||||||
local p2 = d.fill2_pos
|
|
||||||
local pointed_thing = d.fill_pointed_thing
|
|
||||||
|
|
||||||
if
|
if
|
||||||
not p1 or not p2 or
|
not d.fill1 or not d.fill2 or
|
||||||
not pointed_thing or
|
|
||||||
not edit.has_privilege(player)
|
not edit.has_privilege(player)
|
||||||
then return true end
|
then return true end
|
||||||
|
|
||||||
d.fill1_pos = nil
|
local p1 = d.fill1._pos
|
||||||
d.fill2_pos = nil
|
local p2 = d.fill2._pos
|
||||||
d.fill_pointed_thing = nil
|
|
||||||
minetest.remove_node(p1)
|
d.fill1.object:remove()
|
||||||
minetest.remove_node(p2)
|
|
||||||
|
|
||||||
local name
|
local name
|
||||||
local def
|
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)
|
param2 = minetest.dir_to_wallmounted(player:get_look_dir(), true)
|
||||||
end
|
end
|
||||||
|
|
||||||
local on_place = def.on_place
|
local on_place = def.on_place or function() end
|
||||||
|
|
||||||
local start = vector.new(
|
local start = vector.new(
|
||||||
math.min(p1.x, p2.x),
|
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:write_to_map(true)
|
||||||
voxel_manip:update_liquids()
|
voxel_manip:update_liquids()
|
||||||
else
|
else
|
||||||
|
local node = {name = name, param2 = param2}
|
||||||
-- Work top to bottom so we can remove falling nodes
|
-- Work top to bottom so we can remove falling nodes
|
||||||
for x = _end.x, start.x, -1 do
|
for x = _end.x, start.x, -1 do
|
||||||
for y = _end.y, start.y, -1 do
|
for y = _end.y, start.y, -1 do
|
||||||
for z = _end.z, start.z, -1 do
|
for z = _end.z, start.z, -1 do
|
||||||
local pos = vector.new(x, y, z)
|
local pos = vector.new(x, y, z)
|
||||||
|
edit.place_node_like_player(player, node, pos)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
100
init.lua
100
init.lua
@ -44,7 +44,8 @@ end
|
|||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
minetest.register_on_joinplayer(function(player)
|
||||||
edit.player_data[player] = {
|
edit.player_data[player] = {
|
||||||
schematic_offset = vector.new(0, 0, 0)
|
schematic_offset = vector.new(0, 0, 0),
|
||||||
|
mirror_mode = "x",
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -60,6 +61,15 @@ minetest.register_on_leaveplayer(function(player)
|
|||||||
if d.copy_luaentity1 then
|
if d.copy_luaentity1 then
|
||||||
d.copy_luaentity1.object:remove()
|
d.copy_luaentity1.object:remove()
|
||||||
end
|
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
|
edit.player_data[player] = nil
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -76,7 +86,7 @@ function edit.get_pointed_thing_node(player)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
local pos = vector.round(pos2)
|
local pos = vector.round(pos2)
|
||||||
return { type = "node", under = pos, above = pos }
|
return { type = "node", under = pos, above = pos, intersection_point = pos}
|
||||||
end
|
end
|
||||||
|
|
||||||
function edit.pointed_thing_to_pos(pointed_thing)
|
function edit.pointed_thing_to_pos(pointed_thing)
|
||||||
@ -95,6 +105,89 @@ function edit.pointed_thing_to_pos(pointed_thing)
|
|||||||
end
|
end
|
||||||
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")
|
edit.modpath = minetest.get_modpath("edit")
|
||||||
dofile(edit.modpath .. "/copy.lua")
|
dofile(edit.modpath .. "/copy.lua")
|
||||||
dofile(edit.modpath .. "/fill.lua")
|
dofile(edit.modpath .. "/fill.lua")
|
||||||
@ -104,3 +197,6 @@ dofile(edit.modpath .. "/preview.lua")
|
|||||||
dofile(edit.modpath .. "/save.lua")
|
dofile(edit.modpath .. "/save.lua")
|
||||||
dofile(edit.modpath .. "/schematic.lua")
|
dofile(edit.modpath .. "/schematic.lua")
|
||||||
dofile(edit.modpath .. "/undo.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, {})
|
local schematic = minetest.read_schematic(file_path, {})
|
||||||
if schematic then
|
if schematic then
|
||||||
schematic._meta = {}
|
schematic._meta = {}
|
||||||
|
schematic._timers = {}
|
||||||
schematic._rotation = 0
|
schematic._rotation = 0
|
||||||
end
|
end
|
||||||
return schematic
|
return schematic
|
||||||
@ -64,6 +65,7 @@ local function read_world_edit_schematic(file_path)
|
|||||||
|
|
||||||
local schem_data = {}
|
local schem_data = {}
|
||||||
local meta = {}
|
local meta = {}
|
||||||
|
local timers = {}
|
||||||
local size = vector.new(x_max + 1, y_max + 1, z_max + 1)
|
local size = vector.new(x_max + 1, y_max + 1, z_max + 1)
|
||||||
|
|
||||||
local start = vector.new(1, 1, 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))
|
local key = minetest.hash_node_position(vector.new(x, y, z))
|
||||||
meta[key] = node.meta
|
meta[key] = node.meta
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if node.timer then
|
||||||
|
local key = minetest.hash_node_position(vector.new(x, y, z))
|
||||||
|
timers[key] = node.timer
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Replace empty space with air nodes
|
-- Replace empty space with air nodes
|
||||||
@ -93,6 +100,7 @@ local function read_world_edit_schematic(file_path)
|
|||||||
size = size,
|
size = size,
|
||||||
data = schem_data,
|
data = schem_data,
|
||||||
_meta = meta,
|
_meta = meta,
|
||||||
|
_timers = timers,
|
||||||
_rotation = 0,
|
_rotation = 0,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
18
preview.lua
18
preview.lua
@ -315,29 +315,35 @@ minetest.register_globalstep(function(dtime)
|
|||||||
local node1_pos
|
local node1_pos
|
||||||
local node2_pos
|
local node2_pos
|
||||||
local pointed_pos
|
local pointed_pos
|
||||||
local use_under = false
|
|
||||||
local fill_selected = item == "edit:fill"
|
local fill_selected = item == "edit:fill"
|
||||||
local copy_selected = item == "edit:copy"
|
local copy_selected = item == "edit:copy"
|
||||||
|
local circle_selected = item == "edit:circle"
|
||||||
|
local mirror_selected = item == "edit:mirror"
|
||||||
if fill_selected then
|
if fill_selected then
|
||||||
node1_pos = d.fill1_pos
|
if d.fill1 then
|
||||||
node2_pos = d.fill2_pos
|
node1_pos = d.fill1._pos
|
||||||
|
end
|
||||||
|
if d.fill2 then
|
||||||
|
node2_pos = d.fill2._pos
|
||||||
|
end
|
||||||
elseif copy_selected then
|
elseif copy_selected then
|
||||||
if d.copy_luaentity1 then
|
if d.copy_luaentity1 then
|
||||||
node1_pos = d.copy_luaentity1._pos
|
node1_pos = d.copy_luaentity1._pos
|
||||||
end
|
end
|
||||||
use_under = true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if not node2_pos or not node1_pos then
|
if not node2_pos or not node1_pos then
|
||||||
local pointed_thing = edit.get_pointed_thing_node(player)
|
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
|
pointed_pos = pointed_thing.under
|
||||||
else
|
else
|
||||||
pointed_pos = edit.pointed_thing_to_pos(pointed_thing)
|
pointed_pos = edit.pointed_thing_to_pos(pointed_thing)
|
||||||
end
|
end
|
||||||
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
|
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 = minetest.add_entity(player:get_pos(), "edit:place_preview")
|
||||||
d.place_preview_shown = true
|
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 voxel_area = VoxelArea:new({MinEdge = start, MaxEdge = schematic.size})
|
||||||
local data = schematic.data
|
local data = schematic.data
|
||||||
local meta = schematic._meta
|
local meta = schematic._meta
|
||||||
|
local timers = schematic._timers
|
||||||
|
|
||||||
for i in voxel_area:iterp(start, schematic.size) do
|
for i in voxel_area:iterp(start, schematic.size) do
|
||||||
local pos = voxel_area:position(i)
|
local pos = voxel_area:position(i)
|
||||||
local name = data[i].name
|
local name = data[i].name
|
||||||
local meta_key = minetest.hash_node_position(pos)
|
local hash = minetest.hash_node_position(pos)
|
||||||
if name ~= "air" then
|
if name ~= "air" then
|
||||||
table.insert(we, {
|
table.insert(we, {
|
||||||
x = pos.x - 1,
|
x = pos.x - 1,
|
||||||
@ -54,7 +55,8 @@ local function serialize_world_edit_schematic(schematic)
|
|||||||
z = pos.z - 1,
|
z = pos.z - 1,
|
||||||
name = name,
|
name = name,
|
||||||
param2 = data[i].param2,
|
param2 = data[i].param2,
|
||||||
meta = meta[meta_key]
|
meta = meta[hash],
|
||||||
|
timer = timers[hash]
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -3,6 +3,8 @@ function edit.schematic_from_map(pos, size)
|
|||||||
schematic.size = size
|
schematic.size = size
|
||||||
schematic._pos = pos
|
schematic._pos = pos
|
||||||
schematic._meta = {}
|
schematic._meta = {}
|
||||||
|
schematic._timers = {}
|
||||||
|
schematic._rotation = 0
|
||||||
|
|
||||||
local start = vector.new(1, 1, 1)
|
local start = vector.new(1, 1, 1)
|
||||||
local voxel_area = VoxelArea:new({MinEdge = start, MaxEdge = size})
|
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)
|
local key = minetest.hash_node_position(offset)
|
||||||
schematic._meta[key] = meta
|
schematic._meta[key] = meta
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
return schematic
|
return schematic
|
||||||
end
|
end
|
||||||
|
|
||||||
function edit.set_schematic_rotation(schematic, angle)
|
function edit.set_schematic_rotation(schematic, angle)
|
||||||
if not schematic._rotation then schematic._rotation = 0 end
|
|
||||||
schematic._rotation = schematic._rotation + angle
|
schematic._rotation = schematic._rotation + angle
|
||||||
if schematic._rotation < 0 then
|
if schematic._rotation < 0 then
|
||||||
schematic._rotation = schematic._rotation + 360
|
schematic._rotation = schematic._rotation + 360
|
||||||
@ -116,6 +125,25 @@ function edit.schematic_to_map(pos, schematic)
|
|||||||
end
|
end
|
||||||
local node_pos = vector.add(pos, offset)
|
local node_pos = vector.add(pos, offset)
|
||||||
local meta = minetest.get_meta(node_pos)
|
local meta = minetest.get_meta(node_pos)
|
||||||
|
if meta then
|
||||||
meta:from_table(metadata)
|
meta:from_table(metadata)
|
||||||
end
|
end
|
||||||
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