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:
parent
2f2ab8f5d8
commit
f5683cb6c1
@ -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 = {}
|
||||
|
@ -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
152
mods/nc_api_rotate/api.lua
Normal 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
|
76
mods/nc_api_rotate/hud.lua
Normal file
76
mods/nc_api_rotate/hud.lua
Normal 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
|
||||
})
|
9
mods/nc_api_rotate/init.lua
Normal file
9
mods/nc_api_rotate/init.lua
Normal file
@ -0,0 +1,9 @@
|
||||
-- LUALOCALS < ---------------------------------------------------------
|
||||
local include, nodecore
|
||||
= include, nodecore
|
||||
-- LUALOCALS > ---------------------------------------------------------
|
||||
|
||||
nodecore.amcoremod()
|
||||
|
||||
include("api")
|
||||
include("hud")
|
1
mods/nc_api_rotate/mod.conf
Normal file
1
mods/nc_api_rotate/mod.conf
Normal file
@ -0,0 +1 @@
|
||||
depends = nc_api, nc_api_active
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
@ -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]
|
||||
|
@ -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",
|
||||
|
@ -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},
|
||||
|
@ -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
|
||||
})
|
@ -1 +0,0 @@
|
||||
depends = nc_api_all
|
Loading…
x
Reference in New Issue
Block a user