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.entity = include("entity") -- Depends on `utility`. 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 -- 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. nodecore.register_playerstep({ label = "nc_extended_rotating:update", action = function(player, data) local state = update_rotating_state(player, data) update_player_hud(player, state) update_entity_hint(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 all existing door panels. nodecore.register_on_register_item({ retroactive = true, func = function(name, def) if def.groups and def.groups.door_panel and (not def.groups.door) then register_rotatable(name, PANEL_FILTERED_LOOKUP) end end, }) 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 `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 -- The panel version of the door. local pin = def.drop -- Dropped item is the door's pin. replace_panel_on_rightclick(panel.name, pin.name) 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