Compare commits

...

2 Commits

Author SHA1 Message Date
copygirl 8bf8c64841 Don't show rotation hint on "non-success" 7 months ago
copygirl ccc69b0697 Reorganization, don't show hint when holding pin 7 months ago
  1. 157
      init.lua
  2. 66
      registry.lua
  3. 15
      state.lua
  4. 13
      utility.lua

@ -1,25 +1,21 @@
local ipairs, ItemStack, minetest, vector, include, nodecore
= ipairs, ItemStack, minetest, vector, include, nodecore
local ipairs, ItemStack, vector, include, nodecore
= ipairs, ItemStack, vector, include, nodecore
rawset(_G, "nc_extended_rotating", {})
nc_extended_rotating.hud = include("hud")
nc_extended_rotating.registry = include("registry")
nc_extended_rotating.rotate = include("rotate")
nc_extended_rotating.utility = include("utility") -- Depends on `rotate`.
nc_extended_rotating.entity = include("entity") -- Depends on `utility`.
nc_extended_rotating.state = include("state") -- Depends on `registry`, `rotate` and `utility`.
nc_extended_rotating.utility = include("utility") -- Depends on `rotate`.
nc_extended_rotating.entity = include("entity") -- Depends on `utility`.
nc_extended_rotating.registry = include("registry") -- Depends on `rotate` and `state` (late).
nc_extended_rotating.state = include("state") -- Depends on `registry`, `rotate` and `utility`.
local update_entity_hint = nc_extended_rotating.entity.update_entity_hint
local update_player_hud = nc_extended_rotating.hud.update_player_hud
local is_rotatable = nc_extended_rotating.registry.is_rotatable
local register_rotatable = nc_extended_rotating.registry.register_rotatable;
local rotate_node = nc_extended_rotating.rotate.rotate_node
local update_rotating_state = nc_extended_rotating.state.update_rotating_state
local get_rotating_state = nc_extended_rotating.state.get_rotating_state
-------------------------------------
-- Register Player Update Function --
-------------------------------------
-- TODO: Fix HUD showing rotation hint when we wouldn't / can't rotate.
-- TODO: Simplify registration and `on_rightclick` replacement into one call.
-- TODO: Add some more comments.
local update_rotating_state = nc_extended_rotating.state.update_rotating_state
local update_player_hud = nc_extended_rotating.hud.update_player_hud
local update_entity_hint = nc_extended_rotating.entity.update_entity_hint
nodecore.register_playerstep({
label = "nc_extended_rotating:update",
@ -30,106 +26,39 @@ nodecore.register_playerstep({
end,
})
-- Register rotatable nodes from NodeCore.
do
local function nodecore_filtered_lookup(eq_func)
local lookup = {}
for i = 0, 23 do
local facedir = nodecore.facedirs[i]
for j = 0, #lookup - 1 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)
---------------------------------------
-- Register Rotatable NodeCore Nodes --
---------------------------------------
for _, lens_state in ipairs({ "", "_on", "_glow", "_glow_start" }) do
register_rotatable("nc_optics:lens" .. lens_state, nil, LENS_FILTERED_LOOKUP) end
for _, prism_state in ipairs({ "", "_on", "_gated" }) do
register_rotatable("nc_optics:prism" .. prism_state, nil, PRISM_FILTERED_LOOKUP) end
local nodecore_filtered_lookup = nc_extended_rotating.utility.nodecore_filtered_lookup
local register_rotatable = nc_extended_rotating.registry.register_rotatable;
-- Register all existing door panels.
nodecore.register_on_register_item({
retroactive = true,
func = function(name, def)
-- NOTE: This function is actually called BEFORE the item is registered with Minetest.
-- Because of this, looking up the node definition by name does not work.
name = name:gsub("^:", "") -- Fix for older versions of NodeCore.
if def.groups and def.groups.door_panel and (not def.groups.door)
then register_rotatable(name, def, PANEL_FILTERED_LOOKUP) end
end,
})
end
-- Register lenses.
local LENS_FILTERED_LOOKUP = nodecore_filtered_lookup(function(a, b)
return vector.equals(a.f, b.f) end)
for _, lens_state in ipairs({ "", "_on", "_glow", "_glow_start" }) do
register_rotatable("nc_optics:lens" .. lens_state, nil, LENS_FILTERED_LOOKUP) end
local function handle_rightclick(pos, node, clicker)
local state = get_rotating_state(clicker, pos, node)
if (not state) or (not state.success) then return end
rotate_node(pos, node, state.facedir)
end
-- Register prisms.
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)
for _, prism_state in ipairs({ "", "_on", "_gated" }) do
register_rotatable("nc_optics:prism" .. prism_state, nil, PRISM_FILTERED_LOOKUP) end
-- Replace `on_rightclick` of rotatable nodes from NodeCore.
do
local function replace_optics_on_rightclick(name)
local def = minetest.registered_nodes[name]
def.on_rightclick = handle_rightclick
end
-- Panels can't have their `on_rightclick` overridden completely.
-- We need to call the default implementation when holding a "pin".
local function replace_panel_on_rightclick(name, pin)
local def = minetest.registered_nodes[name]
local default_rightclick = def.on_rightclick
def.on_rightclick = function(pos, node, clicker, item_stack, ...)
if nodecore.protection_test(pos, clicker) or ItemStack(item_stack):get_name() == pin
then return default_rightclick(pos, node, clicker, item_stack, ...)
else return handle_rightclick(pos, node, clicker)
end
-- Register all existing and to-be-registered door panels.
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)
nodecore.register_on_register_item({
retroactive = true,
func = function(name, def)
-- This function is actually called BEFORE the item is registered with Minetest.
-- Because of this, looking up the node definition by name does not work.
-- Thankfully, the panel is registered before the door, and as such it already exists.
if def.groups and def.groups.door then
local panel = def.drop_in_place.name -- The panel version of the door.
local pin = def.drop_non_silktouch or def.drop -- Dropped item is the door's pin.
register_rotatable(panel, nil, PANEL_FILTERED_LOOKUP,
function(_, _, _, item_stack) return ItemStack(item_stack):get_name() == pin end)
end
end
for _, lens_state in ipairs({ "", "_on", "_glow", "_glow_start" }) do
replace_optics_on_rightclick("nc_optics:lens" .. lens_state) end
for _, lens_state in ipairs({ "", "_on", "_gated" }) do
replace_optics_on_rightclick("nc_optics:prism" .. lens_state) end
-- Replace `on_rightclick` for all existing doors' panels.
nodecore.register_on_register_item({
retroactive = true,
func = function(name, def)
if def.groups and def.groups.door then
local panel = def.drop_in_place.name -- The panel version of the door.
local pin = def.drop_non_silktouch or def.drop -- Dropped item is the door's pin.
replace_panel_on_rightclick(panel, pin)
end
end,
})
end
-- Override `nc_scaling`'s default empty hand right-click behavior so we can use it
-- to rotate things when holding sneak, which doesn't trigger a node's `on_rightclick`.
local default_on_place = minetest.registered_items[""].on_place
minetest.registered_items[""].on_place = function(item_stack, placer, pointed_thing, ...)
if pointed_thing.type == "node" then
-- Player must sneak in order for this to run.
-- Non-sneak is handled by node's `on_rightclick`.
if minetest.is_player(placer) and placer:get_player_control().sneak then
local pos = pointed_thing.under
local node = minetest.get_node(pos)
if is_rotatable(node) then
handle_rightclick(pos, node, placer)
return -- Skip default behavior.
end
end
end
-- Call the default function, so we can still get the "scaling" functionality.
default_on_place(item_stack, placer, pointed_thing, ...)
end
end,
})

