Rotate optics and doors from NodeCore with ease.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 lines
5.1 KiB

local ipairs, ItemStack, minetest, vector, include, nodecore
= ipairs, ItemStack, minetest, 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.state = include("state") -- Depends on `registry`, `rotate` and `utility`.
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
-- TODO: Fix HUD showing rotation hint when we wouldn't / can't rotate.
-- TODO: Add crosshair indicators for rotating around edges.
-- TODO: Add some more comments.
-- TODO: Add particles to preview rotation?
nodecore.register_playerstep({
label = "nc_extended_rotating:update",
action = function(player, data)
local state = update_rotating_state(player, data)
update_player_hud(player, state)
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)
for _, lens_state in ipairs({ "", "_on", "_glow", "_glow_start" }) do
register_rotatable("nc_optics:lens" .. lens_state, LENS_FILTERED_LOOKUP) end
for _, prism_state in ipairs({ "", "_on", "_gated" }) do
register_rotatable("nc_optics:prism" .. prism_state, PRISM_FILTERED_LOOKUP) end
register_rotatable("nc_doors:panel_plank" , PANEL_FILTERED_LOOKUP)
register_rotatable("nc_doors:panel_cobble", PANEL_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
-- 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
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_panel_on_rightclick("nc_doors:panel_plank" , "nc_woodwork:staff")
replace_panel_on_rightclick("nc_doors:panel_cobble", "nc_lode:rod_tempered")
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