123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937 |
- -- Initial speed of a box.
- local SPEED = 7
- -- Acceleration of a box.
- local ACCEL = 0.1
- -- Elevator interface/database version.
- local VERSION = 8
- -- Maximum time a box can go without players nearby.
- local PTIMEOUT = 120
- -- Localize for performance.
- local vector_distance = vector.distance
- local vector_round = vector.round
- local math_floor = math.floor
- local math_random = math.random
- local math_min = math.min
- local math_max = math.max
- -- Detect optional mods.
- local technic_path = minetest.get_modpath("technic")
- local chains_path = minetest.get_modpath("chains")
- local homedecor_path = minetest.get_modpath("homedecor")
- local armor_path = minetest.get_modpath("3d_armor")
- -- Central "network" table.
- local elevator = {
- motors = {},
- }
- local str = minetest.get_mod_storage and minetest.get_mod_storage()
- local elevator_file = minetest.get_worldpath() .. "/elevator"
- local function load_elevator()
- if str and ((str.contains and str:contains("data")) or (str:get_string("data") and str:get_string("data") ~= "")) then
- elevator = minetest.deserialize(str:get_string("data"))
- return
- end
- local file = io.open(elevator_file)
- if file then
- elevator = minetest.deserialize(file:read("*all")) or {}
- file:close()
- end
- end
- local function save_elevator()
- if str then
- str:set_string("data", minetest.serialize(elevator))
- return
- end
- local f = io.open(elevator_file, "w")
- f:write(minetest.serialize(elevator))
- f:close()
- end
- load_elevator()
- -- Elevator boxes in action.
- local boxes = {}
- -- Player formspecs.
- local formspecs = {}
- -- Player near box timeout.
- local lastboxes = {}
- -- Players riding boxes.
- local riding = {}
- -- Globalstep timer.
- local time = 0
- -- 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
- -- Use homedecor's placeholder if possible.
- local placeholder = homedecor_path and "homedecor:expansion_placeholder" or "elevator:placeholder"
- if homedecor_path 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,
- walkable = false,
- buildable_to = false,
- is_ground_content = false,
- on_dig = function(pos, node, player)
- minetest.remove_node(pos)
- minetest.set_node(pos, {name=placeholder})
- end
- })
- end
- local VISUAL_INCREASE = 1.75
- -- 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:setpos(pos)
- -- Attach the player.
- sender:setpos(pos)
- sender:set_attach(obj, "", {x=0, y=9, z=0}, {x=0, y=0, z=0})
- pova.set_modifier(sender, "eye_offset", {{x=0, y=-9, z=0},{x=0, y=-9, z=0}}, "elevator")
- pova.set_modifier(sender, "properties", {visual_size = {x=VISUAL_INCREASE, y=VISUAL_INCREASE}}, "elevator")
- if armor_path then
- armor:update_player_visuals(sender)
- end
- -- 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:set_velocity({x=0, y=SPEED*obj:get_luaentity().vmult, z=0})
- obj:set_acceleration({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
- -- Try to teleport player away from any closed (on) elevator node.
- 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:get_pos())
- local node = minetest.get_node(pos)
- -- elevator_off is like a shaft, so the player would already be falling.
- 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})
- -- If the front isn't solid, it's ok to teleport the player.
- if not solid(front) and not solid(front_above) then
- player:setpos(front)
- end
- end
- end
- minetest.register_globalstep(function(dtime)
- -- Don't want to run this too often.
- time = time + dtime
- if time < 0.5 then
- return
- end
- time = 0
- -- Only count riders who are still logged in.
- local newriding = {}
- for _,p in ipairs(minetest.get_connected_players()) do
- local pos = p:get_pos()
- 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
- end
- end
- riding = newriding
- for name,r in pairs(riding) do
- -- If the box is no longer loaded or existent, create another.
- local ok = r.box and r.box.get_pos and r.box:get_pos() 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))
- end
- end
- -- Ensure boxes are deleted after <PTIMEOUT> seconds if there are no players nearby.
- for motor,obj in pairs(boxes) do
- if type(obj) ~= "table" then
- return
- end
- lastboxes[motor] = lastboxes[motor] and math_min(lastboxes[motor], PTIMEOUT) or PTIMEOUT
- lastboxes[motor] = math_max(lastboxes[motor] - 1, 0)
- local pos = obj:get_pos()
- 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
- end
- 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
- minetest.log("action", "[elevator] "..minetest.pos_to_string(pos).." broke due to lack of position during player check.")
- boxes[motor] = false
- end
- end
- end)
- minetest.register_on_leaveplayer(function(player)
- -- We don't want players potentially logging into open elevators.
- teleport_player_from_elevator(player)
- end)
- local function phash(pos)
- return minetest.pos_to_string(pos)
- end
- local function punhash(pos)
- return minetest.string_to_pos(pos)
- end
- -- Starting from <pos>, locate a motor hash.
- local function locate_motor(pos)
- local p = vector.new(pos)
- while true do
- local node = get_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]
- -- Just ignore motors that don't exist.
- if not motor then
- return
- end
- local p = punhash(hash)
- local node = get_node(p)
- -- And ignore motors that aren't motors.
- if node.name ~= "elevator:motor" then
- return
- end
- p.y = p.y - 1
- motor.elevators = {}
- motor.pnames = {}
- motor.labels = {}
- -- Run down through the shaft, storing information about elevators.
- while true do
- local node = get_node(p)
- if node.name == "elevator:shaft" then
- p.y = p.y - 1
- else
- p.y = p.y - 1
- local node = get_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))
- table.insert(motor.labels, "")
- p.y = p.y - 1
- need_saving = true
- else
- break
- end
- end
- end
- -- Set the elevators fully.
- for i,m in ipairs(motor.elevators) do
- local pos = punhash(m)
- 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
- motor.labels[i] = meta:get_string("label")
- meta:set_string("motor", hash)
- if motor.labels[i] ~= meta:get_string("infotext") then
- meta:set_string("infotext", motor.labels[i])
- end
- 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
- -- Loop down through the network, set any elevators below this to the off position.
- while true do
- local node = get_node(p)
- if node.name == "elevator:shaft" then
- p.y = p.y - 1
- else
- p.y = p.y - 1
- local node = get_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
- -- After a short delay, build the motor and handle box removal.
- minetest.after(0.01, function(p2, add)
- if not p2 or not add then
- return
- end
- p2.y = p2.y + add
- local motorhash = locate_motor(p2)
- build_motor(motorhash)
- -- If there's a box below this point, break it.
- if boxes[motorhash] and boxes[motorhash]:get_pos() and p2.y >= boxes[motorhash]:get_pos().y then
- boxes[motorhash] = nil
- end
- -- If the box does not exist, just clear it.
- if boxes[motorhash] and not boxes[motorhash]:get_pos() then
- boxes[motorhash] = nil
- end
- end, table.copy(pos), add)
- end
- minetest.register_node("elevator:motor", {
- description = "Elevator Motor",
- tiles = {
- "default_steel_block.png",
- "default_steel_block.png",
- "elevator_motor.png",
- "elevator_motor.png",
- "elevator_motor.png",
- "elevator_motor.png",
- },
- groups = {cracky=1},
- sounds = default.node_sound_stone_defaults(),
- after_place_node = function(pos, placer, itemstack)
- -- Set up the motor table.
- elevator.motors[phash(pos)] = {
- elevators = {},
- pnames = {},
- labels = {},
- }
- save_elevator()
- build_motor(phash(pos))
- end,
- on_destruct = function(pos)
- -- Destroy everything related to this motor.
- boxes[phash(pos)] = nil
- 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
- 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,
- },
- collision_box = {
- type = "fixed",
- fixed = cbox,
- },
- node_box = {
- type = "fixed",
- fixed = box,
- },
- 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",
- -- Emit a bit of light when active.
- 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)
- -- 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})
- -- Try to build a motor above.
- 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(vector.add(pos, {x=0, y=1, z=0}))
- if (node ~= nil and node.name ~= "air" and node.name ~= placeholder) then
- return
- end
- return minetest.item_place(itemstack, placer, pointed_thing)
- end,
- on_rightclick = function(pos, node, sender)
- if not sender or not sender:is_player() then
- return
- end
- local formspec
- 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(), "# Server: You are not inside the booth.")
- return
- end
- -- Build the formspec from the motor table.
- 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)
- local jy = jv -- Is a string.
- local rp = rc.pos_to_realmpos({x=pos.x, y=tonumber(jv), z=pos.z})
- if rp then
- jy = tostring(rp.y)
- end
- local ename = (motor.labels[ji] and motor.labels[ji] ~= "") and (jy .. " - " .. minetest.formspec_escape(motor.labels[ji])) or jy
- if tonumber(jv) > pos.y then
- ename = ename .. " - Up"
- else
- ename = ename .. " - Down"
- end
- table.insert(tpnames_l, ename)
- end
- end
- formspecs[sender:get_player_name()] = {pos, tpnames}
- if #tpnames > 0 then
- if not minetest.test_protection(pos, sender:get_player_name()) then
- formspec = "size[4,6]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots
- .."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,4.4]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots
- .."label[0,0;Click once to travel.]"
- .."textlist[-0.1,0.5;4,4;target;"..table.concat(tpnames_l, ",").."]"
- end
- else
- if not minetest.test_protection(pos, sender:get_player_name()) then
- formspec = "size[4,2]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots
- .."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]"
- else
- formspec = "size[4,0.4]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots
- .."label[0,0;No targets available.]"
- end
- end
- minetest.show_formspec(sender:get_player_name(), "elevator:elevator", formspec)
- elseif not elevator.motors[meta:get_string("motor")] then
- if not minetest.test_protection(pos, sender:get_player_name()) then
- formspec = "size[4,2]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots
- .."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]"
- else
- formspec = "size[4,0.4]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots
- .."label[0,0;This elevator is inactive.]"
- end
- minetest.show_formspec(sender:get_player_name(), "elevator:elevator", formspec)
- elseif boxes[meta:get_string("motor")] then
- if not minetest.test_protection(pos, sender:get_player_name()) then
- formspec = "size[4,2]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots
- .."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]"
- else
- formspec = "size[4,0.4]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots
- .."label[0,0;This elevator is in use.]"
- end
- minetest.show_formspec(sender:get_player_name(), "elevator:elevator", formspec)
- end
- end,
- 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,
- })
- 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()] and formspecs[sender:get_player_name()][1] or nil
- if not pos then
- return true
- end
- local meta = minetest.get_meta(pos)
- if fields.setlabel then
- if minetest.test_protection(pos, sender:get_player_name()) then
- return true
- end
- meta:set_string("label", fields.label)
- meta:set_string("infotext", fields.label)
- -- Rebuild the elevator shaft so the other elevators can read this label.
- local motorhash = meta:get_string("motor")
- build_motor(elevator.motors[motorhash] and motorhash or locate_motor(pos))
- return true
- end
- -- Double check if it's ok to go.
- if vector_distance(sender:get_pos(), pos) > 1 then
- return true
- end
- if fields.target then
- local closeformspec = ""
- -- HACK: With player information extensions enabled, we can check if closing formspecs are now allowed. This is specifically used on Survival in Ethereal.
- 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.\nUpgrade Minetest to avoid this dialog.] button_exit[0,1;4,1;close;Close]"
- end
- -- End hacky HACK.
- minetest.after(0.2, minetest.show_formspec, sender:get_player_name(), "elevator:elevator", closeformspec)
- -- Ensure we're connected to a motor.
- 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(), "# Server: Recalibrated to a new motor, please try again.")
- return true
- end
- end
- if not motor then
- minetest.chat_send_player(sender:get_player_name(), "# Server: This elevator is not attached to a motor.")
- return true
- end
- if not formspecs[sender:get_player_name()][2] or not formspecs[sender:get_player_name()][2][minetest.explode_textlist_event(fields.target).index] then
- return true
- end
- -- Locate our target elevator.
- local target = nil
- local selected_target = formspecs[sender:get_player_name()][2][minetest.explode_textlist_event(fields.target).index]
- for i,v in ipairs(motor.pnames) do
- if v == selected_target then
- target = punhash(motor.elevators[i])
- end
- end
- -- Found the elevator? Then go!
- if target then
- -- Final check.
- if boxes[motorhash] then
- minetest.chat_send_player(sender:get_player_name(), "# Server: This elevator is in use.")
- return true
- end
- local obj = create_box(motorhash, pos, target, sender)
- -- Teleport anyone standing within an on elevator out, or they'd fall through the off elevators.
- for _,p in ipairs(motor.elevators) do
- local p = punhash(p)
- for _,object in ipairs(minetest.get_objects_inside_radius(p, 0.6)) do
- if object.is_player and object:is_player() then
- if object:get_player_name() ~= obj:get_luaentity().attached then
- teleport_player_from_elevator(object)
- end
- end
- end
- end
- else
- minetest.chat_send_player(sender:get_player_name(), "# Server: This target is invalid.")
- return true
- end
- return true
- end
- return true
- end)
- -- Compatability with an older version.
- minetest.register_alias("elevator:elevator", "elevator:elevator_off")
- -- Ensure an elevator is up to the latest version.
- local function upgrade_elevator(pos, meta)
- 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", "")
- meta:set_string("infotext", meta:get_string("label"))
- end
- end
- -- Convert off to on when applicable.
- local offabm = function(pos, node)
- local meta = minetest.get_meta(pos)
- upgrade_elevator(pos, meta)
- 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,
- label = "Elevator (Off)",
- })
- -- Convert on to off when applicable.
- minetest.register_abm({
- nodenames = {"elevator:elevator_on"},
- interval = 1,
- chance = 1,
- action = function(pos, node)
- local meta = minetest.get_meta(pos)
- upgrade_elevator(pos, meta)
- 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,
- label = "Elevator (On)",
- })
- minetest.register_node("elevator:shaft", {
- description = "Elevator Shaft",
- tiles = { "elevator_shaft.png" },
- drawtype = "nodebox",
- paramtype = "light",
- on_rotate = screwdriver.disallow,
- 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)
- -- We might have connected a motor above to an elevator below.
- build_motor(locate_motor(pos))
- end,
- on_destruct = function(pos)
- -- Remove boxes and deactivate elevators below us.
- 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},
- }
- -- Elevator box node. Not intended to be placeable.
- 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,
- })
- -- Remove the player from self, and teleport them to pos if specified.
- 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()
- pova.remove_modifier(player, "eye_offset", "elevator")
- pova.remove_modifier(player, "properties", "elevator")
- if armor_path then
- armor:update_player_visuals(player)
- end
- if pos then
- player:setpos(pos)
- minetest.after(0.1, function(pl, p)
- pl:setpos(p)
- end, player, pos)
- end
- riding[self.attached] = nil
- end
- 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,
- on_activate = function(self, staticdata)
- -- Don't want the box being destroyed by anything except the elevator system.
- self.object:set_armor_groups({immortal=1})
- end,
- on_step = function(self, dtime)
- local pos = self.object:get_pos()
- -- First, check if this box needs removed.
- -- If the motor has a box and it isn't this box.
- 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
- -- If our attached player can't be found.
- if not minetest.get_player_by_name(self.attached) then
- minetest.log("action", "[elevator] "..minetest.pos_to_string(pos).." broke due to lack of attachee logged in.")
- self.object:remove()
- boxes[self.motor] = nil
- return
- end
- -- If our attached player is no longer with us.
- 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
- minetest.log("action", "[elevator] "..minetest.pos_to_string(pos).." broke due to lack of attachee.")
- self.object:remove()
- boxes[self.motor] = nil
- return
- end
- -- If our motor's box is nil, we should self-destruct.
- if not boxes[self.motor] then
- minetest.log("action", "[elevator] "..minetest.pos_to_string(pos).." broke due to nil entry in boxes.")
- detach(self)
- self.object:remove()
- boxes[self.motor] = nil
- return
- end
- minetest.get_player_by_name(self.attached):setpos(pos)
- -- Ensure lastpos is set to something.
- self.lastpos = self.lastpos or pos
- -- Loop through all travelled nodes.
- for y=self.lastpos.y,pos.y,((self.lastpos.y > pos.y) and -0.3 or 0.3) do
- local p = vector_round({x=pos.x, y=y, z=pos.z})
- local node = get_node(p)
- if node.name == "elevator:shaft" then
- -- Nothing, just continue on our way.
- elseif node.name == "elevator:elevator_on" or node.name == "elevator:elevator_off" then
- -- If this is our target, detach the player here, destroy this box, and update the target elevator without waiting for the abm.
- if vector_distance(p, self.target) < 1 then
- minetest.log("action", "[elevator] "..minetest.pos_to_string(p).." broke due to arrival.")
- detach(self, vector.add(self.target, {x=0, y=-0.4, z=0}))
- self.object:remove()
- boxes[self.motor] = nil
- offabm(self.target, node)
- return
- end
- else
- -- Check if we're in the top part of an elevator, if so it's fine.
- local below = vector.add(p, {x=0,y=-1,z=0})
- local belownode = get_node(below)
- if belownode.name ~= "elevator:elevator_on" and belownode.name ~= "elevator:elevator_off" then
- -- If we aren't, then break the box.
- minetest.log("action", "[elevator] "..minetest.pos_to_string(p).." broke on "..node.name)
- boxes[self.motor] = nil
- detach(self, p)
- self.object:remove()
- return
- end
- end
- end
- self.lastpos = pos
- end,
- }
- minetest.register_entity("elevator:box", box_entity)
- -- Register recipes!
- minetest.register_craft({
- output = "elevator:elevator",
- recipe = {
- {"cast_iron:ingot", "chains:iron_chain", "cast_iron:ingot"},
- {"cast_iron:ingot", "", "cast_iron:ingot"},
- {"stainless_steel:ingot", "default:glass", "stainless_steel:ingot"},
- },
- })
- minetest.register_craft({
- output = "elevator:shaft",
- recipe = {
- {"titanium:crystal", "", "titanium:crystal"},
- {"xpanes:chainlink_flat", "default:obsidian_glass", "xpanes:chainlink_flat"},
- {"cast_iron:ingot", "", "cast_iron:ingot"},
- },
- })
- minetest.register_craft({
- output = "elevator:motor",
- recipe = {
- {"default:diamond", "techcrafts:control_logic_unit", "default:diamond"},
- {"stainless_steel:block", "techcrafts:electric_motor", "stainless_steel:block"},
- {"chains:iron_chain", "default:diamond", "chains:iron_chain"}
- },
- })
|