hades_revisited/mods/hades_doors/api.lua

1304 lines
41 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

local S = minetest.get_translator("hades_doors")
hades_doors = { }
local config = {}
config.autoclose_timeout = 2.5
-- Set this true to enable HIDDEN NODE DEBUG.
-- This makes the hidden nodes visible and diggable.
local HIDDEN_DEBUG = false
hades_doors.LOCKING_MODE_UNDEFINED = 0
hades_doors.LOCKING_MODE_UNLOCKED = 1
hades_doors.LOCKING_MODE_LOCKED = 2
hades_doors.LOCKING_MODE_SHARED = 3
hades_doors.CLOSING_MODE_UNDEFINED = 0
hades_doors.CLOSING_MODE_MANUAL = 1
hades_doors.CLOSING_MODE_AUTOCLOSE = 2
hades_doors.CLOSING_MODE_HOLDOPEN = 3
hades_doors.ADJUST_LOCKING = 1
hades_doors.ADJUST_CLOSING = 2
hades_doors.CHECK_LOCKING = 3
hades_doors.CHECK_CLOSING = 4
local hidden_tiles = { "blank.png" }
local hidden_drawtype = "airlike"
local hidden_pointable = false
local hidden_diggable = false
local hidden_can_dig = function() return false end
local hidden_groups = { not_in_creative_inventory = 1 }
if HIDDEN_DEBUG then
hidden_tiles = { "default_stone.png" }
hidden_drawtype = "nodebox"
hidden_pointable = true
hidden_diggable = true
hidden_can_dig = nil
hidden_groups.dig_immediate = 3
end
local offset_y = function ( pos, y )
return { x = pos.x, y = pos.y + ( y or 1 ), z = pos.z }
end
--[[ Hidden door segments are used to occupy the other nodes of doors to
make sure no nodes overlap with the door.
All hidden door segments are defined in such a way that they effectively
"block" other nodes from occupying the same space.
The "standard" offset door (open and closed) uses 1 hidden door segment
* hades_doors:hidden for the upper segment.
The closed center door uses 1 hidden door segment:
* hades_doors:hidden_center (1×) for the upper door segment
The open center door uses 3 hidden door segments:
* hades_doors:hidden_center (1×) for the upper door segment
* hades_doors:hidden (2×) for the lower and upper and lower
door segments of the door's neighbors
The "neighbors" of the center door are the 2 nodes into which the
center door opens. ]]
minetest.register_node( "hades_doors:hidden", {
-- This node is walkable to stop falling nodes.
-- The nodebox is chosen in such a way
-- that it is inside the overlapping nodebox
-- of the main door node, but it still needs
-- to be rotated.
description = S("Shared Hidden Door Segment"),
inventory_image = "hades_doors_hidden_inv.png",
wield_image = "hades_doors_hidden_inv.png",
drawtype = hidden_drawtype,
tiles = hidden_tiles,
use_texture_alpha = "clip",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
walkable = true,
pointable = hidden_pointable,
diggable = hidden_diggable,
can_dig = hidden_can_dig,
buildable_to = false,
floodable = false,
drop = "",
groups = hidden_groups,
on_blast = function( ) end,
node_box = {
type = "fixed",
fixed = { -15/32, 13/32, -15/32, -13/32, 1/2, -13/32 },
},
} )
minetest.register_node( "hades_doors:hidden_center", {
-- Basically the same as hades_doors:hidden, but
-- for center doors instead.
description = S("Hidden Center Door Segment"),
inventory_image = "hades_doors_hidden_center_inv.png",
wield_image = "hades_doors_hidden_center_inv.png",
drawtype = hidden_drawtype,
tiles = hidden_tiles,
use_texture_alpha = "clip",
drawtype = "airlike",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
walkable = true,
pointable = hidden_pointable,
diggable = hidden_diggable,
can_dig = hidden_can_dig,
buildable_to = false,
floodable = false,
drop = "",
groups = hidden_groups,
on_blast = function( ) end,
can_dig = function( ) end,
node_box = {
type = "fixed",
fixed = { -15/32, 13/32, -1/32, -13/32, 1/2, 1/32 },
},
} )
-- table used to aid door opening/closing
-- tier 1 = hand, tier 2 = face, and tier 3 = is_open
local offset_transform = {
left = {
{ [false] = { suffix = "_a", param2 = 3 }, [true] = { suffix = "_b", param2 = 1 } },
{ [false] = { suffix = "_a", param2 = 0 }, [true] = { suffix = "_b", param2 = 2 } },
{ [false] = { suffix = "_a", param2 = 1 }, [true] = { suffix = "_b", param2 = 3 } },
{ [false] = { suffix = "_a", param2 = 2 }, [true] = { suffix = "_b", param2 = 0 } },
},
right = {
{ [false] = { suffix = "_b", param2 = 1 }, [true] = { suffix = "_a", param2 = 3 } },
{ [false] = { suffix = "_b", param2 = 2 }, [true] = { suffix = "_a", param2 = 0 } },
{ [false] = { suffix = "_b", param2 = 3 }, [true] = { suffix = "_a", param2 = 1 } },
{ [false] = { suffix = "_b", param2 = 0 }, [true] = { suffix = "_a", param2 = 2 } },
},
}
local center_transform = {
left = {
{ [false] = { suffix = "_c", param2 = 2 }, [true] = { suffix = "_d", param2 = 2 } },
{ [false] = { suffix = "_c", param2 = 3 }, [true] = { suffix = "_d", param2 = 3 } },
{ [false] = { suffix = "_c", param2 = 0 }, [true] = { suffix = "_d", param2 = 0 } },
{ [false] = { suffix = "_c", param2 = 1 }, [true] = { suffix = "_d", param2 = 1 } },
},
right = {
{ [false] = { suffix = "_c", param2 = 0 }, [true] = { suffix = "_e", param2 = 0 } },
{ [false] = { suffix = "_c", param2 = 1 }, [true] = { suffix = "_e", param2 = 1 } },
{ [false] = { suffix = "_c", param2 = 2 }, [true] = { suffix = "_e", param2 = 2 } },
{ [false] = { suffix = "_c", param2 = 3 }, [true] = { suffix = "_e", param2 = 3 } },
},
}
local center_neighbor = {
left = {
[0] = { x = 0, y = 0, z = 1 },
[1] = { x = 1, y = 0, z = 0 },
[2] = { x = 0, y = 0, z = -1 },
[3] = { x = -1, y = 0, z = 0 },
},
right = {
[0] = { x = 0, y = 0, z = -1 },
[1] = { x = -1, y = 0, z = 0 },
[2] = { x = 0, y = 0, z = 1 },
[3] = { x = 1, y = 0, z = 0 },
},
}
---------------------------------
-- get_door_properties( )
---------------------------------
local function get_door_properties( state, param2 )
local is_open = state % 2 == 1
local type = state < 4 and "offset" or "center"
local hand = math.floor( state / 2 ) % 2 == 0 and "left" or "right"
local face = param2 + 1
return is_open, type, hand, face
end
---------------------------------
-- get_door_transform( )
---------------------------------
local function get_door_transform( state, param2, toggle_open, switch_type, switch_hand, rotate_face )
local is_open, type, hand, face = get_door_properties( state, param2 )
-- NB: lots of nightmarish state calculations in order to maintain
-- backwards compatibility, so venture forward at your own risk :)
if toggle_open then
state = is_open and state - 1 or state + 1 -- add 1 for open, subtract 1 for close
is_open = not is_open
end
if switch_type then
state = type == "offset" and state + 4 or state - 4 -- add 4 for center, substract 4 for offset
type = type == "offset" and "center" or "offset"
-- need to rotate for seamless switch (also avoid face index overflow)
local face_translation = {
center = { [false] = 1, [true] = 2 },
offset = { [false] = 0, [true] = 1 },
}
face = ( face + face_translation[ type ][ is_open ] ) % 4 + 1
end
if switch_hand then
state = hand == "left" and state + 2 or state - 2 -- add 2 for left, subtract 2 for right
hand = hand == "left" and "right" or "left"
-- need to rotate for seamless switch (also avoid face index overflow)
local face_translation = {
center = { left = 3, right = 1 },
offset = { left = 0, right = 2 },
}
if is_open and type == "center" then -- hack to correct open center doors :P
face = face + 2
end
face = ( face + face_translation[ type ][ hand ] ) % 4 + 1
end
if rotate_face then
face = face % 4 + 1
end
return is_open, state, type == "offset" and
offset_transform[ hand ][ face ][ is_open ] or
center_transform[ hand ][ face ][ is_open ]
end
---------------------------------
-- is_door_protected( )
---------------------------------
local function is_door_protected( pos, ndef, player_name )
local owner = minetest.get_meta( pos ):get_string( "doors_owner" )
if minetest.get_player_privs( player_name ).protection_bypass then return false end
return ndef.protected and player_name ~= owner
end
---------------------------------
-- is_door_locked( )
---------------------------------
local function is_door_locked( pos, ndef, player_name )
local meta = minetest.get_meta( pos )
local locking_mode = meta:get_int( "locking_mode" )
local owner = meta:get_string( "doors_owner" )
if minetest.get_player_privs( player_name ).protection_bypass then return false end
if locking_mode == hades_doors.LOCKING_MODE_UNDEFINED then
if ndef.protected and player_name ~= owner then
return true
end
elseif locking_mode == hades_doors.LOCKING_MODE_LOCKED then
if ndef.protected and player_name ~= owner or not ndef.protected then
return true
end
elseif locking_mode == hades_doors.LOCKING_MODE_SHARED then
if minetest.is_protected( pos, player_name ) then
return true
end
end
return false
end
local is_trapdoor_locked = is_door_locked
---------------------------------
-- toggle_door( )
---------------------------------
local function toggle_door( pos, node, player )
local meta = minetest.get_meta( pos )
local ndef = minetest.registered_nodes[ node.name ]
local closing_mode = meta:get_int( "closing_mode" )
if player and is_door_locked( pos, ndef, player:get_player_name( ) ) then
minetest.sound_play( ndef.sound_locked, { pos = pos, gain = 0.3, max_hear_distance = 10 }, true )
return false
end
local state = meta:get_int( "state" )
local _, statetype, hand = get_door_properties( state, node.param2 )
local is_open, state, transform = get_door_transform( state, node.param2, true, false, false )
local new_name = ndef.base_name .. transform.suffix
local new_param2 = transform.param2
if not is_open and closing_mode == hades_doors.CLOSING_MODE_HOLDOPEN then
minetest.sound_play( ndef.sound_held_open, { pos = pos, gain = 0.8, max_hear_distance = 10 }, true )
return false -- abort since this door does not close
end
if statetype == "center" then
local nhand = hand
if not is_open and hand == "left" then
nhand = "right"
end
local neighbor = center_neighbor[nhand][node.param2]
local neighbor_bottom = vector.add(pos, neighbor)
local neighbor_top = offset_y(neighbor_bottom, 1)
local nb1 = minetest.get_node(neighbor_bottom)
local nb2 = minetest.get_node(neighbor_top)
local nb1d = minetest.registered_nodes[nb1.name]
local nb2d = minetest.registered_nodes[nb2.name]
if is_open then
-- Open center door, place hidden nodes to prevent node overlap
if nb1d and nb2d and nb1d.buildable_to and nb2d.buildable_to then
local np2
if hand == "left" then
np2 = node.param2
else
np2 = (node.param2 + 1) % 4
end
minetest.set_node(neighbor_bottom, {name="hades_doors:hidden", param2=np2})
minetest.set_node(neighbor_top, {name="hades_doors:hidden", param2=np2})
else
-- It's blocked, can't open
minetest.sound_play( ndef.sound_held_open, { pos = pos, gain = 0.8, max_hear_distance = 10 }, true )
return false
end
else
-- Close center door, remove place hidden nodes again
if nb1.name == "hades_doors:hidden" then
minetest.remove_node(neighbor_bottom)
end
if nb2.name == "hades_doors:hidden" then
minetest.remove_node(neighbor_top)
minetest.check_for_falling(neighbor_top)
end
end
end
if is_open and closing_mode == hades_doors.CLOSING_MODE_AUTOCLOSE then
local timer = minetest.get_node_timer( pos )
timer:start( config.autoclose_timeout )
end
if is_open then
-- if opened, play open sound
minetest.sound_play( ndef.sound_open, {
pos = pos, gain = 0.3, max_hear_distance = 10
}, true )
else
-- if closed, play close sound
minetest.sound_play( ndef.sound_close, {
pos = pos, gain = 0.3, max_hear_distance = 10
}, true )
end
minetest.swap_node( pos, { name = new_name, param2 = new_param2 } )
meta:set_int( "state", state )
end
---------------------------------
-- toggle_trapdoor( )
---------------------------------
function toggle_trapdoor( pos, node, player )
local meta = minetest.get_meta( pos )
local ndef = minetest.registered_nodes[ node.name ]
local closing_mode = meta:get_int( "closing_mode" )
if player and is_trapdoor_locked( pos, ndef, player:get_player_name( ) ) then
minetest.sound_play( ndef.sound_locked, { pos = pos, gain = 0.3, max_hear_distance = 10 }, true )
return false
end
if node.name == ndef.base_name then
local new_name = ndef.base_name .. "_open"
if closing_mode == hades_doors.CLOSING_MODE_AUTOCLOSE then
local timer = minetest.get_node_timer( pos )
timer:start( config.autoclose_timeout )
end
minetest.sound_play( ndef.sound_open, { pos = pos, gain = 0.3, max_hear_distance = 10 }, true )
minetest.swap_node( pos, { name = new_name, param1 = node.param1, param2 = node.param2 } )
else
local new_name = ndef.base_name
if closing_mode == hades_doors.CLOSING_MODE_HOLDOPEN then
minetest.sound_play( ndef.sound_held_open, { pos = pos, gain = 0.8, max_hear_distance = 10 }, true )
return false -- abort since this trapdoor does not close
end
minetest.sound_play( ndef.sound_close, { pos = pos, gain = 0.3, max_hear_distance = 10 }, true )
minetest.swap_node( pos, { name = ndef.base_name, param1 = node.param1, param2 = node.param2 } )
end
end
---------------------------------
-- on_timer_door( )
---------------------------------
local on_timer_door = function(pos)
-- Auto-close timeout
local node = minetest.get_node( pos )
local meta = minetest.get_meta( pos )
local state = meta:get_int( "state" )
local is_open, state, transform = get_door_transform( state, node.param2, true, false, false )
if not is_open then
toggle_door(pos, node) --pass nil player, since security doesn't matter
end
end
---------------------------------
-- on_timer_trapdoor( )
---------------------------------
local on_timer_trapdoor = function(pos)
local node = minetest.get_node(pos)
local ndef = minetest.registered_nodes[node.name]
if node.name == ndef.base_name .. "_open" then
toggle_trapdoor(pos, node) --pass nil player, since security doesn't matter
end
end
---------------------------------
-- on_adjust_door( )
---------------------------------
local function on_adjust_door( pos, node, player, mode )
local meta = minetest.get_meta( pos )
local ndef = minetest.registered_nodes[ node.name ]
local locking_mode = meta:get_int( "locking_mode" )
local closing_mode = meta:get_int( "closing_mode" )
local player_name = player:get_player_name( )
if is_door_protected( pos, ndef, player_name ) then return false end
if mode == hades_doors.ADJUST_LOCKING or mode == hades_doors.CHECK_LOCKING then
if ndef.is_lockable and locking_mode > 0 then
local mode_defs = { S("unlocked"), S("locked"), S("shared") }
if mode == hades_doors.ADJUST_LOCKING then
locking_mode = locking_mode % 3 + 1
meta:set_int( "locking_mode", locking_mode )
local sound
if locking_mode == hades_doors.LOCKING_MODE_LOCKED then
sound = ndef.sound_locking
elseif locking_mode == hades_doors.LOCKING_MODE_SHARED then
sound = ndef.sound_sharing
elseif locking_mode == hades_doors.LOCKING_MODE_UNLOCKED then
sound = ndef.sound_unlocking
end
minetest.sound_play( sound, { pos = pos, gain = 0.3, max_hear_distance = 10 }, true )
end
minetest.chat_send_player(player_name, minetest.colorize("#00FFFF", S("Locking mode is set to @1.", mode_defs[locking_mode])))
return true
else
minetest.chat_send_player(player_name, minetest.colorize("#FFFF00", S("This block does not provide locking adjustments.")))
return false
end
elseif mode == hades_doors.ADJUST_CLOSING or mode == hades_doors.CHECK_CLOSING then
if ndef.is_closable and closing_mode > 0 then
local mode_defs = { "manual", "auto-close", "hold-open" }
if mode == hades_doors.ADJUST_CLOSING then
closing_mode = closing_mode % 3 + 1
meta:set_int( "closing_mode", closing_mode )
minetest.sound_play( ndef.sound_closing_mode, { pos = pos, gain = 0.3, max_hear_distance = 10 }, true )
end
minetest.chat_send_player(player_name, minetest.colorize("#00FFFF", S("Closing mode is set to @1.", mode_defs[closing_mode])))
return true
else
minetest.chat_send_player(player_name, minetest.colorize("#FFFF00", S("This block does not provide closing adjustments.")))
return false
end
end
minetest.log("error", "[doors] Invalid mode argument in on_adjust_door! (mode="..tostring(mode)..")")
return false
end
local on_adjust_trapdoor = on_adjust_door
local on_adjust_fence_gate = function( pos, node, player, mode )
local player_name = player:get_player_name()
minetest.chat_send_player(player_name, minetest.colorize("#FFFF00", S("Fence gates do not provide locking or closing adjustments.")))
return false
end
---------------------------------
-- on_rotate_door( )
---------------------------------
local function on_rotate_door( pos, node, player, mode )
local ndef = minetest.registered_nodes[ node.name ]
local meta = minetest.get_meta( pos )
local state = meta:get_int( "state" )
if is_door_protected( pos, ndef, player:get_player_name( ) ) then return false end
if mode == screwdriver.ROTATE_FACE then
-- alternate hand between left <-> right
local is_open, state, transform = get_door_transform( state, node.param2, false, false, true, false )
minetest.swap_node( pos, { name = ndef.base_name .. transform.suffix, param2 = transform.param2 } )
local top = offset_y(pos)
local _, stype, shand = get_door_properties( state, transform.param2 )
local p2 = transform.param2
if stype == "center" then
if is_open and shand == "left" then
p2 = (p2 + 2) % 4
end
elseif (shand == "right" and not is_open) or (shand == "left" and is_open) then
p2 = (p2 + 3) % 4
end
if stype == "center" then
minetest.set_node( top, { name = "hades_doors:hidden_center", param2 = p2 } )
if is_open then
local neighbor = center_neighbor[shand][p2]
if shand == "right" then
p2 = (p2 + 1) % 4
end
minetest.set_node( vector.add( top, neighbor ), { name = "hades_doors:hidden", param2 = p2 } )
end
else
minetest.set_node( top, { name = "hades_doors:hidden", param2 = p2 } )
end
meta:set_int( "state", state )
return true
elseif mode == screwdriver.ROTATE_AXIS and ndef.can_center and ndef.can_offset then
-- alternate type between center <-> offset
local is_open, state, transform = get_door_transform( state, node.param2, false, true, false, false )
local top = offset_y(pos)
local _, stype, shand = get_door_properties( state, transform.param2 )
local p2 = transform.param2
if stype == "center" then
if is_open then
p2 = (p2 + 2) % 4
elseif shand == "left" then
p2 = (p2 + 1) % 4
end
elseif (shand == "right" and not is_open) or (shand == "left" and is_open) then
p2 = (p2 + 3) % 4
end
if stype == "center" then
if is_open then
if shand == "right" then
p2 = (p2 + 2) % 4
end
local neighbor = center_neighbor[shand][p2]
local neighbor_top = vector.add(top, neighbor)
local neighbor_bottom = offset_y(neighbor_top, -1)
local nnt = minetest.get_node(neighbor_top)
local nnb = minetest.get_node(neighbor_bottom)
local nntd = minetest.registered_nodes[nnt.name]
local nnbd = minetest.registered_nodes[nnb.name]
if nntd and nnbd and nntd.buildable_to and nnbd.buildable_to then
minetest.swap_node( pos, { name = ndef.base_name .. transform.suffix, param2 = transform.param2 } )
minetest.set_node( top, { name = "hades_doors:hidden_center", param2 = p2 } )
if shand == "right" then
p2 = (p2 + 1) % 4
end
minetest.set_node( neighbor_bottom, { name = "hades_doors:hidden", param2 = p2 })
minetest.set_node( neighbor_top, { name = "hades_doors:hidden", param2 = p2 })
else
return false
end
else
p2 = transform.param2
minetest.swap_node( pos, { name = ndef.base_name .. transform.suffix, param2 = transform.param2 } )
minetest.set_node( top, { name = "hades_doors:hidden_center", param2 = p2 } )
end
else
if shand == "right" then
p2 = (p2 + 3) % 4
end
minetest.swap_node( pos, { name = ndef.base_name .. transform.suffix, param2 = transform.param2 } )
minetest.set_node( top, { name = "hades_doors:hidden", param2 = p2} )
local neighbor = center_neighbor[shand][p2]
local neighbor_top = vector.add(top, neighbor)
local neighbor_bottom = offset_y(neighbor_top, -1)
local nnt = minetest.get_node(neighbor_top)
local nnb = minetest.get_node(neighbor_bottom)
if nnt.name == "hades_doors:hidden" then
minetest.remove_node(neighbor_top)
minetest.check_for_falling(neighbor_top)
end
if nnb.name == "hades_doors:hidden" then
minetest.remove_node(neighbor_bottom)
end
end
meta:set_int( "state", state )
return true
end
return false
end
local on_rotate_trapdoor
if minetest.get_modpath("screwdriver") then
on_rotate_trapdoor = function(pos, node, user, mode, param2)
-- Flip trapdoor vertically
if mode == screwdriver.ROTATE_AXIS then
local minor = node.param2
if node.param2 >= 20 then
minor = node.param2 - 20
if minor == 3 then
minor = 1
elseif minor == 1 then
minor = 3
end
node.param2 = minor
else
if minor == 3 then
minor = 1
elseif minor == 1 then
minor = 3
end
node.param2 = minor
node.param2 = node.param2 + 20
end
minetest.swap_node(pos, node)
return true
end
end
end
---------------------------------
-- hades_doors.get_door_or_nil( )
---------------------------------
hades_doors.get_door_or_nil = function ( pos )
local node = minetest.get_node( pos )
local meta = minetest.get_meta( pos )
local ndef = minetest.registered_nodes[ node.name ]
local self = { }
if ndef.groups.trapdoor then
return hades_doors.get_trapdoor_or_nil( pos )
elseif not ndef.groups.door then
return nil
end
self.get_properties = function ( )
local state = meta:get_int( "state" )
local is_open, type, hand, face = get_door_properties( state, node.param2 )
return { is_open = is_open, type = type, hand = hand, face = face }
end
self.close = function ( )
local state = meta:get_int( "state" )
local is_open, state, transform = get_door_transform( state, node.param2, true, false, false )
local new_name = ndef.base_name .. transform.suffix
local new_param2 = transform.param2
if not is_open then
minetest.sound_play( ndef.sound_close, { pos = pos, gain = 0.3, max_hear_distance = 10 }, true )
minetest.swap_node( pos, { name = new_name, param2 = new_param2 } )
meta:set_int( "state", state )
end
end
self.open = function ( )
local state = meta:get_int( "state" )
local is_open, state, transform = get_door_transform( state, node.param2, true, false, false )
local new_name = ndef.base_name .. transform.suffix
local new_param2 = transform.param2
if is_open then
minetest.sound_play( ndef.sound_open, { pos = pos, gain = 0.3, max_hear_distance = 10 }, true )
minetest.swap_node( pos, { name = new_name, param2 = new_param2 } )
meta:set_int( "state", state )
end
end
self.state = function ( ) -- for backwards compatibility
return self.get_properties( ).is_open
end
return self
end
hades_doors.get = hades_doors.get_door_or_nil -- for backwards compatibility
---------------------------------
-- hades_doors.get_trapdoor_or_nil( )
---------------------------------
hades_doors.get_trapdoor_or_nil = function ( pos )
local node = minetest.get_node( pos )
local meta = minetest.get_meta( pos )
local ndef = minetest.registered_nodes[ node.name ]
local self = { }
if not ndef.groups.trapdoor then return end
self.get_properties = function ( )
return { is_open = node.name == ndef.base_name .. "_open", face = node.param2 + 1 }
end
self.close = function ( )
if node.name == ndef.base_name .. "_open" then
local new_name = ndef.base_name
minetest.sound_play( ndef.sound_close, { pos = pos, gain = 0.3, max_hear_distance = 10 }, true )
minetest.swap_node( pos, { name = new_name, param2 = node.param2 } )
end
end
self.open = function ( )
if node.name == ndef.base_name then
local new_name = ndef.base_name .. "_open"
minetest.sound_play( ndef.sound_open, { pos = pos, gain = 0.3, max_hear_distance = 10 }, true )
minetest.swap_node( pos, { name = new_name, param2 = node.param2 } )
end
end
return self
end
----------------------------------
-- notify_on_placenode( )
----------------------------------
local function notify_on_placenode( pos, new_node, player, old_node, itemstack, pointed_thing )
for _, on_placenode in ipairs( minetest.registered_on_placenodes) do
-- clone tables since callback can modify them
local clone = {
pos = vector.new( pos ),
new_node = { name = new_node.name, param1 = new_node.param1, param2 = new_node.param2 },
old_node = { name = old_node.name, param1 = old_node.param1, param2 = old_node.param2 },
pointed_thing = {
type = pointed_thing.type,
above = vector.new( pointed_thing.above ),
under = vector.new( pointed_thing.under ),
ref = pointed_thing.ref,
}
}
on_placenode( clone.pos, clone.new_node, player, clone.old_node, itemstack, clone.pointed_thing )
end
end
---------------------------------
-- register_door_craftitem( )
---------------------------------
local function register_door_craftitem( name, def )
minetest.register_craftitem( ":" .. name, {
description = def.description,
inventory_image = def.inventory_image,
groups = { node = 1 },
on_place = function( itemstack, player, pointed_thing )
local pos
if not pointed_thing.type == "node" then
return itemstack
end
local node = minetest.get_node(pointed_thing.under)
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.on_rightclick and
not player:get_player_control().sneak then
return ndef.on_rightclick(pointed_thing.under,
node, player, itemstack, pointed_thing)
end
if ndef and ndef.buildable_to then
pos = pointed_thing.under
else
pos = pointed_thing.above
node = minetest.get_node( pos )
ndef = minetest.registered_nodes[ node.name ]
if not ndef or not ndef.buildable_to then
return itemstack
end
end
local top_pos = offset_y( pos )
local top_node = minetest.get_node_or_nil( top_pos )
local top_ndef = top_node and minetest.registered_nodes[ top_node.name ]
if not top_ndef or not top_ndef.buildable_to then
return itemstack
end
local player_name = player:get_player_name( )
if minetest.is_protected( pos, player_name ) or minetest.is_protected( top_pos, player_name ) then
return itemstack
end
local facedir = minetest.dir_to_facedir( player:get_look_dir( ) )
local facedir_to_pos = {
[0] = { x = -1, y = 0, z = 0 },
[1] = { x = 0, y = 0, z = 1 },
[2] = { x = 1, y = 0, z = 0 },
[3] = { x = 0, y = 0, z = -1 },
}
local look_pos = vector.add( pos, facedir_to_pos[ facedir ] )
local state = 0
-- NB: state stores door hand (left vs right) and door type (offset vs center)
-- and door is_open (true or false), while param2 stores door face (1-4).
if minetest.get_item_group( minetest.get_node( look_pos ).name, "door" ) == 1 then
state = state + 2 -- rotate 180
minetest.set_node( pos, { name = name .. "_b", param2 = facedir } )
minetest.set_node( top_pos, { name = "hades_doors:hidden", param2 = ( facedir + 3 ) % 4 } )
else
minetest.set_node( pos, { name = name .. "_a", param2 = facedir } )
minetest.set_node( top_pos, { name = "hades_doors:hidden", param2 = facedir } )
end
local meta = minetest.get_meta( pos )
meta:set_int( "state", state )
if def.protected then
meta:set_int( "oldtime", os.time( ) )
meta:set_int( "newtime", os.time( ) )
meta:set_string( "doors_owner", player_name )
meta:set_string( "infotext", "Owned by " .. player_name )
end
if def.is_lockable then
meta:set_string( "locking_mode", def.protected and hades_doors.LOCKING_MODE_LOCKED or hades_doors.LOCKING_MODE_UNLOCKED )
end
if def.is_closable then
meta:set_string( "closing_mode", hades_doors.CLOSING_MODE_MANUAL )
end
if not minetest.is_creative_enabled(player:get_player_name()) then
itemstack:take_item( )
end
minetest.sound_play( def.sounds.place, { pos = pos }, true )
notify_on_placenode( pos, minetest.get_node( pos ), player, node, itemstack, pointed_thing )
return itemstack
end
} )
end
---------------------------------
-- hades_doors.register_door( )
---------------------------------
function hades_doors.register_door( name, def )
register_door_craftitem( name, def )
-- define the basic properties
def.base_name = name
def.drawtype = "mesh"
def.paramtype = "light"
def.paramtype2 = "facedir"
def.walkable = true
def.is_ground_content = false
def.buildable_to = false
def.inventory_image = nil
def.drop = name
def.groups.not_in_creative_inventory = 1
def.groups.door = 1
def.groups.rotation_takes_precedence = 1
-- define the crafting recipe
if def.recipe then
minetest.register_craft( { output = name, recipe = def.recipe } )
end
def.recipe = nil
-- define the opening/closing sounds
if not def.sounds then
def.sounds = hades_sounds.node_sound_wood_defaults( )
end
if not def.sound_open then
def.sound_open = "doors_door_open"
end
if not def.sound_close then
def.sound_close = "doors_door_close"
end
if not def.sound_locked then
def.sound_locked = "doors_door_locked"
end
if not def.sound_locking then
def.sound_locking = "doors_door_locking"
end
if not def.sound_unlocking then
def.sound_unlocking = "doors_door_unlocking"
end
if not def.sound_sharing then
def.sound_sharing = "doors_door_sharing"
end
if not def.sound_held_open then
def.sound_held_open = "doors_door_held_open"
end
if not def.sound_closing_mode then
def.sound_closing_mode = "doors_door_closing_mode"
end
-- define the placement types
if def.can_offset == nil or not def.can_center then
def.can_offset = true
end
if def.can_center == nil then
def.can_center = true
end
if def.is_lockable == nil then
def.is_lockable = true
end
if def.is_closable == nil then
def.is_closable = true
end
-- define the essential callbacks
def.on_adjust = on_adjust_door
def.on_rotate = on_rotate_door
def.on_rightclick = function ( pos, node, player, itemstack, pointed_thing )
toggle_door( pos, node, player )
return itemstack
end
def.on_destruct = function( pos )
minetest.remove_node( offset_y( pos ) ) -- hidden node
minetest.check_for_falling( offset_y( pos ) )
end
def.on_timer = on_timer_door
if def.protected then
def.can_dig = function ( pos, player )
local player_name = player:get_player_name( )
if is_door_protected( pos, def, player_name ) then
minetest.record_protection_violation( pos, player_name )
return false
else
return true
end
end
def.on_blast = function( ) end
else
def.on_blast = function( pos, intensity )
minetest.remove_node( pos ) -- door node
minetest.remove_node( offset_y( pos ) ) -- hidden node
return { name }
end
end
-- register offset door nodes
-- Prevent steel door crafting recipe from applying to hinge nodes
-- TODO: Generalize this code
if def.groups then
def.groups.steel_door = nil
end
if def.can_offset then
def.selection_box = { type = "fixed", fixed = { -1/2, -1/2, -8/16, 1/2, 3/2, -6/16 } }
def.collision_box = { type = "fixed", fixed = { -1/2, -1/2, -8/16, 1/2, 3/2, -6/16 } }
def.mesh = "door_a.obj"
minetest.register_node( ":" .. name .. "_a", table.copy(def) )
def.mesh = "door_b.obj"
minetest.register_node( ":" .. name .. "_b", table.copy(def) )
end
-- register center door nodes
if def.can_center then
def.on_destruct = function( pos )
local meta = minetest.get_meta( pos )
local state = meta:get_int( "state" )
local oldnode = minetest.get_node(pos)
local is_open, stype, shand = get_door_properties( state, oldnode.param2 )
if is_open and stype == "center" then
local p2 = oldnode.param2
if shand == "left" then
p2 = (p2 + 2) % 4
end
local neighbor = center_neighbor[shand][p2]
local neighbor_bottom = vector.add(pos, neighbor)
local neighbor_top = offset_y(neighbor_bottom)
-- Remove hidden nodes of open center door
minetest.remove_node( neighbor_bottom )
minetest.remove_node( neighbor_top )
minetest.check_for_falling( neighbor_top )
end
-- Remove hidden node (upper door segment)
minetest.remove_node( offset_y( pos ) )
minetest.check_for_falling( offset_y( pos ) )
end
def.selection_box = { type = "fixed", fixed = { -1/2, -1/2, -1/16, 1/2, 3/2, 1/16 } }
def.collision_box = { type = "fixed", fixed = { -1/2, -1/2, -1/16, 1/2, 3/2, 1/16 } }
def.mesh = "door_c.obj" -- shut
minetest.register_node( ":" .. name .. "_c", table.copy(def) )
def.selection_box = { type = "fixed", fixed = { 6/16, -1/2, -15/16, 8/16, 3/2, 1/16 } }
def.collision_box = { type = "fixed", fixed = { 6/16, -1/2, -15/16, 8/16, 3/2, 1/16 } }
def.mesh = "door_d.obj" -- open left-hand
minetest.register_node( ":" .. name .. "_d", table.copy(def) )
def.selection_box = { type = "fixed", fixed = { -8/16, -1/2, -15/16, -6/16, 3/2, 1/16 } }
def.collision_box = { type = "fixed", fixed = { -8/16, -1/2, -15/16, -6/16, 3/2, 1/16 } }
def.mesh = "door_e.obj" -- open right-hand
minetest.register_node( ":" .. name .. "_e", table.copy(def) )
end
end
hades_doors.register = hades_doors.register_door -- for backward compatibility
---------------------------------
-- hades_doors.register_trapdoor( )
---------------------------------
function hades_doors.register_trapdoor( name, def )
-- define the basic properties
def.base_name = name
def.drawtype = "nodebox"
def.paramtype = "light"
def.paramtype2 = "facedir"
def.is_ground_content = false
def.drop = name
if not def.groups then
def.groups = {}
end
def.groups.trapdoor = 1
def.groups.rotation_takes_precedence = 1
-- define the opening/closing sounds
if not def.sounds then
def.sounds = hades_sounds.node_sound_wood_defaults( )
end
if not def.sound_open then
def.sound_open = "doors_door_open"
end
if not def.sound_close then
def.sound_close = "doors_door_close"
end
if not def.sound_locked then
def.sound_locked = "doors_door_locked"
end
if not def.sound_locking then
def.sound_locking = "doors_door_locking"
end
if not def.sound_unlocking then
def.sound_unlocking = "doors_door_unlocking"
end
if not def.sound_sharing then
def.sound_sharing = "doors_door_sharing"
end
if not def.sound_held_open then
def.sound_held_open = "doors_door_held_open"
end
if not def.sound_closing_mode then
def.sound_closing_mode = "doors_door_closing_mode"
end
-- define the placement types
if def.is_lockable == nil then
def.is_lockable = true
end
if def.is_closable == nil then
def.is_closable = true
end
-- define the essential callbacks
def.on_adjust = on_adjust_trapdoor
def.on_rightclick = function ( pos, node, player, itemstack, pointed_thing )
toggle_trapdoor( pos, node, player )
return itemstack
end
def.on_place = function(itemstack, placer, pointed_thing)
-- Place trapdoor on upper or lower side depending on where it was pointed
local p0 = pointed_thing.under
local p1 = pointed_thing.above
local param2 = 0
-- Get correct trapdoor rotation for the trapdoor "hinge"
local dir = vector.subtract(p0, p1)
if dir.y == 0 then
-- Sideways placement: Trapdoor is "hinged"
-- at this side
param2 = minetest.dir_to_facedir(dir)
else
-- Floor or ceiling placement:
-- Rotation is based on placer view dir
local placer_pos = placer:get_pos()
dir = vector.subtract(p1, placer_pos)
param2 = minetest.dir_to_facedir(dir)
end
-- Decide whether to place up or down (upper or lower half of node
-- side pointed)
local finepos = minetest.pointed_thing_to_face_pos(placer, pointed_thing)
local fpos = finepos.y % 1
if p0.y - 1 == p1.y or (fpos > 0 and fpos < 0.5)
or (fpos < -0.5 and fpos > -0.999999999) then
param2 = param2 + 20
if param2 == 21 then
param2 = 23
elseif param2 == 23 then
param2 = 21
end
end
return minetest.item_place(itemstack, placer, pointed_thing, param2)
end
def.on_rotate = on_rotate_trapdoor
def.on_timer = on_timer_trapdoor
if def.protected then
def.can_dig = function ( pos, player )
local player_name = player:get_player_name( )
if is_door_protected( pos, def, player_name ) then
minetest.record_protection_violation( pos, player_name )
return false
else
return true
end
end
def.after_place_node = function ( pos, player, itemstack, pointed_thing )
local player_name = player:get_player_name( )
local meta = minetest.get_meta( pos )
meta:set_int( "oldtime", os.time( ) )
meta:set_int( "newtime", os.time( ) )
meta:set_string( "doors_owner", player_name )
meta:set_string( "infotext", "Owned by " .. player_name )
if def.is_lockable then
meta:set_string( "locking_mode", def.protected and hades_doors.LOCKING_MODE_LOCKED or hades_doors.LOCKING_MODE_UNLOCKED )
end
if def.is_closable then
meta:set_string( "closing_mode", hades_doors.CLOSING_MODE_MANUAL )
end
return minetest.is_creative_enabled(player_name)
end
def.on_blast = function ( ) end
else
def.after_place_node = function ( pos, player, itemstack, pointed_thing )
local player_name = player:get_player_name( )
local meta = minetest.get_meta( pos )
if def.is_lockable then
meta:set_string( "locking_mode", def.protected and hades_doors.LOCKING_MODE_LOCKED or hades_doors.LOCKING_MODE_UNLOCKED )
end
if def.is_closable then
meta:set_string( "closing_mode", hades_doors.CLOSING_MODE_MANUAL )
end
return minetest.is_creative_enabled(player_name)
end
def.on_blast = function ( pos, intensity )
minetest.remove_node( pos )
return { name }
end
end
-- register trapdoor nodes
def.node_box = { type = "fixed", fixed = { -0.5, -0.5, -0.5, 0.5, -6/16, 0.5 } }
def.selection_box = { type = "fixed", fixed = { -0.5, -0.5, -0.5, 0.5, -6/16, 0.5 } }
def.tiles = { def.tile_front, def.tile_front .. '^[transformFY', def.tile_side, def.tile_side, def.tile_side, def.tile_side }
minetest.register_node( ":" .. name, table.copy(def) ) -- closed
local def_open = table.copy(def)
def_open.groups.not_in_creative_inventory = 1
def_open.node_box = { type = "fixed", fixed = { -0.5, -0.5, 6/16, 0.5, 0.5, 0.5 } }
def_open.selection_box = { type = "fixed", fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5 } }
def_open.tiles = { def.tile_side, def.tile_side, def.tile_side .. '^[transform3', def.tile_side .. '^[transform1', def.tile_front .. '^[transform46', def.tile_front .. '^[transform6' }
minetest.register_node( ":" .. name .. "_open", def_open ) -- opened
end
---------------------------------
-- hades_doors.register_fencegate( )
---------------------------------
function hades_doors.register_fencegate( name, def )
local fence = {
description = def.description,
drawtype = "nodebox",
tiles = {def.texture},
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
is_ground_content = false,
drop = name .. "_closed",
connect_sides = {"left", "right"},
groups = def.groups,
sounds = def.sounds,
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
local node_def = minetest.registered_nodes[node.name]
minetest.swap_node(pos, {name = node_def.gate, param2 = node.param2})
minetest.sound_play(node_def.sound, {pos = pos, gain = 0.3,
max_hear_distance = 8}, true)
return itemstack
end,
on_rotate = "simple",
}
if not fence.sounds then
fence.sounds = hades_sounds.node_sound_wood_defaults()
end
fence.groups.fence_gate = 1
fence.on_adjust = on_adjust_fence_gate
local fence_closed = table.copy(fence)
fence_closed.gate = name .. "_open"
fence_closed.sound = "doors_fencegate_open"
fence_closed.node_box = {
type = "fixed",
fixed = {
{-0.5, -6/16, -1/16, -6/16, 6/16, 1/16}, -- left end
{6/16, -6/16, -1/16, 0.5, 6/16, 1/16}, -- right end
{-2/16, -2/16, -1/16, 2/16, 2/16, 1/16}, -- middle
{-6/16, 2/16, -1/16, 6/16, 5/16, 1/16}, -- up bar
{-6/16, -5/16, -1/16, 6/16, -2/16, 1/16}, -- down bar
}
}
fence_closed.collision_box = {
type = "fixed",
fixed = {-1/2, -1/2, -2/16, 1/2, 1/2, 2/16},
}
fence_closed.selection_box = {
type = "fixed",
fixed = {-1/2, -6/16, -1/16, 1/2, 6/16, 1/16},
}
local fence_open = table.copy(fence)
fence_open.gate = name .. "_closed"
fence_open.sound = "doors_fencegate_close"
fence_open.groups.not_in_creative_inventory = 1
fence_open.node_box = {
type = "fixed",
fixed = {
{-0.5, -6/16, -1/16, -6/16, 6/16, 1/16}, -- left end
{6/16, -6/16, -1/16, 0.5, 6/16, 1/16}, -- right end
{-0.5, 2/16, 1/16, -6/16, 5/16, 6/16}, -- up-left bar x
{-0.5, -5/16, 1/16, -6/16, -2/16, 6/16}, -- down-left bar x
{6/16, 2/16, 1/16, 0.5, 5/16, 0.5}, -- up-right bar x
{6/16, -5/16, 1/16, 0.5, -2/16, 0.5}, -- down-right bar x
{-0.5, -5/16, 6/16, -6/16, 5/16, 0.5}, -- middle left
{6/16, -2/16, 0.5, 0.5, 2/16, 6/16}, -- middle right
},
}
fence_open.selection_box = {
type = "fixed",
fixed = {
{-1/2, -6/16, -1/16, 1/2, 6/16, 1/16},
}
}
fence_open.collision_box = {
type = "fixed",
fixed = {
{-0.5, -6/16, -1/16, -6/16, 6/16, 1/16}, -- left end
{6/16, -6/16, -1/16, 0.5, 6/16, 1/16}, -- right end
}
}
minetest.register_node(":" .. name .. "_closed", fence_closed)
minetest.register_node(":" .. name .. "_open", fence_open)
minetest.register_craft({
output = name .. "_closed",
recipe = {
{"hades_core:stick", def.material, "hades_core:stick"},
{"hades_core:stick", def.material, "hades_core:stick"}
}
})
end