Compare commits

..

No commits in common. 'b379937511bd9ffb665183f1ede8c240fed0c5dc' and '000c6f520f9d3bea260251756fb53150a088ac50' have entirely different histories.

  1. 4
      README.md
  2. 87
      init.lua
  3. 2
      mod.conf
  4. 68
      rotate.lua

@ -13,7 +13,3 @@ away from you. Sneaking inverts the direction of the rotation.
At this time, nodes that can be rotated are hardcoded into the mod, since
their valid orientations can't be easily extracted. A function mimicking
NodeCore's own rotation logic has to be provided.
[Minetest]: https://www.minetest.net/
[NodeCore]: https://content.minetest.net/packages/Warr1024/nodecore/
[Extended Placement]: https://content.minetest.net/packages/gamefreq0/extended_placement/

@ -1,61 +1,28 @@
local ipairs, minetest, vector, nodecore, include
= ipairs, minetest, vector, nodecore, include
local minetest, vector, nodecore, include
= minetest, vector, nodecore, include
local hud = include("hud")
local rotate = include("rotate")
local utility = include("utility")
-- TODO: Add crosshair indicators for rotating around edges.
-- TODO: Replace `on_rightclick` of intended nodes?
-- TODO: Allow right-clicking while holding sneak with an empty hand to invert rotation.
-- TODO: Register only the nodes that should be rotatable.
-- TODO: Replace `on_rightclick` of intended nodes.
-- TODO: Add some more comments.
-- Distance at which we want to rotate by "pushing" on an edge.
local EDGE_DISTANCE = 1.5 / 16 -- 1.5 texels (at 16² texture resolution)
-- Contains a per-player state that holds information, created by a raycast
-- done in `playerstep`, about the node this player might rotate. This is used
-- to decide what to do when a rightclick occurs and to update the their HUD.
local rotating_state = {}
-- Register rotatable nodes.
do
local function nodecore_filtered_lookup(eq_func)
local lookup = {}
for i = 0, 23 do
local facedir = nodecore.facedirs[i]
for j = 1, #lookup do
local other = nodecore.facedirs[lookup[j]]
if eq_func(facedir, other) then lookup[i] = j; break end
end
lookup[i] = lookup[i] or i
end
return lookup
end
local LENS_FILTERED_LOOKUP = nodecore_filtered_lookup(
function(a, b) return vector.equals(a.f, b.f) end)
local PRISM_FILTERED_LOOKUP = nodecore_filtered_lookup(
function(a, b) return vector.equals(a.f, b.r) and vector.equals(a.r, b.f) end)
local PANEL_FILTERED_LOOKUP = nodecore_filtered_lookup(
function(a, b) return vector.equals(a.f, b.r) and vector.equals(a.r, b.f) end)
for _, lens_state in ipairs({ "", "_on", "_glow", "_glow_start" }) do
rotate.register_rotatable("nc_optics:lens" .. lens_state, LENS_FILTERED_LOOKUP) end
for _, prism_state in ipairs({ "", "_on", "_gated" }) do
rotate.register_rotatable("nc_optics:prism" .. prism_state, PRISM_FILTERED_LOOKUP) end
rotate.register_rotatable("nc_doors:panel_plank" , PANEL_FILTERED_LOOKUP)
rotate.register_rotatable("nc_doors:panel_cobble", PANEL_FILTERED_LOOKUP)
end
-- Calculates the state for the specified `player` and player `data`.
-- This state contains information about how the player would rotate a block.
local function calculate_rotating_state(player, data)
local state = {}
nodecore.register_playerstep({
label = "extended rotating update",
action = function(player, player_data)
local name = player:get_player_name()
local state = {}; rotating_state[name] = state
local pointed_thing = data.raycast()
if (not pointed_thing) or pointed_thing.type ~= "node" then return end
local pointed_thing = player_data.raycast()
if (not pointed_thing) or pointed_thing.type ~= "node" then return nil end
state.pos = pointed_thing.under
state.node = minetest.get_node(state.pos)
@ -65,31 +32,16 @@ local function calculate_rotating_state(player, data)
state.face = pointed_thing.above - pointed_thing.under
local edge, distance = utility.find_closest_edge(state.node, pointed_thing)
-- FIXME: Something in `find_closest_edge` is causing an unexpected edge to be returned, hence the additional check.
if edge and distance <= EDGE_DISTANCE and state.face:multiply(edge):length() ~= 0 then
state.axis = state.face:cross(edge):normalize()
state.mode = "edge"
else
if edge and distance > EDGE_DISTANCE then
state.axis = state.face
state.mode = "face"
else
state.axis = state.face:cross(edge):normalize():round()
state.mode = "edge"
end
state.invert = player:get_player_control().sneak
local degrees = state.invert and 90 or -90
state.facedir = rotate.rotate_facedir(state.node.param2, state.axis, degrees)
state.facedir = rotate.fix_rotatable_facedir(state.node, state.facedir)
state.success = state.facedir and state.facedir ~= state.node.param2
return state
end
nodecore.register_playerstep({
label = "extended rotating update",
action = function(player, data)
local name = player:get_player_name()
rotating_state[name] = calculate_rotating_state(player, data)
hud.update_player_hud(player, rotating_state[name])
hud.update_player_hud(player, state)
end,
})
@ -98,6 +50,7 @@ minetest.register_on_leaveplayer(function(player)
rotating_state[name] = nil
end)
minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing)
if not minetest.is_player(puncher) then return end
local name = puncher:get_player_name()
@ -108,8 +61,6 @@ minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing)
if not vector.equals(pos, state.pos) then return end
if node.name ~= state.node.name or node.param2 ~= state.node.param2 then return end
-- Only continue if the node can actually be sucessfully rotated.
if not state.success then return end
minetest.swap_node(pos, nodecore.underride({ param2 = state.facedir }, node))
local degrees = state.invert and 90 or -90
rotate.rotate_node(state.pos, state.node, state.axis, degrees)
end)

