local minetest, vector, math_abs, math_sign = minetest, vector, math.abs, math.sign local rotate_box_by_facedir = nc_extended_rotating.rotate.rotate_box_by_facedir -- Default selection boxes for a node that doesn't have one them explicitly. local DEFAULT_SELECTION_BOX = { min = vector.new(-0.5, -0.5, -0.5), max = vector.new( 0.5, 0.5, 0.5) } local utility = {} -- Gets the active `selection_box` for the specified `node` which -- has a `paramtype2` of `facedir`, based on its `param2` value. function utility.get_node_active_selection_box(node) local def = minetest.registered_nodes[node.name] local box = def.selection_box and def.selection_box.fixed -- No need to rotate the default selection box. if not box then return DEFAULT_SELECTION_BOX end -- If node definition specifies multiple selection boxes, just pick the first (for now). if type(box[1]) == "table" then box = box[1] end -- Transform the `{ x1, y1, z1, x2, y2, z2 }` box to a `{ min, max }` one. box = { min = vector.new(box[1], box[2], box[3]), max = vector.new(box[4], box[5], box[6]) } -- Rotate the box to face `facedir`. rotate_box_by_facedir(box, node.param2) return box end -- Calculates the rotation vector from where a player is looking at on this node. -- * If looking near an edge, the vector points in the direction of that edge. -- * If looking near a corner, the vector similarly points in that direction. -- * Otherwise, vector simply points away from the face. function utility.rotation_vector_from_lookat(node, pointed_thing, edge_distance) local box = utility.get_node_active_selection_box(node) local center = (box.min + box.max) / 2 -- Calculate bounds, single vector describing the size of the selection box. local bounds = box.max - center -- This extends both into positive and negative. -- Calculate intersection point relative to these bounds. local point = pointed_thing.intersection_point - pointed_thing.under - center return point:combine(bounds, function(p, b) return b - math_abs(p) <= edge_distance and math_sign(p) or 0 end) end -- NodeCore only has a couple of "canonical" orientations for its nodes. -- This returns a canonical facedir equivalent to the one passed in. function utility.fix_rotatable_facedir(node, facedir) local name = node and node.name or "" local def = minetest.registered_nodes[name] if not def then return nil end if not def.spindata then return facedir end return def.spindata.equiv[facedir] end return utility