2017-03-22 06:15:17 -07:00
-- Initial speed of a box.
2017-03-07 09:00:22 -08:00
local SPEED = 10
2017-03-22 06:15:17 -07:00
-- Acceleration of a box.
2017-03-08 09:32:23 -08:00
local ACCEL = 0.1
2017-03-22 06:15:17 -07:00
-- Elevator interface/database version.
2017-03-09 06:30:29 -08:00
local VERSION = 8
2017-03-22 06:15:17 -07:00
-- Maximum time a box can go without players nearby.
2017-03-09 07:33:31 -08:00
local PTIMEOUT = 120
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
-- Detect optional mods.
2017-03-21 07:07:06 -07:00
local technic_path = minetest.get_modpath ( " technic " )
local chains_path = minetest.get_modpath ( " chains " )
2017-03-22 06:20:54 -07:00
local homedecor_path = minetest.get_modpath ( " homedecor " )
2017-03-21 07:07:06 -07:00
2017-03-19 10:37:29 -07:00
-- Central "network" table.
2017-03-07 09:00:22 -08:00
local elevator = {
motors = { } ,
}
2017-03-21 07:07:06 -07:00
local elevator_file = minetest.get_worldpath ( ) .. " /elevator "
local function load_elevator ( )
local file = io.open ( elevator_file )
if file then
elevator = minetest.deserialize ( file : read ( " *all " ) ) or { }
file : close ( )
end
end
local function save_elevator ( )
local f = io.open ( elevator_file .. " .tmp " , " w " )
f : write ( minetest.serialize ( elevator ) )
f : close ( )
os.rename ( elevator_file .. " .tmp " , elevator_file )
end
load_elevator ( )
2017-03-19 10:37:29 -07:00
-- Elevator boxes in action.
2017-03-07 09:00:22 -08:00
local boxes = { }
2017-03-19 10:37:29 -07:00
-- Player formspecs.
2017-03-07 09:00:22 -08:00
local formspecs = { }
2017-03-19 10:37:29 -07:00
-- Player near box timeout.
2017-03-09 07:10:26 -08:00
local lastboxes = { }
2017-03-19 10:37:29 -07:00
-- Players riding boxes.
local riding = { }
-- Globalstep timer.
2017-03-07 10:50:23 -08:00
local time = 0
2017-03-19 10:37:29 -07:00
2017-03-21 07:07:06 -07:00
-- Helper function to read unloaded nodes.
local function get_node ( pos )
local node = minetest.get_node_or_nil ( pos )
if node then return node end
local _ , _ = VoxelManip ( ) : read_from_map ( pos , pos )
return minetest.get_node_or_nil ( pos )
end
2017-03-22 06:15:17 -07:00
-- Use homedecor's placeholder if possible.
2017-03-22 06:20:54 -07:00
local placeholder = homedecor_path and " homedecor:expansion_placeholder " or " elevator:placeholder "
2017-03-22 06:15:17 -07:00
if homedecor then
minetest.register_alias ( " elevator:placeholder " , " homedecor:expansion_placeholder " )
else
-- Placeholder node, in the style of homedecor.
minetest.register_node ( placeholder , {
description = " Expansion Placeholder " ,
selection_box = {
type = " fixed " ,
fixed = { 0 , 0 , 0 , 0 , 0 , 0 } ,
} ,
groups = {
not_in_creative_inventory = 1
} ,
drawtype = " airlike " ,
paramtype = " light " ,
sunlight_propagates = true ,
2017-03-21 07:07:06 -07:00
2017-03-22 06:15:17 -07:00
walkable = false ,
buildable_to = false ,
is_ground_content = false ,
2017-03-21 07:07:06 -07:00
2017-03-22 06:15:17 -07:00
on_dig = function ( pos , node , player )
minetest.remove_node ( pos )
minetest.set_node ( pos , { name = placeholder } )
end
} )
end
2017-03-21 07:07:06 -07:00
2017-03-19 10:37:29 -07:00
-- Cause <sender> to ride <motorhash> beginning at <pos> and targetting <target>.
local function create_box ( motorhash , pos , target , sender )
-- First create the box.
local obj = minetest.add_entity ( pos , " elevator:box " )
obj : set_pos ( pos )
-- Attach the player.
sender : set_pos ( pos )
sender : set_attach ( obj , " " , { x = 0 , y = 0 , z = 0 } , { x = 0 , y = 0 , z = 0 } )
sender : set_eye_offset ( { x = 0 , y =- 9 , z = 0 } , { x = 0 , y =- 9 , z = 0 } )
-- Set the box properties.
obj : get_luaentity ( ) . motor = motorhash
obj : get_luaentity ( ) . uid = math.floor ( math.random ( ) * 1000000 )
obj : get_luaentity ( ) . attached = sender : get_player_name ( )
obj : get_luaentity ( ) . start = pos
obj : get_luaentity ( ) . target = target
obj : get_luaentity ( ) . halfway = { x = pos.x , y = ( pos.y + target.y ) / 2 , z = pos.z }
obj : get_luaentity ( ) . vmult = ( target.y < pos.y ) and - 1 or 1
-- Set the speed.
obj : setvelocity ( { x = 0 , y = SPEED * obj : get_luaentity ( ) . vmult , z = 0 } )
obj : setacceleration ( { x = 0 , y = ACCEL * obj : get_luaentity ( ) . vmult , z = 0 } )
-- Set the tables.
boxes [ motorhash ] = obj
riding [ sender : get_player_name ( ) ] = {
motor = motorhash ,
pos = pos ,
target = target ,
box = obj ,
}
return obj
end
2017-03-21 07:07:06 -07:00
-- Try to teleport player away from any closed (on) elevator node.
2017-03-19 10:37:29 -07:00
local function teleport_player_from_elevator ( player )
local function solid ( pos )
if not minetest.registered_nodes [ minetest.get_node ( pos ) . name ] then
return true
end
return minetest.registered_nodes [ minetest.get_node ( pos ) . name ] . walkable
end
local pos = vector.round ( player : getpos ( ) )
local node = minetest.get_node ( pos )
2017-03-22 06:15:17 -07:00
-- elevator_off is like a shaft, so the player would already be falling.
2017-03-19 10:37:29 -07:00
if node.name == " elevator:elevator_on " then
local front = vector.subtract ( pos , minetest.facedir_to_dir ( node.param2 ) )
local front_above = vector.add ( front , { x = 0 , y = 1 , z = 0 } )
local front_below = vector.subtract ( front , { x = 0 , y = 1 , z = 0 } )
2017-03-22 06:15:17 -07:00
-- If the front isn't solid, it's ok to teleport the player.
2017-03-19 10:37:29 -07:00
if not solid ( front ) and not solid ( front_above ) then
player : setpos ( front )
end
end
end
2017-03-07 10:50:23 -08:00
minetest.register_globalstep ( function ( dtime )
2017-03-22 06:15:17 -07:00
-- Don't want to run this too often.
2017-03-07 10:50:23 -08:00
time = time + dtime
if time < 0.5 then
return
end
time = 0
2017-03-19 16:49:47 -07:00
-- Only count riders who are still logged in.
2017-03-19 10:37:29 -07:00
local newriding = { }
2017-03-07 10:50:23 -08:00
for _ , p in ipairs ( minetest.get_connected_players ( ) ) do
local pos = p : getpos ( )
2017-03-22 06:15:17 -07:00
local name = p : get_player_name ( )
newriding [ name ] = riding [ name ]
-- If the player is indeed riding, update their position.
if newriding [ name ] then
newriding [ name ] . pos = pos
2017-03-19 10:37:29 -07:00
end
end
riding = newriding
for name , r in pairs ( riding ) do
2017-03-19 16:49:47 -07:00
-- If the box is no longer loaded or existent, create another.
2017-03-19 10:37:29 -07:00
local ok = r.box and r.box . getpos and r.box : getpos ( ) and r.box : get_luaentity ( ) and r.box : get_luaentity ( ) . attached == name
if not ok then
minetest.log ( " action " , " [elevator] " .. minetest.pos_to_string ( r.pos ) .. " created due to lost rider. " )
minetest.after ( 0 , create_box , r.motor , r.pos , r.target , minetest.get_player_by_name ( name ) )
2017-03-07 10:50:23 -08:00
end
end
2017-03-19 16:49:47 -07:00
-- Ensure boxes are deleted after <PTIMEOUT> seconds if there are no players nearby.
2017-03-09 06:55:38 -08:00
for motor , obj in pairs ( boxes ) do
2017-03-19 10:37:29 -07:00
if type ( obj ) ~= " table " then
return
end
2017-03-09 07:33:31 -08:00
lastboxes [ motor ] = lastboxes [ motor ] and math.min ( lastboxes [ motor ] , PTIMEOUT ) or PTIMEOUT
2017-03-09 07:10:26 -08:00
lastboxes [ motor ] = math.max ( lastboxes [ motor ] - 1 , 0 )
2017-03-09 06:55:38 -08:00
local pos = obj : getpos ( )
2017-03-14 13:56:53 -07:00
if pos then
for _ , object in ipairs ( minetest.get_objects_inside_radius ( pos , 5 ) ) do
if object.is_player and object : is_player ( ) then
lastboxes [ motor ] = PTIMEOUT
break
end
2017-03-09 06:55:38 -08:00
end
2017-03-14 13:56:53 -07:00
if lastboxes [ motor ] < 1 then
minetest.log ( " action " , " [elevator] " .. minetest.pos_to_string ( pos ) .. " broke due to lack of players. " )
boxes [ motor ] = false
end
else
2017-03-19 10:37:29 -07:00
minetest.log ( " action " , " [elevator] " .. minetest.pos_to_string ( pos ) .. " broke due to lack of position during player check. " )
2017-03-09 06:55:38 -08:00
boxes [ motor ] = false
end
end
2017-03-07 10:50:23 -08:00
end )
2017-03-19 16:49:47 -07:00
2017-03-07 10:50:23 -08:00
minetest.register_on_leaveplayer ( function ( player )
2017-03-21 07:07:06 -07:00
-- We don't want players potentially logging into open elevators.
2017-03-19 10:37:29 -07:00
teleport_player_from_elevator ( player )
2017-03-07 10:50:23 -08:00
end )
2017-03-19 16:49:47 -07:00
2017-03-07 09:00:22 -08:00
local function phash ( pos )
return minetest.pos_to_string ( pos )
end
2017-03-22 06:15:17 -07:00
local function punhash ( pos )
return minetest.string_to_pos ( pos )
end
2017-03-19 16:49:47 -07:00
-- Starting from <pos>, locate a motor hash.
2017-03-07 09:00:22 -08:00
local function locate_motor ( pos )
local p = vector.new ( pos )
while true do
2017-03-21 07:07:06 -07:00
local node = get_node ( p )
2017-03-07 09:00:22 -08:00
if node.name == " elevator:elevator_on " or node.name == " elevator:elevator_off " then
p.y = p.y + 2
elseif node.name == " elevator:shaft " then
p.y = p.y + 1
elseif node.name == " elevator:motor " then
return phash ( p )
else
return nil
end
end
end
local function build_motor ( hash )
local need_saving = false
local motor = elevator.motors [ hash ]
2017-03-22 06:15:17 -07:00
-- Just ignore motors that don't exist.
2017-03-07 09:00:22 -08:00
if not motor then
return
end
2017-03-22 06:15:17 -07:00
local p = punhash ( hash )
2017-03-21 07:07:06 -07:00
local node = get_node ( p )
2017-03-22 06:15:17 -07:00
-- And ignore motors that aren't motors.
2017-03-07 09:00:22 -08:00
if node.name ~= " elevator:motor " then
return
end
p.y = p.y - 1
motor.elevators = { }
motor.pnames = { }
2017-03-08 14:30:19 -08:00
motor.labels = { }
2017-03-19 16:49:47 -07:00
-- Run down through the shaft, storing information about elevators.
2017-03-07 09:00:22 -08:00
while true do
2017-03-21 07:07:06 -07:00
local node = get_node ( p )
2017-03-07 09:00:22 -08:00
if node.name == " elevator:shaft " then
p.y = p.y - 1
else
p.y = p.y - 1
2017-03-21 07:07:06 -07:00
local node = get_node ( p )
2017-03-07 09:00:22 -08:00
if node.name == " elevator:elevator_on " or node.name == " elevator:elevator_off " then
table.insert ( motor.elevators , phash ( p ) )
table.insert ( motor.pnames , tostring ( p.y ) )
2017-03-08 14:30:19 -08:00
table.insert ( motor.labels , " " )
2017-03-07 09:00:22 -08:00
p.y = p.y - 1
need_saving = true
else
break
end
end
end
2017-03-19 16:49:47 -07:00
-- Set the elevators fully.
2017-03-07 09:00:22 -08:00
for i , m in ipairs ( motor.elevators ) do
2017-03-22 06:15:17 -07:00
local pos = punhash ( m )
2017-03-07 09:00:22 -08:00
local meta = minetest.get_meta ( pos )
meta : set_int ( " version " , VERSION )
if meta : get_string ( " motor " ) ~= hash then
build_motor ( meta : get_string ( " motor " ) )
end
2017-03-08 14:30:19 -08:00
motor.labels [ i ] = meta : get_string ( " label " )
2017-03-07 09:00:22 -08:00
meta : set_string ( " motor " , hash )
2017-03-09 06:30:29 -08:00
if motor.labels [ i ] ~= meta : get_string ( " infotext " ) then
meta : set_string ( " infotext " , motor.labels [ i ] )
end
2017-03-07 09:00:22 -08:00
end
if need_saving then
save_elevator ( )
end
end
local function unbuild ( pos , add )
local need_saving = false
local p = table.copy ( pos )
p.y = p.y - 1
2017-03-22 06:15:17 -07:00
-- Loop down through the network, set any elevators below this to the off position.
2017-03-07 09:00:22 -08:00
while true do
2017-03-21 07:07:06 -07:00
local node = get_node ( p )
2017-03-07 09:00:22 -08:00
if node.name == " elevator:shaft " then
p.y = p.y - 1
else
p.y = p.y - 1
2017-03-21 07:07:06 -07:00
local node = get_node ( p )
2017-03-07 09:00:22 -08:00
if node.name == " elevator:elevator_on " or node.name == " elevator:elevator_off " then
local meta = minetest.get_meta ( p )
meta : set_string ( " motor " , " " )
p.y = p.y - 1
else
break
end
end
end
2017-03-19 16:49:47 -07:00
-- After a short delay, build the motor and handle box removal.
2017-03-09 06:55:38 -08:00
minetest.after ( 0.01 , function ( p2 , add )
if not p2 or not add then
return
end
2017-03-07 09:00:22 -08:00
p2.y = p2.y + add
2017-03-07 10:50:23 -08:00
local motorhash = locate_motor ( p2 )
build_motor ( motorhash )
2017-03-19 16:49:47 -07:00
-- If there's a box below this point, break it.
2017-03-14 13:56:53 -07:00
if boxes [ motorhash ] and boxes [ motorhash ] : getpos ( ) and p2.y >= boxes [ motorhash ] : getpos ( ) . y then
boxes [ motorhash ] = nil
end
2017-03-19 16:49:47 -07:00
-- If the box does not exist, just clear it.
2017-03-14 13:56:53 -07:00
if boxes [ motorhash ] and not boxes [ motorhash ] : getpos ( ) then
2017-03-07 10:50:23 -08:00
boxes [ motorhash ] = nil
end
2017-03-09 06:55:38 -08:00
end , table.copy ( pos ) , add )
2017-03-07 09:00:22 -08:00
end
minetest.register_node ( " elevator:motor " , {
description = " Elevator Motor " ,
2017-03-21 07:07:06 -07:00
tiles = {
" default_steel_block.png " ,
" default_steel_block.png " ,
" elevator_motor.png " ,
" elevator_motor.png " ,
" elevator_motor.png " ,
" elevator_motor.png " ,
} ,
2017-03-07 09:00:22 -08:00
groups = { cracky = 1 } ,
sounds = default.node_sound_stone_defaults ( ) ,
after_place_node = function ( pos , placer , itemstack )
2017-03-22 06:15:17 -07:00
-- Set up the motor table.
2017-03-07 09:00:22 -08:00
elevator.motors [ phash ( pos ) ] = {
elevators = { } ,
pnames = { } ,
2017-03-08 14:30:19 -08:00
labels = { } ,
2017-03-07 09:00:22 -08:00
}
save_elevator ( )
build_motor ( phash ( pos ) )
end ,
on_destruct = function ( pos )
2017-03-22 06:15:17 -07:00
-- Destroy everything related to this motor.
2017-03-07 10:50:23 -08:00
boxes [ phash ( pos ) ] = nil
2017-03-07 09:00:22 -08:00
elevator.motors [ phash ( pos ) ] = nil
save_elevator ( )
end ,
} )
for _ , mode in ipairs ( { " on " , " off " } ) do
2017-03-22 06:15:17 -07:00
local nodename = " elevator:elevator_ " .. mode
local on = ( mode == " on " )
local box
local cbox
if on then
-- Active elevators have a ceiling and floor.
box = {
{ 0.48 , - 0.5 , - 0.5 , 0.5 , 1.5 , 0.5 } ,
{ - 0.5 , - 0.5 , 0.48 , 0.48 , 1.5 , 0.5 } ,
{ - 0.5 , - 0.5 , - 0.5 , - 0.48 , 1.5 , 0.5 } ,
{ - 0.5 , - 0.5 , - 0.5 , 0.5 , - 0.48 , 0.5 } ,
{ - 0.5 , 1.45 , - 0.5 , 0.5 , 1.5 , 0.5 } ,
}
cbox = table.copy ( box )
-- But you can enter them from the top.
cbox [ 5 ] = nil
else
-- Inactive elevators are almost like shafts.
box = {
{ 0.48 , - 0.5 , - 0.5 , 0.5 , 1.5 , 0.5 } ,
{ - 0.5 , - 0.5 , 0.48 , 0.48 , 1.5 , 0.5 } ,
{ - 0.5 , - 0.5 , - 0.5 , - 0.48 , 1.5 , 0.5 } ,
{ - 0.5 , - 0.5 , - 0.48 , 0.5 , 1.5 , - 0.5 } ,
}
cbox = box
end
minetest.register_node ( nodename , {
description = " Elevator " ,
drawtype = " nodebox " ,
sunlight_propagates = false ,
paramtype = " light " ,
paramtype2 = " facedir " ,
on_rotate = screwdriver.disallow ,
selection_box = {
type = " fixed " ,
fixed = box ,
} ,
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
collision_box = {
type = " fixed " ,
fixed = cbox ,
} ,
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
node_box = {
type = " fixed " ,
fixed = box ,
} ,
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
tiles = on and {
" default_steel_block.png " ,
" default_steel_block.png " ,
" elevator_box.png " ,
" elevator_box.png " ,
" elevator_box.png " ,
" elevator_box.png " ,
} or {
" elevator_box.png " ,
" elevator_box.png " ,
" elevator_box.png " ,
" elevator_box.png " ,
" elevator_box.png " ,
" elevator_box.png " ,
} ,
groups = { cracky = 1 , choppy = 1 , snappy = 1 } ,
drop = " elevator:elevator_off " ,
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
-- Emit a bit of light when active.
light_source = ( on and 4 or nil ) ,
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
after_place_node = function ( pos , placer , itemstack )
local meta = minetest.get_meta ( pos )
meta : set_int ( " version " , VERSION )
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
-- Add a placeholder to avoid nodes being placed in the top.
local p = vector.add ( pos , { x = 0 , y = 1 , z = 0 } )
local p2 = minetest.dir_to_facedir ( placer : get_look_dir ( ) )
minetest.set_node ( p , { name = placeholder , paramtype2 = " facedir " , param2 = p2 } )
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
-- Try to build a motor above.
local motor = locate_motor ( pos )
if motor then
build_motor ( motor )
end
end ,
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
after_dig_node = function ( pos , node , meta , digger )
unbuild ( pos , 2 )
end ,
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
on_place = function ( itemstack , placer , pointed_thing )
local pos = pointed_thing.above
local node = minetest.get_node ( vector.add ( pos , { x = 0 , y = 1 , z = 0 } ) )
if ( node ~= nil and node.name ~= " air " and node.name ~= placeholder ) then
2017-03-07 09:00:22 -08:00
return
end
2017-03-22 06:15:17 -07:00
return minetest.item_place ( itemstack , placer , pointed_thing ) ;
end ,
on_rightclick = function ( pos , node , sender )
local meta = minetest.get_meta ( pos )
formspecs [ sender : get_player_name ( ) ] = { pos }
if on then
if vector.distance ( sender : get_pos ( ) , pos ) > 1 or minetest.get_node ( sender : get_pos ( ) ) . name ~= nodename then
minetest.chat_send_player ( sender : get_player_name ( ) , " You are not inside the booth. " )
return
2017-03-08 14:05:10 -08:00
end
2017-03-22 06:15:17 -07:00
-- Build the formspec from the motor table.
local formspec
local tpnames = { }
local tpnames_l = { }
local motorhash = meta : get_string ( " motor " )
local motor = elevator.motors [ motorhash ]
for ji , jv in ipairs ( motor.pnames ) do
if tonumber ( jv ) ~= pos.y then
table.insert ( tpnames , jv )
table.insert ( tpnames_l , ( motor.labels [ ji ] and motor.labels [ ji ] ~= " " ) and ( jv .. " - " .. motor.labels [ ji ] ) or jv )
end
end
formspecs [ sender : get_player_name ( ) ] = { pos , tpnames }
if # tpnames > 0 then
formspec = " size[4,6] "
.. " label[0,0;Click once to travel.] "
.. " textlist[-0.1,0.5;4,4;target; " .. table.concat ( tpnames_l , " , " ) .. " ] "
.. " field[0.25,5.25;4,0;label;; " .. minetest.formspec_escape ( meta : get_string ( " label " ) ) .. " ] "
.. " button_exit[-0.05,5.5;4,1;setlabel;Set label] "
else
formspec = " size[4,2] "
.. " label[0,0;No targets available.] "
.. " field[0.25,1.25;4,0;label;; " .. minetest.formspec_escape ( meta : get_string ( " label " ) ) .. " ] "
.. " button_exit[-0.05,1.5;4,1;setlabel;Set label] "
end
minetest.show_formspec ( sender : get_player_name ( ) , " elevator:elevator " , formspec )
elseif not elevator.motors [ meta : get_string ( " motor " ) ] then
2017-03-08 14:30:19 -08:00
formspec = " size[4,2] "
2017-03-22 06:15:17 -07:00
.. " label[0,0;This elevator is inactive.] "
.. " field[0.25,1.25;4,0;label;; " .. minetest.formspec_escape ( meta : get_string ( " label " ) ) .. " ] "
.. " button_exit[-0.05,1.5;4,1;setlabel;Set label] "
minetest.show_formspec ( sender : get_player_name ( ) , " elevator:elevator " , formspec )
elseif boxes [ meta : get_string ( " motor " ) ] then
formspec = " size[4,2] "
.. " label[0,0;This elevator is in use.] "
.. " field[0.25,1.25;4,0;label;; " .. minetest.formspec_escape ( meta : get_string ( " label " ) ) .. " ] "
.. " button_exit[-0.05,1.5;4,1;setlabel;Set label] "
minetest.show_formspec ( sender : get_player_name ( ) , " elevator:elevator " , formspec )
2017-03-08 14:05:10 -08:00
end
2017-03-22 06:15:17 -07:00
end ,
2017-03-07 09:00:22 -08:00
2017-03-22 06:15:17 -07:00
on_destruct = function ( pos )
local p = vector.add ( pos , { x = 0 , y = 1 , z = 0 } )
if get_node ( p ) . name == placeholder then
minetest.remove_node ( p )
end
end ,
} )
2017-03-07 09:00:22 -08:00
end
minetest.register_on_player_receive_fields ( function ( sender , formname , fields )
if formname ~= " elevator:elevator " then
return
end
2017-03-08 14:05:10 -08:00
local pos = formspecs [ sender : get_player_name ( ) ] and formspecs [ sender : get_player_name ( ) ] [ 1 ] or nil
2017-03-07 09:00:22 -08:00
if not pos then
2017-03-08 14:30:19 -08:00
return true
2017-03-07 09:00:22 -08:00
end
2017-03-07 10:50:23 -08:00
local meta = minetest.get_meta ( pos )
2017-03-08 14:30:19 -08:00
if fields.setlabel then
if minetest.is_protected ( pos , sender : get_player_name ( ) ) then
return true
end
meta : set_string ( " label " , fields.label )
2017-03-09 06:30:29 -08:00
meta : set_string ( " infotext " , fields.label )
2017-03-22 06:15:17 -07:00
-- Rebuild the elevator shaft so the other elevators can read this label.
2017-03-08 14:30:19 -08:00
local motorhash = meta : get_string ( " motor " )
build_motor ( elevator.motors [ motorhash ] and motorhash or locate_motor ( pos ) )
return true
end
2017-03-22 06:15:17 -07:00
-- Double check if it's ok to go.
if vector.distance ( sender : get_pos ( ) , pos ) > 1 then
2017-03-08 14:30:19 -08:00
return true
2017-03-07 10:50:23 -08:00
end
2017-03-08 14:05:10 -08:00
if fields.target then
2017-03-14 13:56:53 -07:00
local closeformspec = " "
2017-03-21 07:07:06 -07:00
-- HACK: With player information extensions enabled, we can check if closing formspecs are now allowed. This is specifically used on Survival in Ethereal.
2017-03-14 13:56:53 -07:00
local pi = minetest.get_player_information ( sender : get_player_name ( ) )
if ( not ( pi.major == 0 and pi.minor == 4 and pi.patch == 15 ) ) and ( pi.protocol_version or 29 ) < 29 then
closeformspec = " size[4,2] label[0,0;You are now using the elevator. \n Upgrade Minetest to avoid this dialog.] button_exit[0,1;4,1;close;Close] "
end
2017-03-21 07:07:06 -07:00
-- End hacky HACK.
2017-03-14 13:56:53 -07:00
minetest.after ( 0.2 , minetest.show_formspec , sender : get_player_name ( ) , " elevator:elevator " , closeformspec )
2017-03-22 06:15:17 -07:00
-- Ensure we're connected to a motor.
2017-03-07 09:00:22 -08:00
local motorhash = meta : get_string ( " motor " )
local motor = elevator.motors [ motorhash ]
if not motor then
motorhash = locate_motor ( pos )
motor = elevator.motors [ motorhash ]
if motor then
meta : set_string ( " motor " , " " )
build_motor ( motorhash )
minetest.chat_send_player ( sender : get_player_name ( ) , " Recalibrated to a new motor, please try again. " )
return true
end
end
if not motor then
minetest.chat_send_player ( sender : get_player_name ( ) , " This elevator is not attached to a motor. " )
return true
end
2017-03-22 06:15:17 -07:00
-- Locate our target elevator.
2017-03-07 09:00:22 -08:00
local target = nil
2017-03-22 06:15:17 -07:00
local selected_target = formspecs [ sender : get_player_name ( ) ] [ 2 ] [ minetest.explode_textlist_event ( fields.target ) . index ]
2017-03-07 09:00:22 -08:00
for i , v in ipairs ( motor.pnames ) do
2017-03-22 06:15:17 -07:00
if v == selected_target then
target = punhash ( motor.elevators [ i ] )
2017-03-07 09:00:22 -08:00
end
end
2017-03-22 06:15:17 -07:00
-- Found the elevator? Then go!
2017-03-07 09:00:22 -08:00
if target then
2017-03-22 06:15:17 -07:00
-- Final check.
2017-03-07 09:00:22 -08:00
if boxes [ motorhash ] then
minetest.chat_send_player ( sender : get_player_name ( ) , " This elevator is in use. " )
return true
end
2017-03-14 13:56:53 -07:00
local obj = create_box ( motorhash , pos , target , sender )
2017-03-21 07:07:06 -07:00
-- Teleport anyone standing within an on elevator out, or they'd fall through the off elevators.
2017-03-07 10:50:23 -08:00
for _ , p in ipairs ( motor.elevators ) do
2017-03-22 06:15:17 -07:00
local p = punhash ( p )
2017-03-19 16:49:47 -07:00
for _ , object in ipairs ( minetest.get_objects_inside_radius ( p , 0.6 ) ) do
2017-03-19 10:37:29 -07:00
if object.is_player and object : is_player ( ) then
2017-03-07 10:50:23 -08:00
if object : get_player_name ( ) ~= obj : get_luaentity ( ) . attached then
2017-03-19 10:37:29 -07:00
teleport_player_from_elevator ( object )
2017-03-07 10:50:23 -08:00
end
end
end
end
2017-03-07 09:00:22 -08:00
else
minetest.chat_send_player ( sender : get_player_name ( ) , " This target is invalid. " )
return true
end
return true
end
return true
end )
2017-03-22 06:15:17 -07:00
-- Compatability with an older version.
2017-03-07 09:00:22 -08:00
minetest.register_alias ( " elevator:elevator " , " elevator:elevator_off " )
2017-03-22 06:15:17 -07:00
-- Ensure an elevator is up to the latest version.
local function upgrade_elevator ( pos , meta )
2017-03-07 09:00:22 -08:00
if meta : get_int ( " version " ) ~= VERSION then
minetest.log ( " action " , " [elevator] Updating elevator with old version at " .. minetest.pos_to_string ( pos ) )
minetest.after ( 0 , function ( pos ) build_motor ( locate_motor ( pos ) ) end , pos )
meta : set_int ( " version " , VERSION )
meta : set_string ( " formspec " , " " )
2017-03-09 06:30:29 -08:00
meta : set_string ( " infotext " , meta : get_string ( " label " ) )
2017-03-07 09:00:22 -08:00
end
2017-03-22 06:15:17 -07:00
end
-- Convert off to on when applicable.
local offabm = function ( pos , node )
local meta = minetest.get_meta ( pos )
upgrade_elevator ( pos , meta )
2017-03-07 09:00:22 -08:00
if not boxes [ meta : get_string ( " motor " ) ] and elevator.motors [ meta : get_string ( " motor " ) ] then
node.name = " elevator:elevator_on "
minetest.swap_node ( pos , node )
end
end
minetest.register_abm ( {
nodenames = { " elevator:elevator_off " } ,
interval = 1 ,
chance = 1 ,
action = offabm ,
} )
2017-03-19 16:49:47 -07:00
-- Convert on to off when applicable.
2017-03-07 09:00:22 -08:00
minetest.register_abm ( {
nodenames = { " elevator:elevator_on " } ,
interval = 1 ,
chance = 1 ,
action = function ( pos , node )
local meta = minetest.get_meta ( pos )
2017-03-22 06:15:17 -07:00
upgrade_elevator ( pos , meta )
2017-03-07 09:00:22 -08:00
if boxes [ meta : get_string ( " motor " ) ] or not elevator.motors [ meta : get_string ( " motor " ) ] then
node.name = " elevator:elevator_off "
minetest.swap_node ( pos , node )
end
end ,
} )
minetest.register_node ( " elevator:shaft " , {
description = " Elevator Shaft " ,
tiles = { " elevator_shaft.png " } ,
drawtype = " nodebox " ,
paramtype = " light " ,
2017-03-15 16:57:53 -07:00
on_rotate = screwdriver.disallow ,
2017-03-07 09:00:22 -08:00
sunlight_propagates = true ,
groups = { cracky = 2 , oddly_breakable_by_hand = 1 } ,
sounds = default.node_sound_stone_defaults ( ) ,
node_box = {
type = " fixed " ,
fixed = {
{ - 8 / 16 , - 8 / 16 , - 8 / 16 , - 7 / 16 , 8 / 16 , 8 / 16 } ,
{ 7 / 16 , - 8 / 16 , - 8 / 16 , 8 / 16 , 8 / 16 , 8 / 16 } ,
{ - 7 / 16 , - 8 / 16 , - 8 / 16 , 7 / 16 , 8 / 16 , - 7 / 16 } ,
{ - 7 / 16 , - 8 / 16 , 8 / 16 , 7 / 16 , 8 / 16 , 7 / 16 } ,
} ,
} ,
collisionbox = {
type = " fixed " ,
fixed = {
{ - 8 / 16 , - 8 / 16 , - 8 / 16 , - 7 / 16 , 8 / 16 , 8 / 16 } ,
{ 7 / 16 , - 8 / 16 , - 8 / 16 , 8 / 16 , 8 / 16 , 8 / 16 } ,
{ - 7 / 16 , - 8 / 16 , - 8 / 16 , 7 / 16 , 8 / 16 , - 7 / 16 } ,
{ - 7 / 16 , - 8 / 16 , 8 / 16 , 7 / 16 , 8 / 16 , 7 / 16 } ,
} ,
} ,
after_place_node = function ( pos )
2017-03-22 06:15:17 -07:00
-- We might have connected a motor above to an elevator below.
2017-03-07 09:00:22 -08:00
build_motor ( locate_motor ( pos ) )
end ,
on_destruct = function ( pos )
2017-03-22 06:15:17 -07:00
-- Remove boxes and deactivate elevators below us.
2017-03-07 09:00:22 -08:00
unbuild ( pos , 1 )
end ,
} )
local box = {
{ 0.48 , - 0.5 , - 0.5 , 0.5 , 1.5 , 0.5 } ,
{ - 0.5 , - 0.5 , 0.48 , 0.48 , 1.5 , 0.5 } ,
{ - 0.5 , - 0.5 , - 0.5 , - 0.48 , 1.5 , 0.5 } ,
{ - 0.5 , - 0.5 , - 0.48 , 0.5 , 1.5 , - 0.5 } ,
{ - 0.5 , - 0.5 , - 0.5 , 0.5 , - 0.48 , 0.5 } ,
{ - 0.5 , 1.45 , - 0.5 , 0.5 , 1.5 , 0.5 } ,
}
2017-03-21 07:07:06 -07:00
-- Elevator box node. Not intended to be placeable.
2017-03-07 09:00:22 -08:00
minetest.register_node ( " elevator:elevator_box " , {
description = " Elevator " ,
2017-03-20 11:46:54 -07:00
drawtype = " nodebox " ,
2017-03-07 09:00:22 -08:00
paramtype = ' light ' ,
paramtype2 = " facedir " ,
wield_scale = { x = 0.6 , y = 0.6 , z = 0.6 } ,
selection_box = {
type = " fixed " ,
fixed = { - 0.5 , - 0.5 , - 0.5 , 0.5 , 1.5 , 0.5 }
} ,
collision_box = {
type = " fixed " ,
fixed = box ,
} ,
node_box = {
type = " fixed " ,
fixed = box ,
} ,
tiles = {
" default_steel_block.png " ,
" default_steel_block.png " ,
" elevator_box.png " ,
" elevator_box.png " ,
" elevator_box.png " ,
" elevator_box.png " ,
} ,
groups = { not_in_creative_inventory = 1 } ,
light_source = 4 ,
} )
2017-03-22 06:15:17 -07:00
-- Remove the player from self, and teleport them to pos if specified.
2017-03-09 07:33:31 -08:00
local function detach ( self , pos )
local player = minetest.get_player_by_name ( self.attached )
local attached = player : get_attach ( )
if not attached or attached : get_luaentity ( ) . uid ~= self.uid then
return
end
player : set_detach ( )
player : set_eye_offset ( { x = 0 , y = 0 , z = 0 } , { x = 0 , y = 0 , z = 0 } )
if pos then
player : setpos ( pos )
2017-03-14 13:56:53 -07:00
minetest.after ( 0.1 , function ( pl , p )
pl : setpos ( p )
end , player , pos )
2017-03-09 07:33:31 -08:00
end
2017-03-19 10:37:29 -07:00
riding [ self.attached ] = nil
2017-03-09 07:33:31 -08:00
end
2017-03-07 09:00:22 -08:00
local box_entity = {
physical = false ,
collisionbox = { 0 , 0 , 0 , 0 , 0 , 0 } ,
visual = " wielditem " ,
visual_size = { x = 1 , y = 1 } ,
textures = { " elevator:elevator_box " } ,
attached = " " ,
motor = false ,
target = false ,
start = false ,
lastpos = false ,
halfway = false ,
vmult = 0 ,
2017-03-19 10:37:29 -07:00
on_activate = function ( self , staticdata )
2017-03-22 06:15:17 -07:00
-- Don't want the box being destroyed by anything except the elevator system.
2017-03-07 09:00:22 -08:00
self.object : set_armor_groups ( { immortal = 1 } )
end ,
on_step = function ( self , dtime )
2017-03-09 07:10:26 -08:00
local pos = self.object : getpos ( )
2017-03-22 06:15:17 -07:00
-- First, check if this box needs removed.
2017-03-19 16:49:47 -07:00
-- If the motor has a box and it isn't this box.
2017-03-09 07:10:26 -08:00
if boxes [ self.motor ] and boxes [ self.motor ] ~= self.object then
minetest.log ( " action " , " [elevator] " .. minetest.pos_to_string ( pos ) .. " broke due to duplication. " )
self.object : remove ( )
return
end
2017-03-19 16:49:47 -07:00
-- If our attached player can't be found.
2017-03-14 13:56:53 -07:00
if not minetest.get_player_by_name ( self.attached ) then
2017-03-15 16:57:53 -07:00
minetest.log ( " action " , " [elevator] " .. minetest.pos_to_string ( pos ) .. " broke due to lack of attachee logged in. " )
2017-03-07 09:00:22 -08:00
self.object : remove ( )
boxes [ self.motor ] = nil
return
end
2017-03-19 16:49:47 -07:00
-- If our attached player is no longer with us.
2017-03-14 13:56:53 -07:00
if not minetest.get_player_by_name ( self.attached ) : get_attach ( ) or minetest.get_player_by_name ( self.attached ) : get_attach ( ) : get_luaentity ( ) . uid ~= self.uid then
2017-03-15 16:57:53 -07:00
minetest.log ( " action " , " [elevator] " .. minetest.pos_to_string ( pos ) .. " broke due to lack of attachee. " )
self.object : remove ( )
boxes [ self.motor ] = nil
2017-03-14 13:56:53 -07:00
return
end
2017-03-19 16:49:47 -07:00
-- If our motor's box is nil, we should self-destruct.
2017-03-07 10:50:23 -08:00
if not boxes [ self.motor ] then
2017-03-21 07:07:06 -07:00
minetest.log ( " action " , " [elevator] " .. minetest.pos_to_string ( pos ) .. " broke due to nil entry in boxes. " )
2017-03-09 07:33:31 -08:00
detach ( self )
2017-03-07 10:50:23 -08:00
self.object : remove ( )
2017-03-09 06:55:38 -08:00
boxes [ self.motor ] = nil
2017-03-07 10:50:23 -08:00
return
end
2017-03-22 06:15:17 -07:00
2017-03-14 13:56:53 -07:00
minetest.get_player_by_name ( self.attached ) : setpos ( pos )
2017-03-22 06:15:17 -07:00
-- Ensure lastpos is set to something.
2017-03-07 09:00:22 -08:00
self.lastpos = self.lastpos or pos
2017-03-22 06:15:17 -07:00
-- Loop through all travelled nodes.
2017-03-07 09:00:22 -08:00
for y = self.lastpos . y , pos.y , ( ( self.lastpos . y > pos.y ) and - 1 or 1 ) do
local p = vector.round ( { x = pos.x , y = y , z = pos.z } )
local below = vector.add ( p , { x = 0 , y =- 1 , z = 0 } )
2017-03-21 07:07:06 -07:00
local node = get_node ( p )
2017-03-07 09:00:22 -08:00
if node.name == " elevator:shaft " then
2017-03-22 06:15:17 -07:00
-- Nothing, just continue on our way.
2017-03-07 09:00:22 -08:00
elseif node.name == " elevator:elevator_on " or node.name == " elevator:elevator_off " then
2017-03-22 06:15:17 -07:00
-- If this is our target, detach the player here, destroy this box, and update the target elevator without waiting for the abm.
2017-03-07 09:00:22 -08:00
if vector.distance ( p , self.target ) < 1 then
2017-03-09 07:10:26 -08:00
minetest.log ( " action " , " [elevator] " .. minetest.pos_to_string ( p ) .. " broke due to arrival. " )
2017-03-09 07:33:31 -08:00
detach ( self , vector.add ( self.target , { x = 0 , y =- 0.4 , z = 0 } ) )
2017-03-07 09:00:22 -08:00
self.object : remove ( )
boxes [ self.motor ] = nil
offabm ( self.target , node )
return
end
else
2017-03-22 06:15:17 -07:00
-- Check if we're in the top part of an elevator, if so it's fine.
2017-03-21 07:07:06 -07:00
local belownode = get_node ( below )
2017-03-07 09:00:22 -08:00
if belownode.name ~= " elevator:elevator_on " and belownode.name ~= " elevator:elevator_off " then
2017-03-22 06:15:17 -07:00
-- If we aren't, then break the box.
2017-03-09 07:10:26 -08:00
minetest.log ( " action " , " [elevator] " .. minetest.pos_to_string ( p ) .. " broke on " .. node.name )
2017-03-07 09:00:22 -08:00
boxes [ self.motor ] = nil
2017-03-09 07:33:31 -08:00
detach ( self , p )
2017-03-07 09:00:22 -08:00
self.object : remove ( )
return
end
end
end
2017-03-22 06:15:17 -07:00
-- Recreate the box every few seconds. This may not be necessary anymore, but does not seem to harm anything.
2017-03-19 10:37:29 -07:00
self.timer = ( self.timer or 0 ) + dtime
if self.timer > 5 and self.motor and self.target and self.attached and pos then
self.object : remove ( )
create_box ( self.motor , pos , self.target , minetest.get_player_by_name ( self.attached ) )
return
end
2017-03-07 09:00:22 -08:00
self.lastpos = pos
end ,
}
minetest.register_entity ( " elevator:box " , box_entity )
2017-03-21 07:07:06 -07:00
if technic_path and chains_path then
minetest.register_craft ( {
output = " elevator:elevator " ,
recipe = {
{ " technic:cast_iron_ingot " , " chains:chain " , " technic:cast_iron_ingot " } ,
{ " technic:cast_iron_ingot " , " default:mese_crystal " , " technic:cast_iron_ingot " } ,
{ " technic:stainless_steel_ingot " , " default:glass " , " technic:stainless_steel_ingot " } ,
} ,
} )
2017-03-07 09:00:22 -08:00
2017-03-21 07:07:06 -07:00
minetest.register_craft ( {
output = " elevator:shaft " ,
recipe = {
{ " technic:cast_iron_ingot " , " default:glass " } ,
{ " default:glass " , " glooptest:chainlink " } ,
} ,
} )
minetest.register_craft ( {
output = " elevator:motor " ,
recipe = {
{ " default:diamond " , " technic:control_logic_unit " , " default:diamond " } ,
{ " default:steelblock " , " technic:motor " , " default:steelblock " } ,
{ " chains:chain " , " default:diamond " , " chains:chain " }
} ,
} )
else
-- Recipes without technic & chains required.
end