@ -2,4 +2,4 @@ name = nc_extended_rotating
title = NodeCore Extended Rotating
author = copygirl
description = Rotate optics and doors from NodeCore with ease.
depends = nc_api_all, nc_optics, nc_doors
depends = nc_api_all

@ -7,8 +7,6 @@ local minetest, vector
local rotate = {}
local rotatable_registry = {}
-- Axis lookup table based on how Minetest's `facedir` operates.
local AXIS_LOOKUP = {
vector.new( 0, 1, 0), -- +Y
@ -52,54 +50,14 @@ for up_index, up in ipairs(AXIS_LOOKUP) do
end
end
local function register_rotatable(name, facedir_lookup)
local def = minetest.registered_nodes[name]
if not def then error("Unknown node '" .. name .. "'") end
if def.paramtype2 ~= "facedir" then error("Node '" .. name .. "' must be 'facedir'") end
rotatable_registry[name] = { lookup = facedir_lookup }
end
rotate.register_rotatable = register_rotatable
-- Returns whether the specified `node` is rotatable by this mod.
local function is_rotatable(node)
local name = node and node.name or ""
return not not rotatable_registry[name]
local name = node and node.name
local def = minetest.registered_nodes[name]
return def and def.paramtype2 == "facedir"
end
rotate.is_rotatable = is_rotatable
-- Fixes facedir for the specified `node` when rotated to `facedir`.
-- * Returns `nil` if the node can't rotate this way.
-- * Returns `facedir` when the node can rotate this way.
-- * Returns another facedir when the node should rotate another equivalent way.
local function fix_rotatable_facedir(node, facedir)
local name = node and node.name or ""
local entry = rotatable_registry[name]
if not entry then return nil end
if not entry.lookup then return facedir end
return entry.lookup[facedir]
end
rotate.fix_rotatable_facedir = fix_rotatable_facedir
-- Returns the specified `facedir` rotated around `axis` by `degrees` counter-clickwise.
local function rotate_facedir(facedir, 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 " .. tostring(axis) .. " is not an axis vector") end
local up = AXIS_LOOKUP[1 + math_floor(facedir / 4)]
local back = minetest.facedir_to_dir(facedir)
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)
return FACEDIR_LOOKUP[up_index][back_index]
end
rotate.rotate_facedir = rotate_facedir
-- Rotates the specified default orientation `{ min, max }` box to
-- one matching Minetest's rotation algorithm pointing to `facedir`.
local function rotate_box_by_facedir(box, facedir)
if facedir == 0 then return end
@ -125,4 +83,24 @@ local function rotate_box_by_facedir(box, facedir)
end
rotate.rotate_box_by_facedir = rotate_box_by_facedir
-- 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

Loading…
Cancel
Save