Minetest-Hyperloop/elevator.lua

670 lines
19 KiB
Lua

--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
]]--
-- for lazy programmers
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
local P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for intllib.
local S = hyperloop.S
local NS = hyperloop.NS
-- To store elevator floors and formspecs
local Cache = {}
local PlayerNameTags = {}
local kPLAYER_OVER_GROUND = 0.5
-------------------------------------------------------------------------------
-- Elevator Shaft
-------------------------------------------------------------------------------
-- Down, Up
local dirs_to_check = {5,6} -- vertical only
if hyperloop.free_tube_placement_enabled then
dirs_to_check = {1,2,3,4,5,6} -- all directions
end
local Shaft = tubelib2.Tube:new({
dirs_to_check = dirs_to_check,
max_tube_length = 1000,
show_infotext = true,
primary_node_names = {"hyperloop:shaft", "hyperloop:shaft2", "hyperloop:shaftA", "hyperloop:shaftA2"},
secondary_node_names = {"hyperloop:elevator_bottom", "hyperloop:elevator_top"},
after_place_tube = function(pos, param2, tube_type, num_tubes)
if tube_type == "S" then
if num_tubes == 2 then
minetest.swap_node(pos, {name = "hyperloop:shaft2", param2 = param2})
else
minetest.swap_node(pos, {name = "hyperloop:shaft", param2 = param2})
end
else
if num_tubes == 2 then
minetest.swap_node(pos, {name = "hyperloop:shaftA2", param2 = param2})
else
minetest.swap_node(pos, {name = "hyperloop:shaftA", param2 = param2})
end
end
end,
})
hyperloop.Shaft = Shaft
local Elevators = hyperloop.Elevators
Shaft:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir)
if out_dir == 6 then -- to the top?
-- switch to elevator_bottom node
pos = Shaft:get_pos(pos, 5)
elseif peer_pos then
local _,node = Shaft:get_node(peer_pos)
if node.name == "hyperloop:elevator_top" then
peer_pos = Shaft:get_pos(peer_pos, 5)
end
end
Elevators:update_connections(pos, out_dir, peer_pos)
end)
minetest.register_node("hyperloop:shaft", {
description = S("Hyperloop Elevator Shaft"),
inventory_image = 'hyperloop_shaft_inv.png',
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_closed.png^[transformR90]",
"hyperloop_tube_closed.png^[transformR90]",
"hyperloop_tube_closed.png",
"hyperloop_tube_closed.png",
'hyperloop_tube.png',
'hyperloop_tube.png',
},
drawtype = "nodebox",
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},
{-8/16, 7/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
if not Shaft:after_place_tube(pos, placer, pointed_thing) then
minetest.remove_node(pos)
return true
end
return false
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Shaft:after_dig_tube(pos, oldnode, oldmetadata)
end,
climbable = true,
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
paramtype = "light",
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
groups = {cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("hyperloop:shaftA", {
description = S("Hyperloop Elevator Shaft"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_closed.png^[transformR90]",
'hyperloop_tube.png',
"hyperloop_tube_closed.png",
"hyperloop_tube_closed.png",
"hyperloop_tube_closed.png",
'hyperloop_tube.png',
},
drawtype = "nodebox",
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},
{-8/16, 7/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, 8/16, -7/16, -7/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
if not Shaft:after_place_tube(pos, placer, pointed_thing) then
minetest.remove_node(pos)
return true
end
return false
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Shaft:after_dig_tube(pos, oldnode, oldmetadata)
end,
climbable = true,
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
paramtype = "light",
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
groups = {cracky = 1, not_in_creative_inventory=1},
drop = "hyperloop:shaft",
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("hyperloop:shaft2", {
description = S("Hyperloop Elevator Shaft"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR270]",
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR90]",
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR180]",
"hyperloop_tube_locked.png^hyperloop_elogo.png",
'hyperloop_tube.png',
'hyperloop_tube.png',
},
drawtype = "nodebox",
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},
{-8/16, 7/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Shaft:after_dig_tube(pos, oldnode, oldmetadata)
end,
climbable = true,
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
paramtype = "light",
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
diggable = false,
groups = {cracky = 1, not_in_creative_inventory=1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("hyperloop:shaftA2", {
description = S("Hyperloop Elevator Shaft"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR270]",
'hyperloop_tube.png',
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR180]",
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR180]",
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR90]",
'hyperloop_tube.png',
},
drawtype = "nodebox",
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},
{-8/16, 7/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, 8/16, -7/16, -7/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Shaft:after_dig_tube(pos, oldnode, oldmetadata)
end,
climbable = true,
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
paramtype = "light",
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
diggable = false,
groups = {cracky = 1, not_in_creative_inventory=1},
sounds = default.node_sound_metal_defaults(),
})
-------------------------------------------------------------------------------
-- Elevator Car
-------------------------------------------------------------------------------
-- Form spec for the floor list
local function formspec(pos, lFloors)
local tRes = {"size[6,10]label[0.5,0; "..S("Select your destination").."]"}
tRes[2] = "label[0.5,0.6;"..S("Destination").."]label[2,0.6;"..S("Floor").."]"
if #lFloors == 0 then
tRes[#tRes+1] = "button_exit[1,3;3,1;button;Update]"
elseif #lFloors < 10 then
for idx,floor in ipairs(lFloors) do
local ypos = 0.5 + idx*0.8
local ypos2 = ypos - 0.2
tRes[#tRes+1] = "button_exit[0.5,"..ypos2..";1,1;button;"..(#lFloors-idx).."]"
if vector.equals(floor.pos, pos) then
tRes[#tRes+1] = "label[2,"..ypos..";"..S("(current position)").."]"
else
tRes[#tRes+1] = "label[2,"..ypos..";"..(floor.name or "<unknown>").."]"
end
end
else
tRes[3] = "scrollbaroptions[smallstep=100;largestep=200]"
tRes[4] = "scrollbar[5.3,1.5;0.4,8.2;vertical;floors;0]"
tRes[5] = "scroll_container[0.5,2;5,9.5;floors;vertical;0.02]"
for idx,floor in ipairs(lFloors) do
local ypos = idx*0.8 - 0.5
local ypos2 = ypos - 0.2
tRes[#tRes+1] = "button_exit[0,"..ypos2..";1,1;button;"..(#lFloors-idx).."]"
if vector.equals(floor.pos, pos) then
tRes[#tRes+1] = "label[1.5,"..ypos..";"..S("(current position)").."]"
else
tRes[#tRes+1] = "label[1.5,"..ypos..";"..(floor.name or "<unknown>").."]"
end
end
tRes[#tRes+1] = "scroll_container_end[]"
end
return table.concat(tRes)
end
local function update_formspec(pos)
local meta = M(pos)
local counter = meta:get_int("change_counter") or 0
local changed, newcounter = Elevators:changed(counter)
local sKey = SP(pos)
if changed or not Cache[sKey] then
local lFloors = Elevators:station_list(pos, pos, "level")
Cache[sKey] = {}
Cache[sKey].lFloors = lFloors
Cache[sKey].formspec = formspec(pos, lFloors)
meta:set_int("change_counter", newcounter)
end
M(pos):set_string("formspec", Cache[sKey].formspec)
end
-- Open/close/darken the elevator door
-- floor_pos: position of elevator floor
-- cmnd: "close", "open", or "darken"
local function door_command(floor_pos, facedir, cmnd, sound)
-- one step up
local door_pos1 = hyperloop.new_pos(floor_pos, facedir, "1B", 0)
local door_pos2 = hyperloop.new_pos(floor_pos, facedir, "1B", 1)
local node1 = minetest.get_node(door_pos1)
local node2 = minetest.get_node(door_pos2)
if sound then
minetest.sound_play("ele_door", {
pos = floor_pos,
gain = 0.8,
max_hear_distance = 10,
})
end
if cmnd == "open" then
node1.name = "air"
minetest.swap_node(door_pos1, node1)
node2.name = "air"
minetest.swap_node(door_pos2, node2)
elseif cmnd == "close" then
M(door_pos1):set_string("floor_pos", SP(floor_pos))
M(door_pos2):set_string("floor_pos", SP(floor_pos))
node1.name = "hyperloop:elevator_door"
node1.param2 = facedir
minetest.swap_node(door_pos1, node1)
node2.name = "hyperloop:elevator_door_top"
node2.param2 = facedir
minetest.swap_node(door_pos2, node2)
elseif cmnd == "darken" then
node1.name = "hyperloop:elevator_door_dark"
node1.param2 = facedir
minetest.swap_node(door_pos1, node1)
node2.name = "hyperloop:elevator_door_dark_top"
node2.param2 = facedir
minetest.swap_node(door_pos2, node2)
end
end
local function on_final_close_door(tArrival)
-- close the door and play sound if no player is around
if hyperloop.is_player_around(tArrival.pos) then
-- try again later
minetest.after(3.0, on_final_close_door, tArrival)
else
door_command(tArrival.pos, tArrival.facedir, "close", true)
end
end
local function on_open_door(tArrival)
door_command(tArrival.pos, tArrival.facedir, "open", true)
minetest.after(5.0, on_final_close_door, tArrival)
tArrival.busy = false
end
local function on_arrival_floor(tDeparture, tArrival, player_name, snd)
local player = minetest.get_player_by_name(player_name)
door_command(tDeparture.pos, tDeparture.facedir, "close", false)
door_command(tArrival.pos, tArrival.facedir, "close", false)
tDeparture.busy = false
if player ~= nil then
tArrival.pos.y = tArrival.pos.y - kPLAYER_OVER_GROUND
player:set_pos(tArrival.pos)
if PlayerNameTags[player_name] then
player:set_nametag_attributes(PlayerNameTags[player_name])
PlayerNameTags[player_name] = nil
end
tArrival.pos.y = tArrival.pos.y + kPLAYER_OVER_GROUND
end
minetest.sound_stop(snd)
minetest.after(1.0, on_open_door, tArrival)
end
local function on_travel(tDeparture, tArrival, player_name, seconds)
local player = minetest.get_player_by_name(player_name)
door_command(tDeparture.pos, tDeparture.facedir, "darken", false)
door_command(tArrival.pos, tArrival.facedir, "darken", false)
if player ~= nil then
PlayerNameTags[player_name] = player:get_nametag_attributes()
player:set_nametag_attributes({text = " "})
end
local snd = minetest.sound_play("ele_norm", {
pos = tDeparture.pos,
gain = 0.5,
max_hear_distance = 3,
loop = true,
})
minetest.after(seconds, on_arrival_floor, tDeparture, tArrival, player_name, snd)
end
minetest.register_node("hyperloop:elevator_bottom", {
description = S("Hyperloop Elevator"),
tiles = {
"hyperloop_elevator_bottom.png",
"hyperloop_elevator_bottom.png",
"hyperloop_elevator.png",
"hyperloop_elevator.png",
"hyperloop_elevator.png",
"hyperloop_elevator.png",
},
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, 7/16, 7/16, 8/16, 8/16},
{ -8/16, -8/16, -8/16, 8/16, -7/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, -8/16, 8/16, 23/16, 8/16 },
},
inventory_image = "hyperloop_elevator_inventory.png",
on_rotate = screwdriver.disallow,
drawtype = "nodebox",
paramtype = 'light',
light_source = 6,
paramtype2 = "facedir",
is_ground_content = false,
groups = {snappy = 3},
after_place_node = function(pos, placer, itemstack, pointed_thing)
local _,node = Shaft:get_node(pos, 6)
if node.name == "air" then
local facedir = hyperloop.get_facedir(placer)
Elevators:set(pos, "<unknown>", {facedir=facedir, busy=false})
Shaft:after_place_node(pos, {5})
-- formspec
local meta = minetest.get_meta(pos)
local formspec = "size[6,4]"..
"label[0,0;"..S("Please insert floor name").."]" ..
"field[0.5,1.5;5,1;floor;"..S("Floor name")..";"..S("Base").."]" ..
"button_exit[2,3;2,1;exit;"..S("Save").."]"
meta:set_string("formspec", formspec)
-- add upper part of the car
pos = Shaft:get_pos(pos, 6)
minetest.add_node(pos, {name="hyperloop:elevator_top", param2=facedir})
Shaft:after_place_node(pos, {6})
else
minetest.remove_node(pos)
return true
end
end,
on_receive_fields = function(pos, formname, fields, player)
-- floor name entered?
if fields.floor ~= nil then
local floor = string.trim(fields.floor)
if floor == "" then
return
end
Elevators:update(pos, {name=floor})
update_formspec(pos)
elseif fields.button ~= nil then -- destination selected?
update_formspec(pos)
local floor = Elevators:get(pos)
if floor then
floor = table.copy(floor)
floor.pos = pos
local sKey = SP(pos)
local idx = tonumber(fields.button)
if idx then
local lFloors = Cache[sKey].lFloors
local dest = lFloors[#lFloors-idx]
if dest and dest.pos and floor.pos then
local dist = hyperloop.distance(dest.pos, floor.pos)
if dist ~= 0 and floor.busy ~= true then
if player ~= nil then
pos.y = pos.y - kPLAYER_OVER_GROUND
player:set_pos(pos)
pos.y = pos.y + kPLAYER_OVER_GROUND
end
-- due to the missing display, a trip needs 20 sec maximum
local seconds = math.min(1 + math.floor(dist/30), 20)
floor.busy = true
door_command(floor.pos, floor.facedir, "close", true)
door_command(dest.pos, dest.facedir, "close", true)
minetest.after(1.0, on_travel, floor, dest, player:get_player_name(), seconds)
end
end
end
end
end
end,
on_punch = function(pos, node, puncher, pointed_thing)
update_formspec(pos)
local floor = Elevators:get(pos)
if floor and floor.busy ~= true then
door_command(pos, floor.facedir, "open", true)
end
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Shaft:after_dig_node(pos, {5})
Elevators:delete(pos)
-- remove the bottom also
pos = Shaft:get_pos(pos, 6)
minetest.remove_node(pos)
Shaft:after_dig_node(pos, {6})
end,
})
minetest.register_node("hyperloop:elevator_top", {
description = S("Hyperloop Elevator"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_elevator_bottom.png",
"hyperloop_elevator_bottom.png",
"hyperloop_elevator_top.png",
"hyperloop_elevator.png",
"hyperloop_elevator.png",
"hyperloop_elevator.png",
},
node_box = {
type = "fixed",
fixed = {
{ -8/16, 7/16, -8/16, 8/16, 8/16, 8/16},
{ -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, 7/16, 7/16, 8/16, 8/16},
},
},
on_rotate = screwdriver.disallow,
drawtype = "nodebox",
paramtype = 'light',
light_source = 6,
paramtype2 = "facedir",
is_ground_content = false,
diggable = false,
groups = {not_in_creative_inventory=1},
drop = "",
})
minetest.register_node("hyperloop:elevator_door_top", {
description = "Hyperloop Elevator Door",
tiles = {
-- up, down, right, left, back, front
"hyperloop_elevator_door_top.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
},
},
on_rotate = screwdriver.disallow,
drop = "",
paramtype = 'light',
paramtype2 = "facedir",
use_texture_alpha = hyperloop.CLIP,
is_ground_content = false,
groups = {snappy = 3, not_in_creative_inventory=1},
})
minetest.register_node("hyperloop:elevator_door", {
description = "Hyperloop Elevator Door",
tiles = {
-- up, down, right, left, back, front
"hyperloop_elevator_door.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, 6.5/16, 8/16, 24/16, 8/16 },
},
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local floor_pos = P(M(pos):get_string("floor_pos"))
if floor_pos ~= nil then
update_formspec(floor_pos)
local floor = Elevators:get(floor_pos)
if floor and floor.busy ~= true then
door_command(floor_pos, floor.facedir, "open", true)
end
end
end,
on_rotate = screwdriver.disallow,
drop = "",
paramtype = 'light',
paramtype2 = "facedir",
use_texture_alpha = hyperloop.CLIP,
is_ground_content = false,
groups = {snappy = 3, not_in_creative_inventory=1},
})
minetest.register_node("hyperloop:elevator_door_dark_top", {
description = "Hyperloop Elevator Door",
tiles = {
-- up, down, right, left, back, front
"hyperloop_elevator_dark_top.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
},
},
on_rotate = screwdriver.disallow,
drop = "",
paramtype = 'light',
paramtype2 = "facedir",
is_ground_content = false,
groups = {snappy = 3, not_in_creative_inventory=1},
})
minetest.register_node("hyperloop:elevator_door_dark", {
description = "Hyperloop Elevator Door",
tiles = {
-- up, down, right, left, back, front
"hyperloop_elevator_dark.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, 7/16, 8/16, 24/16, 8/16 },
},
on_rotate = screwdriver.disallow,
drop = "",
paramtype = 'light',
paramtype2 = "facedir",
is_ground_content = false,
groups = {snappy = 3, not_in_creative_inventory=1},
})