Detect rotation restriction (#29)

Autodetect rotation restrictions if on MT<5.0
This commit is contained in:
Pierre-Yves Rollo 2019-03-14 09:36:41 +01:00 committed by GitHub
parent 767c2653c2
commit fd4c8e6c68
4 changed files with 138 additions and 129 deletions

1
API.md

@ -14,6 +14,7 @@ This method triggers entities update for the display node at pos. Actual entity
This is a helper to register entities used for display. This is a helper to register entities used for display.
`entity_name`: Name of the entity to register. `entity_name`: Name of the entity to register.
## Provided callback implementations ## Provided callback implementations
### on_place ### on_place
**display\_api.on\_place(itemstack, placer, pointed\_thing)** **display\_api.on\_place(itemstack, placer, pointed\_thing)**

@ -2,9 +2,7 @@
This library's purpose is to ease creation of nodes with one or more displays on sides. For example, signs and clocks. Display can be dynamic and/or different for each node instance. This library's purpose is to ease creation of nodes with one or more displays on sides. For example, signs and clocks. Display can be dynamic and/or different for each node instance.
**Limitations**: This lib uses entities to draw display. This means display has to be vertical (and "upside up") on Minetest before version 5.0. This restriction can be set in settings. **Limitations**: This lib uses entities to draw display. This means display has to be vertical (and "upside up") on Minetest before version 5.0.
**Settings**: `display_rotation_restriction` should be set to false if using Minetest 5.0 and above, true otherwise.
**Dependancies**:default **Dependancies**:default
@ -25,10 +23,12 @@ Following objects are deprecated, shows a warning in log when used:
These objects will be removed in the future. These objects will be removed in the future.
## Change log ## Change log
### 2019-03-14
- __dispay_api__: Display API now detects automatically whenr rotation restrictions have to be applied.
### 2019-03-09 ### 2019-03-09
- __display_api__: Display nodes can be rotated in every directions (if running Minetest 5 or above). - __display_api__: Display nodes can be rotated in every directions (if running Minetest 5 or above).
- __display_api__: New setting to restrict rotations to Minetest 0.4 abilities (**restriction enabled by default**). - __display_api__: New setting to restrict rotations to Minetest 0.4 abilities (restriction enabled by default).
### 2018-12-14 ### 2018-12-14
- __display_api__: New `yaw` attributes, entities can now have different angles with node. - __display_api__: New `yaw` attributes, entities can now have different angles with node.

@ -22,37 +22,16 @@
-- variable as spacing between entity and node -- variable as spacing between entity and node
display_api.entity_spacing = 0.002 display_api.entity_spacing = 0.002
-- Settings
display_api.rotation_restriction =
minetest.settings:get_bool("display_rotation_restriction", true)
if display_api.rotation_restriction then
minetest.log("action", "[display_api] Legacy rotation restriction in effect")
end
-- Maximum entity position relative to the node pos -- Maximum entity position relative to the node pos
local max_entity_pos = 1.5 local max_entity_pos = 1.5
local wallmounted_rotations, facedir_rotations local wallmounted_rotations = {
if display_api.rotation_restriction then
-- Legacy rotations (MT<5.0)
wallmounted_rotations = {
[2]={x=0, y=3, z=0}, [3]={x=0, y=1, z=0},
[4]={x=0, y=0, z=0}, [5]={x=0, y=2, z=0},
}
facedir_rotations = {
[0]={x=0, y=0, z=0}, [1]={x=0, y=3, z=0},
[2]={x=0, y=2, z=0}, [3]={x=0, y=1, z=0},
}
else
-- Full rotations (MT>=5.0)
wallmounted_rotations = {
[0]={x=1, y=0, z=0}, [1]={x=3, y=0, z=0}, [0]={x=1, y=0, z=0}, [1]={x=3, y=0, z=0},
[2]={x=0, y=3, z=0}, [3]={x=0, y=1, z=0}, [2]={x=0, y=3, z=0}, [3]={x=0, y=1, z=0},
[4]={x=0, y=0, z=0}, [5]={x=0, y=2, z=0}, [4]={x=0, y=0, z=0}, [5]={x=0, y=2, z=0},
} }
facedir_rotations = {
local facedir_rotations = {
[ 0]={x=0, y=0, z=0}, [ 1]={x=0, y=3, z=0}, [ 0]={x=0, y=0, z=0}, [ 1]={x=0, y=3, z=0},
[ 2]={x=0, y=2, z=0}, [ 3]={x=0, y=1, z=0}, [ 2]={x=0, y=2, z=0}, [ 3]={x=0, y=1, z=0},
[ 4]={x=3, y=0, z=0}, [ 5]={x=0, y=3, z=3}, [ 4]={x=3, y=0, z=0}, [ 5]={x=0, y=3, z=3},
@ -66,7 +45,6 @@ else
[20]={x=0, y=0, z=2}, [21]={x=0, y=1, z=2}, [20]={x=0, y=0, z=2}, [21]={x=0, y=1, z=2},
[22]={x=0, y=2, z=2}, [23]={x=0, y=3, z=2}, [22]={x=0, y=2, z=2}, [23]={x=0, y=3, z=2},
} }
end
-- Compute other useful values depending on wallmounted and facedir param -- Compute other useful values depending on wallmounted and facedir param
local wallmounted_values = {} local wallmounted_values = {}
@ -86,7 +64,9 @@ local function compute_values(r)
for _ = 1, r.x do d, w, h = rx(d), rx(w), rx(h) end for _ = 1, r.x do d, w, h = rx(d), rx(w), rx(h) end
for _ = 1, r.y do d, w, h = ry(d), ry(w), ry(h) end for _ = 1, r.y do d, w, h = ry(d), ry(w), ry(h) end
return {rotation=r, depth=d, width=w, height=h} return {
rotation=r, depth=d, width=w, height=h,
restricted=(r.x==0 and r.z==0) }
end end
for i, r in pairs(facedir_rotations) do for i, r in pairs(facedir_rotations) do
@ -97,7 +77,38 @@ for i, r in pairs(wallmounted_rotations) do
wallmounted_values[i] = compute_values(r) wallmounted_values[i] = compute_values(r)
end end
local function get_values(node) -- Detect rotation restriction
local rotation_restricted = nil
minetest.register_entity('display_api:dummy_entity', {
collisionbox = { 0, 0, 0, 0, 0, 0 },
visual = "upright_sprite",
textures = {} })
function display_api.is_rotation_restricted()
if rotation_restricted == nil then
local objref = minetest.add_entity(
{x=0, y=0, z=0}, 'display_api:dummy_entity')
if objref then
rotation_restricted = objref.set_rotation == nil
objref:remove()
end
end
return rotation_restricted
end
-- Clip position property to maximum entity position
local function clip_pos_prop(posprop)
if posprop then
return math.max(-max_entity_pos, math.min(max_entity_pos, posprop))
else
return 0
end
end
-- Get values needed for orientation computation of node
local function get_orientation_values(node)
local ndef = minetest.registered_nodes[node.name] local ndef = minetest.registered_nodes[node.name]
if ndef then if ndef then
@ -106,12 +117,17 @@ local function get_values(node)
return wallmounted_values[node.param2 % 8] return wallmounted_values[node.param2 % 8]
elseif paramtype2 == "facedir" or paramtype2 == "colorfacedir" then elseif paramtype2 == "facedir" or paramtype2 == "colorfacedir" then
return facedir_values[node.param2 % 32] return facedir_values[node.param2 % 32]
else
-- No orientation or unknown orientation type
return facedir_values[0]
end end
end end
end end
--- Gets the display entities attached with a node. Removes extra ones -- Gets the display entities attached with a node.
local function get_entities(pos) -- Add missing and remove duplicates
local function get_display_objrefs(pos, create)
local objrefs = {} local objrefs = {}
local ndef = minetest.registered_nodes[minetest.get_node(pos).name] local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
if ndef and ndef.display_entities then if ndef and ndef.display_entities then
@ -127,85 +143,57 @@ local function get_entities(pos)
end end
end end
end end
end if create then
return objrefs -- Add missing
end for name, _ in pairs(ndef.display_entities) do
if not objrefs[name] then
local function clip_pos_prop(posprop) objrefs[name] = minetest.add_entity(pos, name,
if posprop then
return math.max(-max_entity_pos, math.min(max_entity_pos, posprop))
else
return 0
end
end
--- (Create and) place display entities according to the node orientation
local function place_entities(pos)
local node = minetest.get_node(pos)
local ndef = minetest.registered_nodes[node.name]
local v = get_values(node)
local objrefs = get_entities(pos)
if v and ndef and ndef.display_entities then
for entity_name, props in pairs(ndef.display_entities) do
local depth = clip_pos_prop(props.depth)
local right = clip_pos_prop(props.right)
local top = clip_pos_prop(props.top)
if not objrefs[entity_name] then
objrefs[entity_name] = minetest.add_entity(pos, entity_name,
minetest.serialize({ nodepos = pos })) minetest.serialize({ nodepos = pos }))
end end
objrefs[entity_name]:set_pos({
x = pos.x + v.depth.x*depth + v.width.x*right - v.height.x*top,
y = pos.y + v.depth.y*depth + v.width.y*right - v.height.y*top,
z = pos.z + v.depth.z*depth + v.width.z*right - v.height.z*top,
})
if objrefs[entity_name].set_rotation then
objrefs[entity_name]:set_rotation({
x = v.rotation.x*math.pi/2,
y = v.rotation.y*math.pi/2 + (props.yaw or 0),
z = v.rotation.z*math.pi/2,
})
else -- For minetest < 5.0 -- TODO: To be removed in the future
objrefs[entity_name]:set_yaw(v.rotation.y*math.pi/2 + (props.yaw or 0))
end end
end end
end end
return objrefs return objrefs
end end
--- Entity update --- Force entity update : position and texture
function update_entity(entity)
if not entity then
return
end
if not entity.nodepos then
entity.object:remove() -- Remove old/buggy entity
return
end
local node = minetest.get_node(entity.nodepos)
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.display_entities and
ndef.display_entities[entity.name] and
ndef.display_entities[entity.name].on_display_update
then
-- Call on_display_update callback of a node for one of its display entities
ndef.display_entities[entity.name].on_display_update(entity.nodepos,
entity.object)
else
-- Display node has been removed, remove entity also
entity.object:remove()
end
end
--- Force entity update
function display_api.update_entities(pos) function display_api.update_entities(pos)
for _, objref in pairs(place_entities(pos)) do
update_entity(objref:get_luaentity()) local node = minetest.get_node(pos)
local ndef = minetest.registered_nodes[node.name]
local ov = get_orientation_values(node)
for _, objref in pairs(get_display_objrefs(pos, true)) do
local edef = ndef.display_entities[objref:get_luaentity().name]
local depth = clip_pos_prop(edef.depth)
local right = clip_pos_prop(edef.right)
local top = clip_pos_prop(edef.top)
objref:set_pos({
x = pos.x + ov.depth.x*depth + ov.width.x*right - ov.height.x*top,
y = pos.y + ov.depth.y*depth + ov.width.y*right - ov.height.y*top,
z = pos.z + ov.depth.z*depth + ov.width.z*right - ov.height.z*top,
})
if objref.set_rotation then
objref:set_rotation({
x = ov.rotation.x*math.pi/2,
y = ov.rotation.y*math.pi/2 + (edef.yaw or 0),
z = ov.rotation.z*math.pi/2,
})
else
if ov.rotation.x ~=0 or ov.rotation.y ~= 0 then
minetest.log("warning", string.format(
"[display_api] unable to rotate correctly entity for node at %s without set_rotation method.",
minetest.pos_to_string(pos)))
end
objref:set_yaw(ov.rotation.y*math.pi/2 + (edef.yaw or 0))
end
-- Call on_display_update callback of a node for one of its display entities
if edef.on_display_update then
edef.on_display_update(pos, objref)
end
end end
end end
@ -220,7 +208,23 @@ function display_api.on_activate(entity, staticdata)
end end
entity.object:set_armor_groups({immortal=1}) entity.object:set_armor_groups({immortal=1})
end end
update_entity(entity)
if entity.nodepos then
local node = minetest.get_node(entity.nodepos)
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.display_entities then
local edef = ndef.display_entities[entity.name]
if edef then
-- Call on_display_update callback of the entity to build texture
if edef.on_display_update then
edef.on_display_update(entity.nodepos, entity.object)
end
return
end
end
end
-- If we got here, this display entity is buggy and should be removed
entity.object:remove()
end end
end end
@ -235,7 +239,9 @@ function display_api.on_place(itemstack, placer, pointed_thing, override_param2)
z = pointed_thing.under.z - pointed_thing.above.z, z = pointed_thing.under.z - pointed_thing.above.z,
} }
if display_api.rotation_restriction then local rotation_restriction = display_api.is_rotation_restricted()
if rotation_restriction then
-- If item is not placed on a wall, use the player's view direction instead -- If item is not placed on a wall, use the player's view direction instead
if dir.x == 0 and dir.z == 0 then if dir.x == 0 and dir.z == 0 then
dir = placer:get_look_dir() dir = placer:get_look_dir()
@ -251,7 +257,7 @@ function display_api.on_place(itemstack, placer, pointed_thing, override_param2)
elseif ndef.paramtype2 == "facedir" or elseif ndef.paramtype2 == "facedir" or
ndef.paramtype2 == "colorfacedir" then ndef.paramtype2 == "colorfacedir" then
param2 = minetest.dir_to_facedir(dir, not display_api.rotation_restriction) param2 = minetest.dir_to_facedir(dir, not rotation_restriction)
end end
end end
return minetest.item_place(itemstack, placer, pointed_thing, return minetest.item_place(itemstack, placer, pointed_thing,
@ -267,17 +273,20 @@ end
--- On_destruct callback for display_api items. --- On_destruct callback for display_api items.
-- Removes entities. -- Removes entities.
function display_api.on_destruct(pos) function display_api.on_destruct(pos)
for _, objref in pairs(get_entities(pos)) do for _, objref in pairs(get_display_objrefs(pos)) do
objref:remove() objref:remove()
end end
end end
-- On_rotate (screwdriver) callback for display_api items. Prevents invalid rotations and reorients entities. -- On_rotate (screwdriver) callback for display_api items. Prevents invalid
-- rotations and reorients entities.
function display_api.on_rotate(pos, node, user, _, new_param2) function display_api.on_rotate(pos, node, user, _, new_param2)
node.param2 = new_param2 node.param2 = new_param2
if get_values(node) then local ov = get_orientation_values(node)
if ov.restricted or not display_api.is_rotation_restricted() then
minetest.swap_node(pos, node) minetest.swap_node(pos, node)
place_entities(pos) display_api.update_entities(pos)
return true return true
else else
return false return false

@ -1 +0,0 @@
display_rotation_restriction(Legacy nodes rotation restriction MT<5.0) bool true