From fd4c8e6c68d0706e8a38626dbec7bc945eb0a5bb Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Thu, 14 Mar 2019 09:36:41 +0100 Subject: [PATCH] Detect rotation restriction (#29) Autodetect rotation restrictions if on MT<5.0 --- API.md | 1 + README.md | 8 +- display.lua | 257 ++++++++++++++++++++++++----------------------- settingtypes.txt | 1 - 4 files changed, 138 insertions(+), 129 deletions(-) delete mode 100644 settingtypes.txt diff --git a/API.md b/API.md index e69ae60..acfd867 100644 --- a/API.md +++ b/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. `entity_name`: Name of the entity to register. + ## Provided callback implementations ### on_place **display\_api.on\_place(itemstack, placer, pointed\_thing)** diff --git a/README.md b/README.md index a0f0f6e..73acdf9 100644 --- a/README.md +++ b/README.md @@ -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. -**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. - -**Settings**: `display_rotation_restriction` should be set to false if using Minetest 5.0 and above, true otherwise. +**Limitations**: This lib uses entities to draw display. This means display has to be vertical (and "upside up") on Minetest before version 5.0. **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. ## Change log +### 2019-03-14 +- __dispay_api__: Display API now detects automatically whenr rotation restrictions have to be applied. ### 2019-03-09 - __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 - __display_api__: New `yaw` attributes, entities can now have different angles with node. diff --git a/display.lua b/display.lua index 3ed1f02..2fed40f 100644 --- a/display.lua +++ b/display.lua @@ -22,51 +22,29 @@ -- variable as spacing between entity and node 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 local max_entity_pos = 1.5 -local wallmounted_rotations, facedir_rotations +local wallmounted_rotations = { + [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}, + [4]={x=0, y=0, z=0}, [5]={x=0, y=2, z=0}, +} -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}, - [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}, - [ 4]={x=3, y=0, z=0}, [ 5]={x=0, y=3, z=3}, - [ 6]={x=1, y=0, z=2}, [ 7]={x=0, y=1, z=1}, - [ 8]={x=1, y=0, z=0}, [ 9]={x=0, y=3, z=1}, - [10]={x=3, y=0, z=2}, [11]={x=0, y=1, z=3}, - [12]={x=0, y=0, z=1}, [13]={x=3, y=0, z=1}, - [14]={x=2, y=0, z=1}, [15]={x=1, y=0, z=1}, - [16]={x=0, y=0, z=3}, [17]={x=1, y=0, z=3}, - [18]={x=2, y=0, z=3}, [19]={x=3, y=0, z=3}, - [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}, - } -end +local 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}, + [ 4]={x=3, y=0, z=0}, [ 5]={x=0, y=3, z=3}, + [ 6]={x=1, y=0, z=2}, [ 7]={x=0, y=1, z=1}, + [ 8]={x=1, y=0, z=0}, [ 9]={x=0, y=3, z=1}, + [10]={x=3, y=0, z=2}, [11]={x=0, y=1, z=3}, + [12]={x=0, y=0, z=1}, [13]={x=3, y=0, z=1}, + [14]={x=2, y=0, z=1}, [15]={x=1, y=0, z=1}, + [16]={x=0, y=0, z=3}, [17]={x=1, y=0, z=3}, + [18]={x=2, y=0, z=3}, [19]={x=3, y=0, z=3}, + [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}, +} -- Compute other useful values depending on wallmounted and facedir param 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.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 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) 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] if ndef then @@ -106,12 +117,17 @@ local function get_values(node) return wallmounted_values[node.param2 % 8] elseif paramtype2 == "facedir" or paramtype2 == "colorfacedir" then return facedir_values[node.param2 % 32] + else + -- No orientation or unknown orientation type + return facedir_values[0] end end end ---- Gets the display entities attached with a node. Removes extra ones -local function get_entities(pos) +-- Gets the display entities attached with a node. +-- Add missing and remove duplicates + +local function get_display_objrefs(pos, create) local objrefs = {} local ndef = minetest.registered_nodes[minetest.get_node(pos).name] if ndef and ndef.display_entities then @@ -127,85 +143,57 @@ local function get_entities(pos) end end end - end - return objrefs -end - -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 - ---- (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 })) - 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)) + if create then + -- Add missing + for name, _ in pairs(ndef.display_entities) do + if not objrefs[name] then + objrefs[name] = minetest.add_entity(pos, name, + minetest.serialize({ nodepos = pos })) + end end end end return objrefs end ---- Entity update -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 +--- Force entity update : position and texture 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 @@ -220,7 +208,23 @@ function display_api.on_activate(entity, staticdata) end entity.object:set_armor_groups({immortal=1}) 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 @@ -235,7 +239,9 @@ function display_api.on_place(itemstack, placer, pointed_thing, override_param2) 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 dir.x == 0 and dir.z == 0 then 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 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 return minetest.item_place(itemstack, placer, pointed_thing, @@ -267,17 +273,20 @@ end --- On_destruct callback for display_api items. -- Removes entities. 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() 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) 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) - place_entities(pos) + display_api.update_entities(pos) return true else return false diff --git a/settingtypes.txt b/settingtypes.txt deleted file mode 100644 index 58a0108..0000000 --- a/settingtypes.txt +++ /dev/null @@ -1 +0,0 @@ -display_rotation_restriction(Legacy nodes rotation restriction MT<5.0) bool true