diff --git a/init.lua b/init.lua index 3590e86..9064164 100755 --- a/init.lua +++ b/init.lua @@ -1,65 +1,20 @@ -local math, ipairs, tostring - = math, ipairs, tostring - local minetest, vector, include = minetest, vector, include -local function debug_tell(...) - local text = "" - for _, v in ipairs({...}) do text = text .. tostring(v) end - minetest.chat_send_all(text) -end - -local AXIS_LOOKUP = { - vector.new( 0, 1, 0), -- +Y - vector.new( 0, 0, 1), -- +Z - vector.new( 0, 0, -1), -- -Z - vector.new( 1, 0, 0), -- +X - vector.new(-1, 0, 0), -- -X - vector.new( 0, -1, 0), -- -Y -} - -local function unit_vector_to_axis_index(vec) - if vec.y == 1 then if vec.x == 0 and vec.z == 0 then return 1 end - elseif vec.z == 1 then if vec.x == 0 and vec.y == 0 then return 2 end - elseif vec.z == -1 then if vec.x == 0 and vec.y == 0 then return 3 end - elseif vec.x == 1 then if vec.y == 0 and vec.z == 0 then return 4 end - elseif vec.x == -1 then if vec.y == 0 and vec.z == 0 then return 5 end - elseif vec.y == -1 then if vec.x == 0 and vec.z == 0 then return 6 end - end - error("Not a unit vector") -end - -local FACEDIR_LOOKUP = {} -for up_index, up in ipairs(AXIS_LOOKUP) do - FACEDIR_LOOKUP[up_index] = {} - for rot = 0, 3 do - local facedir = (up_index - 1) * 4 + rot - local back = minetest.facedir_to_dir(facedir) - local back_index = unit_vector_to_axis_index(back) - FACEDIR_LOOKUP[up_index][back_index] = facedir - end -end +local rotate = include("rotate") minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing) local def = minetest.registered_nodes[node.name] - if def.paramtype2 == "facedir" and pointed_thing.above and pointed_thing.under then - local facedir = node.param2 - -- Vector that points away from the punched face. - local punched_face = vector.subtract(pointed_thing.above, pointed_thing.under) - - local up = AXIS_LOOKUP[1 + math.floor(facedir / 4)] - local back = minetest.facedir_to_dir(facedir) + if def.paramtype2 ~= "facedir" then return end - up = up:rotate_around_axis(punched_face, math.rad(-90)):round() - back = back:rotate_around_axis(punched_face, math.rad(-90)):round() + -- Vector that points away from the punched face. + if (not pointed_thing.above) or (not pointed_thing.under) then return end + local axis = vector.subtract(pointed_thing.above, pointed_thing.under) - local up_index = unit_vector_to_axis_index(up) - local back_index = unit_vector_to_axis_index(back) + -- Rotate clockwise by default. + local degrees = -90 + -- If player is sneaking, reverse the rotation. + if minetest.is_player(puncher) and puncher:get_player_control().sneak then degrees = -degrees end - facedir = FACEDIR_LOOKUP[up_index][back_index] - if facedir == nil then minetest.chat_send_all("Could not look up facedir") end - node.param2 = facedir - minetest.set_node(pos, node) - end + rotate.rotate_node(pos, node, axis, degrees) end) diff --git a/rotate.lua b/rotate.lua new file mode 100644 index 0000000..cb6a164 --- /dev/null +++ b/rotate.lua @@ -0,0 +1,64 @@ +local math, ipairs + = math, ipairs + +local minetest, vector + = minetest, vector + +local rotate = {} + +-- Axis lookup table based on how Minetest's `facedir` operates. +local AXIS_LOOKUP = { + vector.new( 0, 1, 0), -- +Y + vector.new( 0, 0, 1), -- +Z + vector.new( 0, 0, -1), -- -Z + vector.new( 1, 0, 0), -- +X + vector.new(-1, 0, 0), -- -X + vector.new( 0, -1, 0), -- -Y +} + +-- Takes an axis vector and returns its index in the `AXIS_LOOKUP` table. +-- Returns `nil` for any vector that is not a valid axis vector. +local function axis_vector_to_index(vec) + if vec.y == 1 then if vec.x == 0 and vec.z == 0 then return 1 end + elseif vec.z == 1 then if vec.x == 0 and vec.y == 0 then return 2 end + elseif vec.z == -1 then if vec.x == 0 and vec.y == 0 then return 3 end + elseif vec.x == 1 then if vec.y == 0 and vec.z == 0 then return 4 end + elseif vec.x == -1 then if vec.y == 0 and vec.z == 0 then return 5 end + elseif vec.y == -1 then if vec.x == 0 and vec.z == 0 then return 6 end + else return nil end +end + +local FACEDIR_LOOKUP = {} +for up_index, up in ipairs(AXIS_LOOKUP) do + FACEDIR_LOOKUP[up_index] = {} + for rot = 0, 3 do + local facedir = (up_index - 1) * 4 + rot + local back = minetest.facedir_to_dir(facedir) + local back_index = axis_vector_to_index(back) + FACEDIR_LOOKUP[up_index][back_index] = facedir + end +end + +-- Rotates `node` at the specified `pos` around `axis` by `degrees` counter-clockwise. +local function rotate_node(pos, node, axis, degrees) + if degrees % 90 ~= 0 then error("degrees must be divisible by 90") end + if axis_vector_to_index(axis) == nil then error("axis must be an axis vector") end + + local def = minetest.registered_nodes[node.name] + if def.paramtype2 ~= "facedir" then error("node's paramtype2 is not 'facedir'") end + + local up = AXIS_LOOKUP[1 + math.floor(node.param2 / 4)] + local back = minetest.facedir_to_dir(node.param2) + + up = up :rotate_around_axis(axis, math.rad(degrees)):round() + back = back:rotate_around_axis(axis, math.rad(degrees)):round() + + local up_index = axis_vector_to_index(up) + local back_index = axis_vector_to_index(back) + + node.param2 = FACEDIR_LOOKUP[up_index][back_index] + minetest.set_node(pos, node) +end +rotate.rotate_node = rotate_node + +return rotate diff --git a/utility.lua b/utility.lua new file mode 100644 index 0000000..81647b6 --- /dev/null +++ b/utility.lua @@ -0,0 +1,16 @@ +local ipairs, tostring + = ipairs, tostring + +local minetest + = minetest + +local utility = {} + +local function debug_tell(...) + local text = "" + for _, v in ipairs({...}) do text = text .. tostring(v) end + minetest.chat_send_all(text) +end +utility.debug_tell = debug_tell + +return utility