--GPLv3, derived from Travelnet by Sokomine local SPEED = 10 local ACCEL = 1 local VERSION = 5 local elevator = { motors = {}, } local boxes = {} local formspecs = {} 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() local function phash(pos) return minetest.pos_to_string(pos) end local function locate_motor(pos) local p = vector.new(pos) while true do local node = technic.get_or_load_node(p) or technic.get_or_load_node(p) 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] if not motor then return end local p = minetest.string_to_pos(hash) local node = technic.get_or_load_node(p) or technic.get_or_load_node(p) if node.name ~= "elevator:motor" then return end p.y = p.y - 1 motor.elevators = {} motor.pnames = {} while true do local node = technic.get_or_load_node(p) or technic.get_or_load_node(p) if node.name == "elevator:shaft" then p.y = p.y - 1 else p.y = p.y - 1 local node = technic.get_or_load_node(p) or technic.get_or_load_node(p) 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)) p.y = p.y - 1 need_saving = true else break end end end for i,m in ipairs(motor.elevators) do local tpnames = {} local pos = minetest.string_to_pos(m) local meta = minetest.get_meta(pos) for ji,jv in ipairs(motor.pnames) do if ji ~= i then table.insert(tpnames, jv) end end meta:set_string("elevator_formspec", "size[3,2]" .."dropdown[0,0;3;target;"..table.concat(tpnames, ",")..";1]" .."button_exit[0,1;3,1;go;Go]") meta:set_int("version", VERSION) if meta:get_string("motor") ~= hash then build_motor(meta:get_string("motor")) end meta:set_string("motor", hash) 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 while true do local node = technic.get_or_load_node(p) or technic.get_or_load_node(p) if node.name == "elevator:shaft" then p.y = p.y - 1 else p.y = p.y - 1 local node = technic.get_or_load_node(p) or technic.get_or_load_node(p) 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 minetest.after(0.01, function(p2) p2.y = p2.y + add build_motor(locate_motor(p2)) end, table.copy(pos)) end minetest.register_node("elevator:motor", { description = "Elevator Motor", tiles = { "technic_wrought_iron_block.png^homedecor_motor.png" }, groups = {cracky=1}, sounds = default.node_sound_stone_defaults(), after_place_node = function(pos, placer, itemstack) elevator.motors[phash(pos)] = { elevators = {}, pnames = {}, } save_elevator() build_motor(phash(pos)) end, on_destruct = function(pos) if boxes[phash(pos)] then boxes[phash(pos)]:remove() boxes[phash(pos)] = nil end elevator.motors[phash(pos)] = nil save_elevator() end, }) for _,mode in ipairs({"on", "off"}) do local nodename = "elevator:elevator_"..mode local on = (mode == "on") local box if on then 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}, --groundplate to stand on { -0.5,-0.5,-0.5,0.5,-0.48, 0.5}, { -0.5, 1.45,-0.5,0.5, 1.5, 0.5}, } else 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.48, -0.4, 1.5, -0.5}, --{-0.2 , -0.5, -0.48, -0.1, 1.5, -0.5}, --{0.1 , -0.5, -0.48, 0.2, 1.5, -0.5}, --{0.4 , -0.5, -0.48, 0.5, 1.5, -0.5}, --groundplate to stand on { -0.5,-0.5,-0.5,0.5,-0.48, 0.5}, { -0.5, 1.45,-0.5,0.5, 1.5, 0.5}, } end minetest.register_node(nodename, { description = "Elevator", drawtype = (on and "mesh" or "nodebox"), mesh = "travelnet_elevator.obj", --drawtype = "nodebox", sunlight_propagates = false, 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 = on and { "elevator_box.png", "elevator_box.png", "elevator_box.png", "elevator_box.png", "default_steel_block.png", "default_steel_block.png", } or { "elevator_box.png", "elevator_box.png", "elevator_box.png", "elevator_box.png", "elevator_box.png", "elevator_box.png", }, --inventory_image = "travelnet_elevator_inv.png", groups = {cracky=1, choppy=1, snappy=1}, drop = "elevator:elevator_off", light_source = (on and 4 or nil), after_place_node = function(pos, placer, itemstack) local meta = minetest.get_meta(pos) meta:set_int("version", VERSION) local p = {x=pos.x, y=pos.y+1, z=pos.z} local p2 = minetest.dir_to_facedir(placer:get_look_dir()) minetest.set_node(p, {name="homedecor:expansion_placeholder", paramtype2="facedir", param2=p2}) local motor = locate_motor(pos) if motor then build_motor(motor) end end, after_dig_node = function(pos, node, meta, digger) unbuild(pos, 2) end, on_place = function(itemstack, placer, pointed_thing) local pos = pointed_thing.above local node = minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}) if( node ~= nil and node.name ~= "air" and node.name ~= 'homedecor:expansion_placeholder') then return end return minetest.item_place(itemstack, placer, pointed_thing); end, on_rightclick = function(pos, node, sender) local meta = minetest.get_meta(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 end formspecs[sender:get_player_name()] = pos minetest.show_formspec(sender:get_player_name(), "elevator:elevator", meta:get_string("elevator_formspec")) elseif not elevator.motors[meta:get_string("motor")] then minetest.chat_send_player(sender:get_player_name(), "This elevator is inactive.") elseif boxes[meta:get_string("motor")] then minetest.chat_send_player(sender:get_player_name(), "This elevator is in use.") end end, on_destruct = function(pos) local p = {x=pos.x, y=pos.y+1, z=pos.z} minetest.remove_node(p) end, }) end minetest.register_on_player_receive_fields(function(sender, formname, fields) if formname ~= "elevator:elevator" then return end local pos = formspecs[sender:get_player_name()] if not pos then return end if fields.go then local meta = minetest.get_meta(pos) 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 local target = nil for i,v in ipairs(motor.pnames) do if v == fields.target then target = minetest.string_to_pos(motor.elevators[i]) end end if target then if boxes[motorhash] then minetest.chat_send_player(sender:get_player_name(), "This elevator is in use.") return true end local obj = minetest.add_entity(pos, "elevator:box") obj:set_pos(pos) 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}) obj:get_luaentity().motor = motorhash 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 obj:setvelocity({x=0, y=SPEED*obj:get_luaentity().vmult, z=0}) obj:setacceleration({x=0, y=ACCEL*obj:get_luaentity().vmult, z=0}) boxes[motorhash] = obj else minetest.chat_send_player(sender:get_player_name(), "This target is invalid.") return true end return true end return true end) minetest.register_alias("elevator:elevator", "elevator:elevator_off") local offabm = function(pos, node) local meta = minetest.get_meta(pos) 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", "") end 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, }) minetest.register_abm({ nodenames = {"elevator:elevator_on"}, interval = 1, chance = 1, action = function(pos, node) local meta = minetest.get_meta(pos) 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", "") end 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", 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) build_motor(locate_motor(pos)) end, on_destruct = function(pos) 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.48, -0.4, 1.5, -0.5}, --{-0.2 , -0.5, -0.48, -0.1, 1.5, -0.5}, --{0.1 , -0.5, -0.48, 0.2, 1.5, -0.5}, --{0.4 , -0.5, -0.48, 0.5, 1.5, -0.5}, --groundplate to stand on { -0.5,-0.5,-0.5,0.5,-0.48, 0.5}, { -0.5, 1.45,-0.5,0.5, 1.5, 0.5}, } minetest.register_node("elevator:elevator_box", { description = "Elevator", drawtype = ("nodebox"), 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, }) local box_entity = { physical = false, --collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, collisionbox = {0,0,0,0,0,0}, visual = "wielditem", mesh = "carts_cart.b3d", visual_size = {x=1, y=1}, textures = {"elevator:elevator_box"}, attached = "", motor = false, target = false, start = false, lastpos = false, halfway = false, vmult = 0, on_activate = function(self) self.object:set_armor_groups({immortal=1}) end, on_step = function(self, dtime) if not minetest.get_player_by_name(self.attached) then self.object:remove() boxes[self.motor] = nil return end local pos = self.object:getpos() self.lastpos = self.lastpos or pos 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 above = vector.add(p, {x=0,y=1,z=0}) local below = vector.add(p, {x=0,y=-1,z=0}) local node = technic.get_or_load_node(p) or technic.get_or_load_node(p) if node.name == "elevator:shaft" then -- Nothing elseif node.name == "elevator:elevator_on" or node.name == "elevator:elevator_off" then if vector.distance(p, self.target) < 1 then self.object:remove() minetest.get_player_by_name(self.attached):set_detach() minetest.get_player_by_name(self.attached):set_pos(vector.add(self.target, {x=0, y=-0.4, z=0})) minetest.get_player_by_name(self.attached):set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0}) boxes[self.motor] = nil offabm(self.target, node) return end else --local abovenode = technic.get_or_load_node(above) or technic.get_or_load_node(above) local belownode = technic.get_or_load_node(below) or technic.get_or_load_node(below) if belownode.name ~= "elevator:elevator_on" and belownode.name ~= "elevator:elevator_off" then boxes[self.motor] = nil self.object:remove() minetest.get_player_by_name(self.attached):set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0}) return end end end self.lastpos = pos if vector.distance(pos, self.target) < 32 and self.object:getvelocity().y > SPEED/2 then self.object:setacceleration({x=0, y=((SPEED)*self.vmult*-1), z=0}) elseif self.object:getvelocity().y > SPEED and vector.distance(pos, self.target) < math.min(100, vector.distance(pos, self.start)) then self.object:setacceleration({x=0, y=((SPEED/10)*self.vmult*-1), z=0}) elseif self.object:getvelocity().y <= SPEED/1.5 then self.object:setacceleration({x=0, y=((SPEED/10)*self.vmult), z=0}) end end, } minetest.register_entity("elevator:box", box_entity) 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"}, }, }) minetest.register_craft({ output = "elevator:shaft", recipe = { {"technic:cast_iron_ingot", "default:glass"}, {"default:glass", "homedecor:chainlink_steel"}, }, }) 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"} }, })