@ -1,17 +1,57 @@
local minetest
= minetest
local minetest, nodecore
= minetest, nodecore
-- Table of nodes that can be rotated by the mod.
local registered_rotatables = {}
local registry = {}
function registry.register_rotatable(name, def, facedir_lookup)
def = def or minetest.registered_nodes[name]
if not def then error("Unknown node '" .. name .. "'") end
-- Override `nc_scaling`'s default empty hand right-click behavior so we can use it
-- to rotate things when holding sneak, which doesn't trigger a node's `on_rightclick`.
local default_on_place = minetest.registered_items[""].on_place
minetest.registered_items[""].on_place = function(item_stack, placer, pointed_thing, ...)
if pointed_thing.type == "node" then
-- Player must sneak in order for this to run.
-- Non-sneak is handled by node's `on_rightclick`.
if minetest.is_player(placer) and placer:get_player_control().sneak then
local pos = pointed_thing.under
local node = minetest.get_node(pos)
if registry.is_rotatable(node) then
local def = minetest.registered_nodes[node.name]
def.on_rightclick(pos, node, placer, item_stack, pointed_thing)
return -- Skip default behavior.
end
end
end
-- Call the default function, so we can still get the "scaling" functionality.
default_on_place(item_stack, placer, pointed_thing, ...)
end
local rotate_node = nc_extended_rotating.rotate.rotate_node
-- local get_rotating_state = nc_extended_rotating.state.get_rotating_state
function registry.register_rotatable(name, def, facedir_lookup, rightclick_filter_func)
def = def or minetest.registered_nodes[name] or error("Unknown node '" .. name .. "'")
if def.paramtype2 ~= "facedir" then error("Node '" .. name .. "' must be 'facedir'") end
registered_rotatables[name] = { lookup = facedir_lookup }
-- Replace the default `on_rightclick` function so
-- we can call our custom rotation handling code.
local default_on_rightclick = def.on_rightclick
def.on_rightclick = function(pos, node, clicker, ...)
-- If `rightclick_filter_func` is set and returns true, call the default `on_rightclick`.
if registry.is_rightclick_filtered(pos, node, clicker, ...) then
default_on_rightclick(pos, node, clicker, ...)
else
if nodecore.protection_test(pos, clicker) then return end
local state = nc_extended_rotating.state.get_rotating_state(clicker, pos, node)
if (not state) or (not state.success) then return end
rotate_node(pos, node, state.facedir)
end
end
registered_rotatables[name] = {
facedir_lookup = facedir_lookup,
rightclick_filter_func = rightclick_filter_func,
}
end
-- Returns whether the specified `node` is rotatable by this mod.
@ -19,6 +59,14 @@ function registry.is_rotatable(node)
return registered_rotatables[node.name] ~= nil
end
-- Returns whether the `on_rightclick` is filtered and the default
-- implementation should be called, instead of the custom rotation logic.
function registry.is_rightclick_filtered(pos, node, player, item_stack, pointed_thing)
local entry = registered_rotatables[node.name]
return entry and entry.rightclick_filter_func
and entry.rightclick_filter_func(pos, node, player, item_stack, pointed_thing)
end
-- 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.
@ -27,8 +75,8 @@ function registry.fix_rotatable_facedir(node, facedir)
local name = node and node.name or ""
local entry = registered_rotatables[name]
if not entry then return nil end
if not entry.lookup then return facedir end
return entry.lookup[facedir]
if not entry.facedir_lookup then return facedir end
return entry.facedir_lookup[facedir]
end
return registry

