From f5683cb6c1c9479c6db34315e2c3b99367be9a6d Mon Sep 17 00:00:00 2001 From: Aaron Suen Date: Sun, 15 Sep 2024 14:04:17 -0400 Subject: [PATCH] Partially working new rotation API - Things can be registered to rotate - Rotations apply correctly based on direction arrows - Doors and optics registered Known issues: - param2 equivalence, i.e. ensuring each node has the lowest value for param2 that is semantically equivalent. --- mods/nc_api/util_misc.lua | 11 ++ mods/nc_api/util_spin.lua | 2 + mods/nc_api_rotate/api.lua | 152 ++++++++++++++++++ mods/nc_api_rotate/hud.lua | 76 +++++++++ mods/nc_api_rotate/init.lua | 9 ++ mods/nc_api_rotate/mod.conf | 1 + .../textures/nc_api_rotate_hudarrow_long.png} | Bin .../nc_api_rotate_hudarrow_short.png} | Bin .../textures/src/nc_api_rotate_hudarrow.svg} | 0 mods/nc_doors/register.lua | 15 +- mods/nc_optics/lens.lua | 14 +- mods/nc_optics/prism.lua | 16 +- mods/nc_player_rotate/init.lua | 106 ------------ mods/nc_player_rotate/mod.conf | 1 - 14 files changed, 279 insertions(+), 124 deletions(-) create mode 100644 mods/nc_api_rotate/api.lua create mode 100644 mods/nc_api_rotate/hud.lua create mode 100644 mods/nc_api_rotate/init.lua create mode 100644 mods/nc_api_rotate/mod.conf rename mods/{nc_player_rotate/textures/nc_player_rotate_hudarrow_long.png => nc_api_rotate/textures/nc_api_rotate_hudarrow_long.png} (100%) rename mods/{nc_player_rotate/textures/nc_player_rotate_hudarrow_short.png => nc_api_rotate/textures/nc_api_rotate_hudarrow_short.png} (100%) rename mods/{nc_player_rotate/textures/src/nc_player_rotate_hudarrow.svg => nc_api_rotate/textures/src/nc_api_rotate_hudarrow.svg} (100%) delete mode 100644 mods/nc_player_rotate/init.lua delete mode 100644 mods/nc_player_rotate/mod.conf diff --git a/mods/nc_api/util_misc.lua b/mods/nc_api/util_misc.lua index 258dfb37..fa57a687 100644 --- a/mods/nc_api/util_misc.lua +++ b/mods/nc_api/util_misc.lua @@ -128,6 +128,17 @@ function nodecore.dirs() } end +function nodecore.vector_to_dir(p) + local ax = math_abs(p.x) + local ay = math_abs(p.y) + local az = math_abs(p.z) + return ay > ax and ay > az + and vector.new(0, p.y < 0 and -1 or 1, 0) + or ax > az + and vector.new(p.x < 0 and -1 or 1, 0, 0) + or vector.new(0, 0, p.z < 0 and -1 or 1) +end + function nodecore.pickrand(tbl, weight, rng) weight = weight or function() end local t = {} diff --git a/mods/nc_api/util_spin.lua b/mods/nc_api/util_spin.lua index df1894b8..3b15ae32 100644 --- a/mods/nc_api/util_spin.lua +++ b/mods/nc_api/util_spin.lua @@ -4,6 +4,7 @@ local minetest, nodecore -- LUALOCALS > --------------------------------------------------------- function nodecore.spin_filter_facedirs(func) + minetest.log("warning", "deprecated nodecore.spin_filter_facedirs; use nc_player_rotate") local allowed = {} local equiv = {} for i = 0, 23 do @@ -37,6 +38,7 @@ function nodecore.spin_filter_facedirs(func) end function nodecore.spin_node_cycle(pos, node, clicker, itemstack) + minetest.log("warning", "deprecated nodecore.spin_node_cycle; use nc_player_rotate") if nodecore.protection_test(pos, clicker) then return end node = node or minetest.get_node(pos) local def = minetest.registered_items[node.name] or {} diff --git a/mods/nc_api_rotate/api.lua b/mods/nc_api_rotate/api.lua new file mode 100644 index 00000000..3a2d61c4 --- /dev/null +++ b/mods/nc_api_rotate/api.lua @@ -0,0 +1,152 @@ +-- LUALOCALS < --------------------------------------------------------- +local ipairs, minetest, nodecore, table, vector + = ipairs, minetest, nodecore, table, vector +local table_concat + = table.concat +-- LUALOCALS > --------------------------------------------------------- + +local vec_to_dir = nodecore.vector_to_dir + +local vector_add = vector.add +local vector_multiply = vector.multiply +local vector_subtract = vector.subtract +local vector_cross = vector.cross +local vector_equals = vector.equals + +local function rotkey(v, param2) + return table_concat({ + param2, + -- negative zeros >:-( + v.x == 0 and 0 or v.x, + v.y == 0 and 0 or v.y, + v.z == 0 and 0 or v.z + }, ":") +end + +do + local vz = vector.zero() + local function rotcheck(a, b, c) + local x = vector_cross(a, b) + return vector.equals(x, vz) and vector_equals(a, b) or vector_equals(x, c) + end + function nodecore.rotation_filter(equiv_func) + local lut = {} + for _, dir in ipairs(nodecore.dirs()) do + for fromp2 = 0, 23 do + local fromfd = nodecore.facedirs[fromp2] + local key = rotkey(dir, fromp2) + for top2 = 0, 23 do + if fromp2 ~= top2 then + local tofd = nodecore.facedirs[top2] + if rotcheck(fromfd.t, tofd.t, dir) + and rotcheck(fromfd.f, tofd.f, dir) + then + for chkp2 = 0, top2 do + if chkp2 == top2 or equiv_func( + nodecore.facedirs[chkp2], + tofd + ) then + lut[key] = top2 + break + end + end + break + end + end + if lut[key] then break end + end + end + end + return lut + end +end + +local function getcheck(pname, pos, group) + if minetest.is_protected(pos, pname) then return end + local node = minetest.get_node_or_nil(pos) + if not node then return end + local def = minetest.registered_nodes[node.name] + if not def then return end + local grps = def.groups + if not (grps and (grps[group] or 0) > 0) then return end + local rots = def.nc_rotations + if not rots then return end + return pos, node, rots +end + +function nodecore.rotation_compute(player, pointed_thing) + if not (pointed_thing and pointed_thing.above and pointed_thing.under + and pointed_thing.intersection_point + and pointed_thing.intersection_normal) then return end + + local pname = player:get_player_name() + local pos, node, lut = getcheck(pname, pointed_thing.above, "nc_api_rotate_above") + if not pos then + pos, node, lut = getcheck(pname, pointed_thing.under, "nc_api_rotate_under") + if not pos then return end + end + + local facectr = vector_multiply(vector_add(pointed_thing.above, pointed_thing.under), 0.5) + local facerel = vector_subtract(pointed_thing.intersection_point, facectr) + + local cdata = { + vector = pointed_thing.intersection_normal, + facectr = facectr, + facerel = facerel, + } + cdata.param2 = lut[rotkey(cdata.vector, node.param2)] + + if cdata.param2 + and facerel.x > -1/4 and facerel.x < 1/4 + and facerel.y > -1/4 and facerel.y < 1/4 + and facerel.z > -1/4 and facerel.z < 1/4 + then return pos, node, cdata end + + local rotdir = vec_to_dir(facerel) + local rdata = { + vector = vector_cross(pointed_thing.intersection_normal, rotdir), + facectr = facectr, + facerel = facerel, + rotdir = rotdir, + } + rdata.param2 = lut[rotkey(rdata.vector, node.param2)] + if rdata.param2 then return pos, node, rdata end + + if cdata.param2 then return pos, node, cdata end +end + +local function raycast(player) + local pos = player:get_pos() + pos.y = pos.y + player:get_properties().eye_height + local look = player:get_look_dir() + local wield = minetest.registered_items[player + :get_wielded_item():get_name()] + local range = wield and wield.range or 4 + local target = vector_add(pos, vector_multiply(look, range)) + for pt in minetest.raycast(pos, target, true, false) do + if pt.type == "object" or pt.ref ~= player + or pt.ref:get_attach() ~= player then + return pt + end + end +end + +function nodecore.rotation_apply(player, pointed_thing) + if not (pointed_thing and pointed_thing.above + and pointed_thing.under) then return end + + local pt = raycast(player) + if not (pt and pt.above and vector_equals(pt.above, pointed_thing.above) + and pt.under and vector_equals(pt.under, pointed_thing.under)) + then return end + + local pos, node, rotdata = nodecore.rotation_compute(player, pt) + if not rotdata then return end + + node.param2 = rotdata.param2 + nodecore.set_loud(pos, node) +end + +function nodecore.rotation_on_rightclick(_, _, clicker, _, pointed) + return nodecore.rotation_apply(clicker, pointed) +end diff --git a/mods/nc_api_rotate/hud.lua b/mods/nc_api_rotate/hud.lua new file mode 100644 index 00000000..c35c508c --- /dev/null +++ b/mods/nc_api_rotate/hud.lua @@ -0,0 +1,76 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore, tostring, vector + = math, minetest, nodecore, tostring, vector +local math_pi + = math.pi +-- LUALOCALS > --------------------------------------------------------- + +nodecore.amcoremod() + +local modname = minetest.get_current_modname() + +local vec_to_dir = nodecore.vector_to_dir + +local transform_by_scrkey = { + ["0-1"] = "I", + ["10"] = "R270", + ["01"] = "R180", + ["-10"] = "R90", +} + +nodecore.register_playerstep({ + label = "rotation scan", + action = function(player, data) + local pt = data.raycast() + + local _, _, rot = nodecore.rotation_compute(player, pt) + if not rot then + return nodecore.hud_set(player, { + label = modname, + ttl = 0, + quick = true + }) + end + + if not rot.rotdir then + return nodecore.hud_set(player, { + label = modname, + hud_elem_type = "image_waypoint", + text = "nc_api_rotate_hudarrow_long.png", + scale = {x = 1, y = 1}, + world_pos = rot.facectr, + precision = 0, + quick = true + }) + end + + local lookdir = player:get_look_dir() + local camrt = minetest.yaw_to_dir(player:get_look_horizontal() - math_pi / 2) + local camup = vector.cross(camrt, lookdir) + local function screenspace(p) + return vector.new(vector.dot(p, camrt), vector.dot(p, camup), 0) + end + local scrrot = screenspace(rot.rotdir) + local scrnorm = screenspace(pt.intersection_normal) + + local txr = nodecore.tmod("nc_api_rotate_hudarrow_short.png") + if vec_to_dir(vector.cross(scrrot, scrnorm)).z > 0 then + txr = txr:transform("FX") + end + do + local r = vec_to_dir(scrrot) + txr = txr:transform(transform_by_scrkey[r.x .. r.y]) + end + + return nodecore.hud_set(player, { + label = modname, + hud_elem_type = "image_waypoint", + text = tostring(txr), + scale = {x = 1, y = 1}, + world_pos = vector.add(rot.facectr, + vector.multiply(rot.rotdir, 0.4)), + precision = 0, + quick = true + }) + end + }) diff --git a/mods/nc_api_rotate/init.lua b/mods/nc_api_rotate/init.lua new file mode 100644 index 00000000..4fffb0dc --- /dev/null +++ b/mods/nc_api_rotate/init.lua @@ -0,0 +1,9 @@ +-- LUALOCALS < --------------------------------------------------------- +local include, nodecore + = include, nodecore +-- LUALOCALS > --------------------------------------------------------- + +nodecore.amcoremod() + +include("api") +include("hud") diff --git a/mods/nc_api_rotate/mod.conf b/mods/nc_api_rotate/mod.conf new file mode 100644 index 00000000..273098af --- /dev/null +++ b/mods/nc_api_rotate/mod.conf @@ -0,0 +1 @@ +depends = nc_api, nc_api_active \ No newline at end of file diff --git a/mods/nc_player_rotate/textures/nc_player_rotate_hudarrow_long.png b/mods/nc_api_rotate/textures/nc_api_rotate_hudarrow_long.png similarity index 100% rename from mods/nc_player_rotate/textures/nc_player_rotate_hudarrow_long.png rename to mods/nc_api_rotate/textures/nc_api_rotate_hudarrow_long.png diff --git a/mods/nc_player_rotate/textures/nc_player_rotate_hudarrow_short.png b/mods/nc_api_rotate/textures/nc_api_rotate_hudarrow_short.png similarity index 100% rename from mods/nc_player_rotate/textures/nc_player_rotate_hudarrow_short.png rename to mods/nc_api_rotate/textures/nc_api_rotate_hudarrow_short.png diff --git a/mods/nc_player_rotate/textures/src/nc_player_rotate_hudarrow.svg b/mods/nc_api_rotate/textures/src/nc_api_rotate_hudarrow.svg similarity index 100% rename from mods/nc_player_rotate/textures/src/nc_player_rotate_hudarrow.svg rename to mods/nc_api_rotate/textures/src/nc_api_rotate_hudarrow.svg diff --git a/mods/nc_doors/register.lua b/mods/nc_doors/register.lua index 33c6453c..d65d2f85 100644 --- a/mods/nc_doors/register.lua +++ b/mods/nc_doors/register.lua @@ -35,13 +35,17 @@ function nodecore.register_door(basemod, basenode, desc, pin, lv, basedef) tiles[4] = tiles[4] .. scuff .. ")" tiles[5] = tiles[5] .. scuff .. "^[transformR180)" - local spindata = nodecore.spin_filter_facedirs(function(a, b) + local nc_rotations = nodecore.rotation_filter(function(a, b) return vector.equals(a.f, b.r) and vector.equals(a.r, b.f) end) local doorname = modname .. ":door_" .. basenode - local groups = nodecore.underride({door_panel = lv}, basedef.groups) + local groups = nodecore.underride({ + door_panel = lv, + nc_api_rotate_under = 1, + nc_api_rotate_3d = 1, + }, basedef.groups) local paneldef = nodecore.underride({}, { name = modname .. ":panel_" .. basenode, description = (desc or basedef.description) .. " Panel", @@ -49,13 +53,12 @@ function nodecore.register_door(basemod, basenode, desc, pin, lv, basedef) paramtype2 = "facedir", silktouch = false, groups = groups, - spindata = spindata, - on_rightclick = function(pos, node, clicker, stack, pointed, ...) + nc_rotations = nc_rotations, + on_rightclick = function(pos, node, clicker, stack, pointed) if nodecore.protection_test(pos, clicker) then return end stack = stack and ItemStack(stack) if (not stack) or (stack:get_name() ~= pin) then - return nodecore.spin_node_cycle(pos, node, - clicker, stack, pointed, ...) + return nodecore.rotation_apply(clicker, pointed) end local fd = node and node.param2 or 0 fd = nodecore.facedirs[fd] diff --git a/mods/nc_optics/lens.lua b/mods/nc_optics/lens.lua index d64e4ec1..9da7b56e 100644 --- a/mods/nc_optics/lens.lua +++ b/mods/nc_optics/lens.lua @@ -34,6 +34,10 @@ local pact = modname .. "_port_active.png" local pout = modname .. "_port_output.png" local pinp = modname .. "_port_input.png" +local nc_rotations = nodecore.rotation_filter(function(a, b) + return vector.equals(a.f, b.f) + end) + local basedef = { description = "Lens", drawtype = "mesh", @@ -54,7 +58,9 @@ local basedef = { optic_lens = 1, cracky = 3, scaling_time = 125, - optic_gluable = 1 + optic_gluable = 1, + nc_api_rotate_under = 1, + nc_api_rotate_3d = 1, }, silktouch = false, drop = modname .. ":lens", @@ -65,10 +71,8 @@ local basedef = { optic_check = lens_check, paramtype = "light", paramtype2 = "facedir", - spindata = nodecore.spin_filter_facedirs(function(a, b) - return vector.equals(a.f, b.f) - end), - on_rightclick = nodecore.spin_node_cycle, + nc_rotations = nc_rotations, + on_rightclick = nodecore.rotation_on_rightclick, sounds = nodecore.sounds("nc_optics_glassy"), nc_optic_family = "lens", stackfamily = modname .. ":lens", diff --git a/mods/nc_optics/prism.lua b/mods/nc_optics/prism.lua index 185c650b..8802dd61 100644 --- a/mods/nc_optics/prism.lua +++ b/mods/nc_optics/prism.lua @@ -25,6 +25,11 @@ local pina = modname .. "_port_wide_act.png" local shin = modname .. "_shine_end.png" local dark = modname .. "_port_input.png" +local nc_rotations = nodecore.rotation_filter(function(a, b) + return vector.equals(a.f, b.r) + and vector.equals(a.r, b.f) + end) + local basedef = { description = "Prism", drawtype = "mesh", @@ -44,7 +49,9 @@ local basedef = { cracky = 3, silica_prism = 1, scaling_time = 125, - optic_gluable = 1 + optic_gluable = 1, + nc_api_rotate_under = 1, + nc_api_rotate_3d = 1, }, silktouch = false, drop = modname .. ":prism", @@ -55,11 +62,8 @@ local basedef = { optic_check = prism_check, paramtype = "light", paramtype2 = "facedir", - spindata = nodecore.spin_filter_facedirs(function(a, b) - return vector.equals(a.f, b.r) - and vector.equals(a.r, b.f) - end), - on_rightclick = nodecore.spin_node_cycle, + nc_rotations = nc_rotations, + on_rightclick = nodecore.rotation_on_rightclick, sounds = nodecore.sounds("nc_optics_glassy"), nc_optic_family = "prism", mapcolor = {r = 139, g = 187, b = 212}, diff --git a/mods/nc_player_rotate/init.lua b/mods/nc_player_rotate/init.lua deleted file mode 100644 index 5ae5478b..00000000 --- a/mods/nc_player_rotate/init.lua +++ /dev/null @@ -1,106 +0,0 @@ --- LUALOCALS < --------------------------------------------------------- -local math, minetest, nodecore, tostring, type, vector - = math, minetest, nodecore, tostring, type, vector -local math_abs, math_pi - = math.abs, math.pi --- LUALOCALS > --------------------------------------------------------- - -nodecore.amcoremod() - -local modname = minetest.get_current_modname() - -local function vec_to_dir(p) - local ax = math_abs(p.x) - local ay = math_abs(p.y) - local az = math_abs(p.z) - return ay > ax and ay > az - and vector.new(0, p.y < 0 and -1 or 1, 0) - or ax > az - and vector.new(p.x < 0 and -1 or 1, 0, 0) - or vector.new(0, 0, p.z < 0 and -1 or 1) -end - -local rotvector = {} -minetest.register_on_leaveplayer(function(player) - rotvector[player] = nil - end) - -function nodecore.get_player_rotate_vector(player) - player = type(player) == "string" and player or player:get_player_name() - return rotvector[player] -end - -local transform_by_scrkey = { - ["0-1"] = "I", - ["10"] = "R270", - ["01"] = "R180", - ["-10"] = "R90", -} - -nodecore.register_playerstep({ - label = "rotation scan", - action = function(player, data) - local pname = player:get_player_name() - local pt = data.raycast() - - if not (pt and pt.above and pt.under and pt.intersection_point - and pt.intersection_normal) then - rotvector[pname] = nil - return nodecore.hud_set(player, { - label = modname, - ttl = 0, - quick = true - }) - end - - local facectr = vector.multiply(vector.add(pt.above, pt.under), 0.5) - local facerel = vector.subtract(pt.intersection_point, facectr) - - if facerel.x > -1/4 and facerel.x < 1/4 - and facerel.y > -1/4 and facerel.y < 1/4 - and facerel.z > -1/4 and facerel.z < 1/4 - then - rotvector[data.pname] = vector.multiply(pt.intersection_normal, -1) - return nodecore.hud_set(player, { - label = modname, - hud_elem_type = "image_waypoint", - text = "nc_player_rotate_hudarrow_long.png", - scale = {x = 1, y = 1}, - world_pos = facectr, - precision = 0, - quick = true - }) - end - - local rotdir = vec_to_dir(facerel) - rotvector[data.pname] = vector.cross(pt.intersection_normal, rotdir) - - local lookdir = player:get_look_dir() - local camrt = minetest.yaw_to_dir(player:get_look_horizontal() - math_pi / 2) - local camup = vector.cross(camrt, lookdir) - local function screenspace(p) - return vector.new(vector.dot(p, camrt), vector.dot(p, camup), 0) - end - local scrrot = screenspace(rotdir) - local scrnorm = screenspace(pt.intersection_normal) - - local txr = nodecore.tmod("nc_player_rotate_hudarrow_short.png") - if vec_to_dir(vector.cross(scrrot, scrnorm)).z > 0 then - txr = txr:transform("FX") - end - do - local r = vec_to_dir(scrrot) - txr = txr:transform(transform_by_scrkey[r.x .. r.y]) - end - - return nodecore.hud_set(player, { - label = modname, - hud_elem_type = "image_waypoint", - text = tostring(txr), - scale = {x = 1, y = 1}, - world_pos = vector.add(facectr, vector.multiply(rotdir, 0.4)), - precision = 0, - quick = true - }) - end - }) diff --git a/mods/nc_player_rotate/mod.conf b/mods/nc_player_rotate/mod.conf deleted file mode 100644 index 2d3333bb..00000000 --- a/mods/nc_player_rotate/mod.conf +++ /dev/null @@ -1 +0,0 @@ -depends = nc_api_all \ No newline at end of file