From 462fcff3768c89af744d49ec92b38e656b105efc Mon Sep 17 00:00:00 2001 From: Abigail Read Date: Tue, 17 Feb 2026 19:38:37 -0800 Subject: [PATCH 1/5] Default node facing to reflect camera angle This is intended only for blocks where the rotation serves no gameplay purpose other than cosmetic. I've touched things up and verified the accuracy of all the comments. I've also tested in-game, and it seems to all be working as intended. Feels very nice. --- mods/sbz_pipeworks/autoplace_tubes.lua | 81 ++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/mods/sbz_pipeworks/autoplace_tubes.lua b/mods/sbz_pipeworks/autoplace_tubes.lua index 56de3e91..6145c287 100644 --- a/mods/sbz_pipeworks/autoplace_tubes.lua +++ b/mods/sbz_pipeworks/autoplace_tubes.lua @@ -31,9 +31,58 @@ local function nodeside(node, tubedir) local right = vector.dot(rightdir, tubedir) if right == 1 then return "right" - else + elseif right == -1 then return "left" end + + -- Fallback with a warning for inapplicable values + -- (e.g. one that produces a direction not aligned to any axis). + core.log("warning", "[pipeworks] nodeside: unexpected tubedir, defaulting to left. " .. + "node.param2=" .. tostring(node.param2) .. + " tubedir=" .. minetest.pos_to_string(tubedir)) + return "left" +end + +-- Convert camera yaw and pitch to the nearest facedir param2 value. + +-- Degrees start at 0 facing North, increasing while turning counter-clockwise (if looking down from above): +-- 0 = looking toward +Z (North) +-- π/2 = looking toward -X (West) +-- π = looking toward -Z (South) +-- 3π/2 = looking toward +X (East) +-- +local function look_to_facedir(yaw, pitch) + -- Vertical placement threshold (directionality favors yaw, but allows pitch) + local limit = math.pi * 0.40 -- ≈ 72° + + -- Looking up means node faces down + if pitch > limit then + return 0 + elseif pitch < -limit then + return 20 + end + + -- Round yaw to nearest 90° sector. + -- Normalise to [0, 2π) first so the modulo is well-defined. + yaw = yaw % (2 * math.pi) + local sector = math.floor(yaw / (math.pi / 2) + 0.5) % 4 + + -- Map sector index to facedir param2. + -- Sector 0 (yaw ≈ 0): looking toward +Z (North) | param2 as 2 would make node face North + -- Sector 1 (yaw ≈ π/2): looking toward -X (West) | param2 as 1 would make node face West + -- Sector 2 (yaw ≈ π): looking toward -Z (South) | param2 as 0 would make node face South + -- Sector 3 (yaw ≈ 3π/2):looking toward +X (East) | param2 as 3 would make node face East + + -- Use opposite angles instead so that it faces camera + return ({ [0] = 0, 3, 2, 1 })[sector] -- node mirrors (not matches) camera angle when placed +end + +local function yaw_to_4dir(yaw) + yaw = yaw % (2 * math.pi) + local sector = math.floor(yaw / (math.pi / 2) + 0.5) % 4 + + -- Map yaw sectors to 4dir param2 + return ({ [0] = 0, 3, 2, 1 })[sector] end local vts = { 0, 3, 1, 4, 2, 5 } @@ -100,13 +149,37 @@ local function tube_autoroute(pos) end function pipeworks.scan_for_tube_objects(pos) - for side = 0, 6 do + for side = 1, 6 do tube_autoroute(vector.add(pos, pipeworks.directions.side_to_dir(side))) end end -function pipeworks.after_place(pos) - pipeworks.scan_for_tube_objects(pos) +-- FIX: after_place previously saved original_param2, called scan_for_tube_objects +-- (which skipped autorouting pos itself), then restored original_param2 — but that +-- restoration was misguided because: +-- 1. The placed node IS a tube and SHOULD be autorouted like any other tube. +-- 2. Neighbours were updated while seeing whatever param2 the node happened to +-- have at placement time, which may not match the final autorouted state. + +function pipeworks.after_place(pos, placer) + if placer and placer.get_look_horizontal then + local node = minetest.get_node(pos) + local def = minetest.registered_nodes[node.name] + + local yaw = placer:get_look_horizontal() + local pitch = placer.get_look_vertical and placer:get_look_vertical() or 0 + + if def.paramtype2 == "4dir" then + node.param2 = yaw_to_4dir(yaw) + elseif def.paramtype2 == "facedir" then + node.param2 = look_to_facedir(yaw, pitch) + end + + minetest.swap_node(pos, node) + end + + tube_autoroute(pos) + pipeworks.scan_for_tube_objects(pos) end function pipeworks.after_dig(pos) From 0ee676786b129eb94700b56e4939eefaa8d71788 Mon Sep 17 00:00:00 2001 From: Abigail Read Date: Tue, 17 Feb 2026 19:46:04 -0800 Subject: [PATCH 2/5] Enable storinator rotation after placement Since the metadata contains both color and facing, we have to bitmask and handle the proper bits for rotation. Tested ingame and working so far in all cases. --- mods/sbz_resources/storinators.lua | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/mods/sbz_resources/storinators.lua b/mods/sbz_resources/storinators.lua index 9d57640f..9ce61be3 100644 --- a/mods/sbz_resources/storinators.lua +++ b/mods/sbz_resources/storinators.lua @@ -105,6 +105,27 @@ local function register_storinator(added_name, def) def_copy.on_metadata_inventory_move = update_node_texture def_copy.info_extra = def_copy.slots .. " Slots" + def_copy.on_rotate = function(pos, node, user, mode, new_param2) + -- Only handle the 'Face' rotation (Left Click) + if mode ~= screwdriver.ROTATE_FACE then + return false + end + + -- 1. Isolate the rotation (first 5 bits) and the color (everything else) + local rotation = node.param2 % 32 + local color_bits = node.param2 - rotation + + -- 2. Manually cycle through the 4 horizontal directions (0, 1, 2, 3) + -- This makes it behave exactly like the Crystal Grower + local new_rotation = (rotation + 1) % 4 + + -- 3. Re-combine and apply + node.param2 = color_bits + new_rotation + minetest.swap_node(pos, node) + + return true -- Tells the screwdriver we handled it! + end + local dropname = "sbz_resources:storinator" if #added_name ~= 0 then dropname = dropname .. "_" .. added_name @@ -303,4 +324,4 @@ minetest.register_craft({ { "sbz_resources:matter_plate", "sbz_resources:simple_circuit", "sbz_resources:matter_plate" }, { "sbz_resources:retaining_circuit", "sbz_resources:matter_plate", "sbz_resources:retaining_circuit" } } -}) +}) \ No newline at end of file From 9a3dccbcccf0fa21649e6537f99cccae38470082 Mon Sep 17 00:00:00 2001 From: Abigail Read Date: Tue, 17 Feb 2026 19:49:19 -0800 Subject: [PATCH 3/5] Enable drawer rotation after placement Doing the same thing as with storinators here. Tested and working ingame in all cases so far. --- mods/drawers/api.lua | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/mods/drawers/api.lua b/mods/drawers/api.lua index c1153eb5..807b75dd 100755 --- a/mods/drawers/api.lua +++ b/mods/drawers/api.lua @@ -251,7 +251,26 @@ function drawers.register_drawer(name, def) def.on_metadata_inventory_put = drawers.add_drawer_upgrade def.on_metadata_inventory_take = drawers.remove_drawer_upgrade - if minetest.get_modpath 'screwdriver' and screwdriver then def.on_rotate = def.on_rotate or screwdriver.disallow end + if minetest.get_modpath 'screwdriver' and screwdriver then + def.on_rotate = function(pos, node, user, mode, new_param2) + if mode ~= screwdriver.ROTATE_FACE then + return false + end + + local rotation = node.param2 % 32 + local color_bits = node.param2 - rotation + local new_rotation = (rotation + 1) % 4 + + node.param2 = color_bits + new_rotation + minetest.swap_node(pos, node) + + -- Respawn visuals with new rotation + drawers.remove_visuals(pos) + drawers.spawn_visuals(pos) + + return true + end + end if minetest.get_modpath 'pipeworks' and pipeworks then def.groups.tubedevice = 1 @@ -411,4 +430,4 @@ function drawers.register_drawer_upgrade(name, def) } template = name end -end +end \ No newline at end of file From 4587622d2e3a7e1948b1a95db60d174627817e40 Mon Sep 17 00:00:00 2001 From: Abigail Read Date: Tue, 17 Feb 2026 19:52:15 -0800 Subject: [PATCH 4/5] Make punchers face toward, deployers/breakers away It makes more sense to me, as this is probably how you would set things up when playing, attaching node breakers and deployers to a nearby tube or other receptical rather than clicking a (for some reason already there?) plant for instance. Also, it makes it more obvious what you're looking at if the textured face that does stuff is visible from the get-go. --- mods/sbz_pipeworks/wielder.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/mods/sbz_pipeworks/wielder.lua b/mods/sbz_pipeworks/wielder.lua index d5d7d194..2b7da399 100644 --- a/mods/sbz_pipeworks/wielder.lua +++ b/mods/sbz_pipeworks/wielder.lua @@ -150,6 +150,12 @@ function pipeworks.register_wielder(def) -- Rotate to face the clicked block local dir = vector.subtract(pointed_thing.above, pointed_thing.under) + + -- Flip direction if this wielder should face away + if def.place_facing == "away" then + dir = vector.multiply(dir, -1) + end + local param2 = minetest.dir_to_facedir(dir, true) local node = minetest.get_node(pos) @@ -229,6 +235,7 @@ function pipeworks.register_wielder(def) }) end +-- Nodebreaker faces away from clicked block side when placed. pipeworks.register_wielder { name = 'pipeworks:nodebreaker', description = S 'Node Breaker', @@ -241,6 +248,7 @@ pipeworks.register_wielder { 'nodebreaker_front.png', }, connect_sides = { top = 1, bottom = 1, left = 1, right = 1, back = 1 }, + place_facing = "away", wield_inv = { name = 'pick', width = 1, height = 1 }, wield_hand = true, eject_drops = true, @@ -291,6 +299,7 @@ pipeworks.register_wielder { cost = 20, } +-- Deployer faces away from clicked block side when placed. pipeworks.register_wielder { name = 'pipeworks:deployer', description = S 'Deployer', @@ -302,6 +311,7 @@ pipeworks.register_wielder { 'deployer_back.png', 'deployer_front.png', }, + place_facing = "away", wield_inv = { name = 'main', width = 3, height = 3 }, connect_sides = { back = 1 }, action = function(fakeplayer, pointed) @@ -321,6 +331,7 @@ pipeworks.register_wielder { cost = 20, } +-- Puncher faces toward clicked block side when placed. pipeworks.register_wielder { name = 'pipeworks:puncher', description = S 'Puncher', @@ -332,6 +343,7 @@ pipeworks.register_wielder { 'interactor_bottom.png', -- back 'interactor_top.png', -- front }, + place_facing = "toward", wield_inv = { name = 'pick', width = 1, height = 1 }, connect_sides = { top = 1, bottom = 1, left = 1, right = 1, back = 1 }, wield_hand = true, @@ -403,4 +415,4 @@ do { I, I, I }, }, }) -end +end \ No newline at end of file From d0ddd3a08e39d8a304b0a85b3262e6dc318bf2b2 Mon Sep 17 00:00:00 2001 From: Abigail Read Date: Thu, 19 Feb 2026 04:08:26 -0800 Subject: [PATCH 5/5] Speed up placement There's not really much I can figure out how to do with this to get it any better at this time aside from shortcutting unnecessary checks that make it look glitchy due to processing time before orientating. Pipeworks is complicated. --- mods/sbz_pipeworks/basic_tubes.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mods/sbz_pipeworks/basic_tubes.lua b/mods/sbz_pipeworks/basic_tubes.lua index 06a35fa4..cce005a9 100644 --- a/mods/sbz_pipeworks/basic_tubes.lua +++ b/mods/sbz_pipeworks/basic_tubes.lua @@ -192,7 +192,9 @@ minetest.register_node('pipeworks:one_way_tube', { end, priority = 75, -- Higher than normal tubes, but lower than receivers }, - after_place_node = pipeworks.after_place, + after_place_node = function(pos) + pipeworks.scan_for_tube_objects(pos) + end, after_dig_node = pipeworks.after_dig, on_rotate = pipeworks.on_rotate, })