1 local S
= minetest
.get_translator("doors")
8 _doors
.registered_doors
= {}
9 _doors
.registered_trapdoors
= {}
11 local function replace_old_owner_information(pos
)
12 local meta
= minetest
.get_meta(pos
)
13 local owner
= meta
:get_string("doors_owner")
14 if owner
and owner
~= "" then
15 meta
:set_string("owner", owner
)
16 meta
:set_string("doors_owner", "")
20 local can_interact_with_node
= function(player
, pos
)
22 if minetest
.check_player_privs(player
, "protection_bypass") then
29 local meta
= minetest
.get_meta(pos
)
30 local owner
= meta
:get_string("owner")
32 if not owner
or owner
== "" or owner
== player
:get_player_name() then
39 -- returns an object to a door object or nil
40 function doors
.get(pos
)
41 local node_name
= minetest
.get_node(pos
).name
42 if _doors
.registered_doors
[node_name
] then
43 -- A normal upright door
46 open
= function(self
, player
)
50 return _doors
.door_toggle(self
.pos
, nil, player
)
52 close
= function(self
, player
)
53 if not self
:state() then
56 return _doors
.door_toggle(self
.pos
, nil, player
)
58 toggle
= function(self
, player
)
59 return _doors
.door_toggle(self
.pos
, nil, player
)
61 state
= function(self
)
62 local state
= minetest
.get_meta(self
.pos
):get_int("state")
66 elseif _doors
.registered_trapdoors
[node_name
] then
70 open
= function(self
, player
)
74 return _doors
.trapdoor_toggle(self
.pos
, nil, player
)
76 close
= function(self
, player
)
77 if not self
:state() then
80 return _doors
.trapdoor_toggle(self
.pos
, nil, player
)
82 toggle
= function(self
, player
)
83 return _doors
.trapdoor_toggle(self
.pos
, nil, player
)
85 state
= function(self
)
86 return minetest
.get_node(self
.pos
).name
:sub(-5) == "_open"
94 -- this hidden node is placed on top of the bottom, and prevents
95 -- nodes from being placed in the top half of the door.
96 minetest
.register_node("doors:hidden", {
97 description
= S("Hidden Door Segment"),
98 -- can't use airlike otherwise falling nodes will turn to entities
99 -- and will be forever stuck until door is removed.
100 drawtype
= "nodebox",
102 paramtype2
= "facedir",
103 sunlight_propagates
= true,
104 -- has to be walkable for falling nodes to stop falling.
108 buildable_to
= false,
111 groups
= {not_in_creative_inventory
= 1},
112 on_blast
= function() end,
113 tiles
= {"blank.png"},
114 use_texture_alpha
= true,
115 -- 1px transparent block inside door hinge near node top.
118 fixed
= {-15/32, 13/32, -15/32, -13/32, 1/2, -13/32},
120 -- collision_box needed otherise selection box would be full node size
123 fixed
= {-15/32, 13/32, -15/32, -13/32, 1/2, -13/32},
127 -- table used to aid door opening/closing
130 {v
= "_a", param2
= 3},
131 {v
= "_a", param2
= 0},
132 {v
= "_a", param2
= 1},
133 {v
= "_a", param2
= 2},
136 {v
= "_b", param2
= 1},
137 {v
= "_b", param2
= 2},
138 {v
= "_b", param2
= 3},
139 {v
= "_b", param2
= 0},
142 {v
= "_b", param2
= 1},
143 {v
= "_b", param2
= 2},
144 {v
= "_b", param2
= 3},
145 {v
= "_b", param2
= 0},
148 {v
= "_a", param2
= 3},
149 {v
= "_a", param2
= 0},
150 {v
= "_a", param2
= 1},
151 {v
= "_a", param2
= 2},
155 function _doors
.door_toggle(pos
, node
, clicker
)
156 local meta
= minetest
.get_meta(pos
)
157 node
= node
or minetest
.get_node(pos
)
158 local def
= minetest
.registered_nodes
[node
.name
]
159 local name
= def
.door
.name
161 local state
= meta
:get_string("state")
163 -- fix up lvm-placed right-hinged doors, default closed
164 if node
.name
:sub(-2) == "_b" then
170 state
= tonumber(state
)
173 replace_old_owner_information(pos
)
175 if clicker
and not can_interact_with_node(clicker
, pos
) then
179 -- until Lua-5.2 we have no bitwise operators :(
180 if state
% 2 == 1 then
186 local dir
= node
.param2
187 if state
% 2 == 0 then
188 minetest
.sound_play(def
.door
.sounds
[1],
189 {pos
= pos
, gain
= 0.3, max_hear_distance
= 10}, true)
191 minetest
.sound_play(def
.door
.sounds
[2],
192 {pos
= pos
, gain
= 0.3, max_hear_distance
= 10}, true)
195 minetest
.swap_node(pos
, {
196 name
= name
.. transform
[state
+ 1][dir
+1].v
,
197 param2
= transform
[state
+ 1][dir
+1].param2
199 meta
:set_int("state", state
)
205 local function on_place_node(place_to
, newnode
,
206 placer
, oldnode
, itemstack
, pointed_thing
)
208 for _
, callback
in ipairs(minetest
.registered_on_placenodes
) do
209 -- Deepcopy pos, node and pointed_thing because callback can modify them
210 local place_to_copy
= {x
= place_to
.x
, y
= place_to
.y
, z
= place_to
.z
}
212 {name
= newnode
.name
, param1
= newnode
.param1
, param2
= newnode
.param2
}
214 {name
= oldnode
.name
, param1
= oldnode
.param1
, param2
= oldnode
.param2
}
215 local pointed_thing_copy
= {
216 type = pointed_thing
.type,
217 above
= vector
.new(pointed_thing
.above
),
218 under
= vector
.new(pointed_thing
.under
),
219 ref
= pointed_thing
.ref
,
221 callback(place_to_copy
, newnode_copy
, placer
,
222 oldnode_copy
, itemstack
, pointed_thing_copy
)
226 local function can_dig_door(pos
, digger
)
227 replace_old_owner_information(pos
)
228 if can_interact_with_node(digger
, pos
) then
231 minetest
.record_protection_violation(pos
, digger
:get_player_name())
236 function doors
.register(name
, def
)
237 if not name
:find(":") then
238 name
= "doors:" .. name
241 minetest
.register_craftitem(":" .. name
, {
242 description
= def
.description
,
243 inventory_image
= def
.inventory_image
,
244 groups
= table.copy(def
.groups
),
246 on_place
= function(itemstack
, placer
, pointed_thing
)
249 if not pointed_thing
.type == "node" then
253 local node
= minetest
.get_node(pointed_thing
.under
)
254 local pdef
= minetest
.registered_nodes
[node
.name
]
255 if pdef
and pdef
.on_rightclick
and
256 not placer
:get_player_control().sneak
then
257 return pdef
.on_rightclick(pointed_thing
.under
,
258 node
, placer
, itemstack
, pointed_thing
)
261 if pdef
and pdef
.buildable_to
then
262 pos
= pointed_thing
.under
264 pos
= pointed_thing
.above
265 node
= minetest
.get_node(pos
)
266 pdef
= minetest
.registered_nodes
[node
.name
]
267 if not pdef
or not pdef
.buildable_to
then
272 local above
= {x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
}
273 local top_node
= minetest
.get_node_or_nil(above
)
274 local topdef
= top_node
and minetest
.registered_nodes
[top_node
.name
]
276 if not topdef
or not topdef
.buildable_to
then
280 local pn
= placer
:get_player_name()
281 if minetest
.is_protected(pos
, pn
) or minetest
.is_protected(above
, pn
) then
285 local dir
= minetest
.dir_to_facedir(placer
:get_look_dir())
288 {x
= -1, y
= 0, z
= 0},
289 {x
= 0, y
= 0, z
= 1},
290 {x
= 1, y
= 0, z
= 0},
291 {x
= 0, y
= 0, z
= -1},
295 x
= pos
.x
+ ref
[dir
+ 1].x
,
296 y
= pos
.y
+ ref
[dir
+ 1].y
,
297 z
= pos
.z
+ ref
[dir
+ 1].z
,
301 if minetest
.get_item_group(minetest
.get_node(aside
).name
, "door") == 1 then
303 minetest
.set_node(pos
, {name
= name
.. "_b", param2
= dir
})
304 minetest
.set_node(above
, {name
= "doors:hidden", param2
= (dir
+ 3) % 4})
306 minetest
.set_node(pos
, {name
= name
.. "_a", param2
= dir
})
307 minetest
.set_node(above
, {name
= "doors:hidden", param2
= dir
})
310 local meta
= minetest
.get_meta(pos
)
311 meta
:set_int("state", state
)
313 if def
.protected
then
314 meta
:set_string("owner", pn
)
315 meta
:set_string("infotext", S("Owned by @1", pn
))
318 if not (creative
and creative
.is_enabled_for
and creative
.is_enabled_for(pn
)) then
319 itemstack
:take_item()
322 minetest
.sound_play(def
.sounds
.place
, {pos
= pos
}, true)
324 on_place_node(pos
, minetest
.get_node(pos
),
325 placer
, node
, itemstack
, pointed_thing
)
330 def
.inventory_image
= nil
333 minetest
.register_craft({
340 if not def
.sounds
then
341 def
.sounds
= hades_sounds
.node_sound_wood_defaults()
344 if not def
.sound_open
then
345 def
.sound_open
= "doors_door_open"
348 if not def
.sound_close
then
349 def
.sound_close
= "doors_door_close"
352 def
.groups
.not_in_creative_inventory
= 1
357 sounds
= { def
.sound_close
, def
.sound_open
},
360 def
.on_rightclick
= function(pos
, node
, clicker
, itemstack
, pointed_thing
)
361 _doors
.door_toggle(pos
, node
, clicker
)
364 def
.after_dig_node
= function(pos
, node
, meta
, digger
)
365 minetest
.remove_node({x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
})
366 minetest
.check_for_falling({x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
})
368 def
.on_rotate
= function(pos
, node
, user
, mode
, new_param2
)
372 if def
.protected
then
373 def
.can_dig
= can_dig_door
374 def
.on_blast
= function() end
375 def
.on_key_use
= function(pos
, player
)
376 local door
= doors
.get(pos
)
379 def
.on_skeleton_key_use
= function(pos
, player
, newsecret
)
380 replace_old_owner_information(pos
)
381 local meta
= minetest
.get_meta(pos
)
382 local owner
= meta
:get_string("owner")
383 local pname
= player
:get_player_name()
385 -- verify placer is owner of lockable door
386 if owner
~= pname
then
387 minetest
.record_protection_violation(pos
, pname
)
388 minetest
.chat_send_player(pname
, S("You do not own this locked door."))
392 local secret
= meta
:get_string("key_lock_secret")
395 meta
:set_string("key_lock_secret", secret
)
398 return secret
, "a locked door", owner
401 def
.on_blast
= function(pos
, intensity
)
402 minetest
.remove_node(pos
)
403 -- hidden node doesn't get blasted away.
404 minetest
.remove_node({x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
})
409 def
.on_destruct
= function(pos
)
410 minetest
.remove_node({x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
})
413 def
.drawtype
= "mesh"
414 def
.paramtype
= "light"
415 def
.paramtype2
= "facedir"
416 def
.sunlight_propagates
= true
418 def
.is_ground_content
= false
419 def
.buildable_to
= false
420 def
.selection_box
= {type = "fixed", fixed
= {-1/2,-1/2,-1/2,1/2,3/2,-6/16}}
421 def
.collision_box
= {type = "fixed", fixed
= {-1/2,-1/2,-1/2,1/2,3/2,-6/16}}
423 def
.mesh
= "door_a.obj"
424 minetest
.register_node(":" .. name
.. "_a", def
)
426 def
.mesh
= "door_b.obj"
427 minetest
.register_node(":" .. name
.. "_b", def
)
429 _doors
.registered_doors
[name
.. "_a"] = true
430 _doors
.registered_doors
[name
.. "_b"] = true
433 -- Capture mods using the old API as best as possible.
434 function doors
.register_door(name
, def
)
435 if def
.only_placer_can_open
then
438 def
.only_placer_can_open
= nil
440 local i
= name
:find(":")
441 local modname
= name
:sub(1, i
- 1)
442 if not def
.tiles
then
443 if def
.protected
then
444 def
.tiles
= {{name
= "doors_door_steel.png", backface_culling
= true}}
446 def
.tiles
= {{name
= "doors_door_wood.png", backface_culling
= true}}
448 minetest
.log("warning", modname
.. " registered door \"" .. name
.. "\" " ..
449 "using deprecated API method \"doors.register_door()\" but " ..
450 "did not provide the \"tiles\" parameter. A fallback tiledef " ..
451 "will be used instead.")
453 def
.use_texture_alpha
= true
455 doors
.register(name
, def
)
460 function _doors
.trapdoor_toggle(pos
, node
, clicker
)
461 node
= node
or minetest
.get_node(pos
)
463 replace_old_owner_information(pos
)
465 if clicker
and not can_interact_with_node(clicker
, pos
) then
469 local def
= minetest
.registered_nodes
[node
.name
]
471 if string.sub(node
.name
, -5) == "_open" then
472 minetest
.sound_play(def
.sound_close
,
473 {pos
= pos
, gain
= 0.3, max_hear_distance
= 10}, true)
474 minetest
.swap_node(pos
, {name
= string.sub(node
.name
, 1,
475 string.len(node
.name
) - 5), param1
= node
.param1
, param2
= node
.param2
})
477 minetest
.sound_play(def
.sound_open
,
478 {pos
= pos
, gain
= 0.3, max_hear_distance
= 10}, true)
479 minetest
.swap_node(pos
, {name
= node
.name
.. "_open",
480 param1
= node
.param1
, param2
= node
.param2
})
484 function doors
.register_trapdoor(name
, def
)
485 if not name
:find(":") then
486 name
= "doors:" .. name
489 local name_closed
= name
490 local name_opened
= name
.."_open"
492 def
.on_rightclick
= function(pos
, node
, clicker
, itemstack
, pointed_thing
)
493 _doors
.trapdoor_toggle(pos
, node
, clicker
)
497 -- Common trapdoor configuration
498 def
.drawtype
= "nodebox"
499 def
.paramtype
= "light"
500 def
.paramtype2
= "facedir"
501 def
.is_ground_content
= false
503 if def
.protected
then
504 def
.can_dig
= can_dig_door
505 def
.after_place_node
= function(pos
, placer
, itemstack
, pointed_thing
)
506 local pn
= placer
:get_player_name()
507 local meta
= minetest
.get_meta(pos
)
508 meta
:set_string("owner", pn
)
509 meta
:set_string("infotext", S("Owned by @1", pn
))
511 return (creative
and creative
.is_enabled_for
and creative
.is_enabled_for(pn
))
514 def
.on_blast
= function() end
515 def
.on_key_use
= function(pos
, player
)
516 local door
= doors
.get(pos
)
519 def
.on_skeleton_key_use
= function(pos
, player
, newsecret
)
520 replace_old_owner_information(pos
)
521 local meta
= minetest
.get_meta(pos
)
522 local owner
= meta
:get_string("owner")
523 local pname
= player
:get_player_name()
525 -- verify placer is owner of lockable door
526 if owner
~= pname
then
527 minetest
.record_protection_violation(pos
, pname
)
528 minetest
.chat_send_player(pname
, S("You do not own this trapdoor."))
532 local secret
= meta
:get_string("key_lock_secret")
535 meta
:set_string("key_lock_secret", secret
)
538 return secret
, "a locked trapdoor", owner
541 def
.on_blast
= function(pos
, intensity
)
542 minetest
.remove_node(pos
)
547 if not def
.sounds
then
548 def
.sounds
= hades_sounds
.node_sound_wood_defaults()
551 if not def
.sound_open
then
552 def
.sound_open
= "doors_door_open"
555 if not def
.sound_close
then
556 def
.sound_close
= "doors_door_close"
559 local def_opened
= table.copy(def
)
560 local def_closed
= table.copy(def
)
562 def_closed
.node_box
= {
564 fixed
= {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
566 def_closed
.selection_box
= {
568 fixed
= {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
570 def_closed
.tiles
= {def
.tile_front
,
571 def
.tile_front
.. '^[transformFY',
572 def
.tile_side
, def
.tile_side
,
573 def
.tile_side
, def
.tile_side
}
575 def_opened
.node_box
= {
577 fixed
= {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
579 def_opened
.selection_box
= {
581 fixed
= {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
583 def_opened
.tiles
= {def
.tile_side
, def
.tile_side
,
584 def
.tile_side
.. '^[transform3',
585 def
.tile_side
.. '^[transform1',
586 def
.tile_front
.. '^[transform46',
587 def
.tile_front
.. '^[transform6'}
589 def_opened
.drop
= name_closed
590 def_opened
.groups
.not_in_creative_inventory
= 1
592 minetest
.register_node(name_opened
, def_opened
)
593 minetest
.register_node(name_closed
, def_closed
)
595 _doors
.registered_trapdoors
[name_opened
] = true
596 _doors
.registered_trapdoors
[name_closed
] = true
602 function doors
.register_fencegate(name
, def
)
604 description
= def
.description
,
606 tiles
= {def
.texture
},
608 paramtype2
= "facedir",
609 sunlight_propagates
= true,
610 is_ground_content
= false,
611 drop
= name
.. "_closed",
612 connect_sides
= {"left", "right"},
615 on_rightclick
= function(pos
, node
, clicker
, itemstack
, pointed_thing
)
616 local node_def
= minetest
.registered_nodes
[node
.name
]
617 minetest
.swap_node(pos
, {name
= node_def
.gate
, param2
= node
.param2
})
618 minetest
.sound_play(node_def
.sound
, {pos
= pos
, gain
= 0.3,
619 max_hear_distance
= 8}, true)
624 fixed
= {-1/2, -1/2, -1/4, 1/2, 1/2, 1/4},
628 if not fence
.sounds
then
629 fence
.sounds
= hades_sounds
.node_sound_wood_defaults()
632 fence
.groups
.fence
= 1
633 fence
.groups
.fence_gate
= 1
635 local fence_closed
= table.copy(fence
)
636 fence_closed
.mesh
= "doors_fencegate_closed.obj"
637 fence_closed
.gate
= name
.. "_open"
638 fence_closed
.sound
= "doors_fencegate_open"
639 fence_closed
.collision_box
= {
641 fixed
= {-1/2, -1/2, -1/4, 1/2, 1/2, 1/4},
644 local fence_open
= table.copy(fence
)
645 fence_open
.mesh
= "doors_fencegate_open.obj"
646 fence_open
.gate
= name
.. "_closed"
647 fence_open
.sound
= "doors_fencegate_close"
648 fence_open
.groups
.not_in_creative_inventory
= 1
649 fence_open
.collision_box
= {
651 fixed
= {{-1/2, -1/2, -1/4, -3/8, 1/2, 1/4},
652 {-1/2, -3/8, -1/2, -3/8, 3/8, 0}},
655 minetest
.register_node(":" .. name
.. "_closed", fence_closed
)
656 minetest
.register_node(":" .. name
.. "_open", fence_open
)
658 minetest
.register_craft({
659 output
= name
.. "_closed",
661 {"group:stick", def
.material
, "group:stick"},
662 {"group:stick", def
.material
, "group:stick"}