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 -- Returns whether the specified `node` is rotatable by this mod. -- TODO: Register such that only certain nodes may be rotated. local function is_rotatable(node) local def = minetest.registered_nodes[node.name] return def.paramtype2 == "facedir" end rotate.is_rotatable = is_rotatable -- 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 if not is_rotatable(node) then error("node is not rotatable") 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