Compare commits
5 Commits
767c2653c2
...
3a43e4ad81
Author | SHA1 | Date |
---|---|---|
coil | 3a43e4ad81 | |
coil | 09974f0c90 | |
Pierre-Yves Rollo | 4e9af449ff | |
Pierre-Yves Rollo | 2b142e9dfe | |
Pierre-Yves Rollo | fd4c8e6c68 |
7
API.md
7
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)**
|
||||
|
@ -46,9 +47,9 @@ This is a helper to register entities used for display.
|
|||
|
||||
### Display_entities fields
|
||||
`on_display_update` is a callback in charge of setting up entity texture. If not set, entity will have no texture and will be displayed as unknown item.\
|
||||
`depth`, `right` and `height`: Entity position regarding to node facedir/wallmounted main axis.\
|
||||
`depth`, `right` and `top`: Entity position regarding to node facedir/wallmounted main axis.\
|
||||
Values for these fields can be any number between -1.5 and 1.5 (default value is 0). Position 0,0,0 is the center of the node.\
|
||||
`depth` goes from front (-0.5) to rear (0.5), `height` goes from bottom (-0.5) to top (0.5) and `right` goes from left (-0.5) to right (0.5).\
|
||||
`depth` goes from front (-0.5) to rear (0.5), `top` goes from bottom (-0.5) to top (0.5) and `right` goes from left (-0.5) to right (0.5).\
|
||||
`yaw`: Entity yaw in radians, regarding to main axis. Default is 0, aligned to node face.
|
||||
|
||||
In order to avoid flickering text, it's better to have text a little behind node surface. A good spacing value is given by `display_api.entity_spacing` variable.
|
||||
|
@ -79,7 +80,7 @@ In order to avoid flickering text, it's better to have text a little behind node
|
|||
depth = 0.3,
|
||||
on_display_update = my_display_update1 },
|
||||
["mymod:entity1"] = {
|
||||
depth = 0.2, height = 0.1,
|
||||
depth = 0.2, top = 0.1,
|
||||
on_display_update = my_display_update2 },
|
||||
},
|
||||
...
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
function deprecated_group(deprecated_group, replacement_group)
|
||||
for name, ndef in pairs(minetest.registered_nodes) do
|
||||
if ndef.groups and ndef.groups[deprecated_group] then
|
||||
minetest.log("warning", string.format('Node %s belongs to deprecated "%s" group which should be replaced with new "%s" group.',
|
||||
minetest.log("warning", string.format(
|
||||
'Node %s belongs to deprecated "%s" group which should be replaced with new "%s" group.',
|
||||
name, deprecated_group, replacement_group))
|
||||
end
|
||||
end
|
||||
|
@ -33,9 +34,10 @@ function deprecated_global_table(deprecated_global_name, replacement_global_name
|
|||
assert(type(replacement_global_name) == 'string', "replacement_global_name should be a string.")
|
||||
assert(deprecated_global_name ~= '', "deprecated_global_name should not be empty.")
|
||||
assert(replacement_global_name ~= '', "replacement_global_name should not be empty.")
|
||||
assert(rawget(_G, deprecated_global_name) == nil, "replacement global already exists.")
|
||||
assert(rawget(_G, deprecated_global_name) == nil, "deprecated global does not exist.")
|
||||
if _G[replacement_global_name] == nil then
|
||||
print('warn_deprecated_functions: Warning, replacement global "'..replacement_global_name..'" does not exists.')
|
||||
minetest.log('warning', string.format(
|
||||
'Replacement global "%s" does not exists.', replacement_global_name))
|
||||
return
|
||||
end
|
||||
local meta = {
|
||||
|
@ -44,15 +46,19 @@ function deprecated_global_table(deprecated_global_name, replacement_global_name
|
|||
__index = function(table, key)
|
||||
local meta = getmetatable(table)
|
||||
local dbg = debug.getinfo(2, "lS")
|
||||
minetest.log("warning", string.format('Warning: Accessing deprecated "%s" table, "%s" should be used instead (%s:%d).',
|
||||
meta.deprecated, meta.replacement, (dbg.short_src or 'unknown'), (dbg.currentline or 0)))
|
||||
minetest.log("warning", string.format(
|
||||
'Accessing deprecated "%s" table, "%s" should be used instead (%s:%d).',
|
||||
meta.deprecated, meta.replacement, (dbg.short_src or 'unknown'),
|
||||
(dbg.currentline or 0)))
|
||||
return _G[meta.replacement][key]
|
||||
end,
|
||||
__newindex = function(table, key, value)
|
||||
local meta = getmetatable(table)
|
||||
local dbg = debug.getinfo(2, "lS")
|
||||
minetest.log("warning", string.format('Warning: Accessing deprecated "%s" table, "%s" should be used instead (%s:%d).',
|
||||
meta.deprecated, meta.replacement, (dbg.short_src or 'unknown'), (dbg.currentline or 0)))
|
||||
minetest.log("warning", string.format(
|
||||
'Accessing deprecated "%s" table, "%s" should be used instead (%s:%d).',
|
||||
meta.deprecated, meta.replacement, (dbg.short_src or 'unknown'),
|
||||
(dbg.currentline or 0)))
|
||||
_G[meta.replacement][key]=value
|
||||
end,
|
||||
}
|
||||
|
|
264
display.lua
264
display.lua
|
@ -22,51 +22,30 @@
|
|||
-- 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},
|
||||
[6]={x=1, y=0, z=0}, [7]={x=1, y=1, z=1},
|
||||
}
|
||||
|
||||
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 +65,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 +78,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 +118,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 +144,60 @@ 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)
|
||||
if not ndef or not ov then
|
||||
return
|
||||
end
|
||||
|
||||
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 +212,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 +243,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 +261,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 +277,23 @@ 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 not ov then
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
display_rotation_restriction(Legacy nodes rotation restriction MT<5.0) bool true
|
Loading…
Reference in New Issue