@ -2,6 +2,7 @@ local math_round, minetest, vector
= math.round, minetest, vector
local fix_rotatable_facedir = nc_extended_rotating.registry.fix_rotatable_facedir;
local is_rightclick_filtered = nc_extended_rotating.registry.is_rightclick_filtered;
local is_rotatable = nc_extended_rotating.registry.is_rotatable;
local rotate_facedir = nc_extended_rotating.rotate.rotate_facedir;
local rotation_vector_from_lookat = nc_extended_rotating.utility.rotation_vector_from_lookat;
@ -32,17 +33,22 @@ local function calculate_rotating_state(player, data)
local pointed_thing = data.raycast()
if (not pointed_thing) or pointed_thing.type ~= "node" then return nil end
if vector.equals(pointed_thing.above, pointed_thing.under) then return nil end
state.face = pointed_thing.above - pointed_thing.under
state.pos = pointed_thing.under
state.node = minetest.get_node(state.pos)
if not is_rotatable(state.node) then return nil end
if vector.equals(pointed_thing.above, pointed_thing.under) then return nil end
state.face = pointed_thing.above - pointed_thing.under
local wielded = player:get_wielded_item()
-- When player is sneaking, must be empty-handed for rotating to work.
local is_sneaking = player:get_player_control().sneak
if is_sneaking and (not player:get_wielded_item():is_empty()) then return nil end
if is_sneaking and (not wielded:is_empty()) then return nil end
-- Rightclick action is being filtered and the default `on_righclick` will be called instead.
if is_rightclick_filtered(state.pos, state.node, player, wielded, pointed_thing) then return nil end
local degrees = 90
local r = rotation_vector_from_lookat(state.node, pointed_thing, EDGE_DISTANCE)
@ -63,6 +69,9 @@ local function calculate_rotating_state(player, data)
state.facedir = fix_rotatable_facedir(state.node, state.facedir)
state.success = state.facedir and state.facedir ~= state.node.param2
-- TODO: Until we have a way to display "rotation non-success", don't show rotation hint at all.
if not state.success then return nil end
return state
end

@ -44,4 +44,17 @@ function utility.rotation_vector_from_lookat(node, pointed_thing, edge_distance)
return b - math_abs(p) <= edge_distance and math_sign(p) or 0 end)
end
function utility.nodecore_filtered_lookup(eq_func)
local lookup = {}
for i = 0, 23 do
local facedir = nodecore.facedirs[i]
for j = 0, #lookup - 1 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
return utility

Loading…
Cancel
Save