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.
This commit is contained in:
Aaron Suen 2024-09-15 14:04:17 -04:00
parent 2f2ab8f5d8
commit f5683cb6c1
14 changed files with 279 additions and 124 deletions

View File

@ -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 = {}

View File

@ -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 {}

152
mods/nc_api_rotate/api.lua Normal file
View File

@ -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

View File

@ -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
})

View File

@ -0,0 +1,9 @@
-- LUALOCALS < ---------------------------------------------------------
local include, nodecore
= include, nodecore
-- LUALOCALS > ---------------------------------------------------------
nodecore.amcoremod()
include("api")
include("hud")

View File

@ -0,0 +1 @@
depends = nc_api, nc_api_active

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -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]

View File

@ -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",

View File

@ -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},

View File

@ -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
})

View File

@ -1 +0,0 @@
depends = nc_api_all