From a3966d1a029cbeaaf6778313ee44dd6ada9ed369 Mon Sep 17 00:00:00 2001 From: SX Date: Fri, 14 Feb 2020 02:07:10 +0200 Subject: Refactoring crafts formspecs components helpers --- components.lua | 334 ++++++++++++++++++++++++++++++++++++++++++++++++ crafts.lua | 87 +++++++++++++ formspecs.lua | 90 +++++++++++++ helpers.lua | 38 ++++++ hooks.lua | 6 +- init.lua | 390 ++++----------------------------------------------------- register.lua | 201 ----------------------------- 7 files changed, 575 insertions(+), 571 deletions(-) create mode 100644 components.lua create mode 100644 crafts.lua create mode 100644 formspecs.lua create mode 100644 helpers.lua delete mode 100644 register.lua diff --git a/components.lua b/components.lua new file mode 100644 index 0000000..d018ee0 --- /dev/null +++ b/components.lua @@ -0,0 +1,334 @@ + +local phash = elevator.phash +local get_node = elevator.get_node + +local homedecor_path = minetest.get_modpath("homedecor") + +-- Use homedecor's placeholder if possible. +if homedecor_path then + minetest.register_alias("elevator:placeholder", "homedecor:expansion_placeholder") +else + -- Placeholder node, in the style of homedecor. + minetest.register_node("elevator: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="elevator:placeholder"}) + end + }) +end + +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. + elevator.build_motor(elevator.locate_motor(pos)) + end, + on_destruct = function(pos) + -- Remove boxes and deactivate elevators below us. + elevator.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, +}) + +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 = {}, + } + elevator.save_elevator() + elevator.build_motor(phash(pos)) + end, + on_destruct = function(pos) + -- Destroy everything related to this motor. + elevator.boxes[phash(pos)] = nil + elevator.motors[phash(pos)] = nil + elevator.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", elevator.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="elevator:placeholder", paramtype2="facedir", param2=p2}) + + -- Try to build a motor above. + local motor = elevator.locate_motor(pos) + if motor then + elevator.build_motor(motor) + end + end, + + after_dig_node = function(pos, node, meta, digger) + elevator.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 ~= "elevator: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) + elevator.formspecs[sender:get_player_name()] = {pos} + if on then + if vector.distance(sender:getpos(), pos) > 1 or minetest.get_node(sender:getpos()).name ~= nodename then + minetest.chat_send_player(sender:get_player_name(), "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) + table.insert(tpnames_l, (motor.labels[ji] and motor.labels[ji] ~= "") and (jv.." - "..minetest.formspec_escape(motor.labels[ji])) or jv) + end + end + elevator.formspecs[sender:get_player_name()] = {pos, tpnames} + if #tpnames > 0 then + if not minetest.is_protected(pos, sender:get_player_name()) 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,4.4]" + .."label[0,0;Click once to travel.]" + .."textlist[-0.1,0.5;4,4;target;"..table.concat(tpnames_l, ",").."]" + end + else + if not minetest.is_protected(pos, sender:get_player_name()) then + 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]" + else + formspec = "size[4,0.4]" + .."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.is_protected(pos, sender:get_player_name()) then + formspec = "size[4,2]" + .."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]" + .."label[0,0;This elevator is inactive.]" + end + minetest.show_formspec(sender:get_player_name(), "elevator:elevator", formspec) + elseif elevator.boxes[meta:get_string("motor")] then + if not minetest.is_protected(pos, sender:get_player_name()) 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]" + else + formspec = "size[4,0.4]" + .."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 == "elevator:placeholder" then + minetest.remove_node(p) + end + end, + }) +end + +-- Compatability with an older version. +minetest.register_alias("elevator:elevator", "elevator:elevator_off") diff --git a/crafts.lua b/crafts.lua new file mode 100644 index 0000000..5f5831a --- /dev/null +++ b/crafts.lua @@ -0,0 +1,87 @@ +-- Detect optional mods. +local technic_path = minetest.get_modpath("technic") +local chains_path = minetest.get_modpath("chains") + +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"}, + }, + }) + + 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"} + }, + }) +elseif technic_path and farming and farming.mod and farming.mod == "redo" then + -- add alternative recipe with hemp rope + minetest.register_craft({ + output = "elevator:elevator", + recipe = { + {"technic:cast_iron_ingot", "farming:hemp_rope", "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", "farming:hemp_rope"}, + }, + }) + + minetest.register_craft({ + output = "elevator:motor", + recipe = { + {"default:diamond", "technic:control_logic_unit", "default:diamond"}, + {"default:steelblock", "technic:motor", "default:steelblock"}, + {"farming:hemp_rope", "default:diamond", "farming:hemp_rope"} + }, + }) + +-- Recipes without technic & chains required. +-- Recipes for default dependency fallback. +else + minetest.register_craft({ + output = "elevator:elevator", + recipe = { + {"default:steel_ingot", "farming:cotton", "default:steel_ingot"}, + {"default:steel_ingot", "default:mese_crystal", "default:steel_ingot"}, + {"xpanes:pane_flat", "default:glass", "xpanes:pane_flat"}, + }, + }) + + minetest.register_craft({ + output = "elevator:shaft", + recipe = { + {"default:steel_ingot", "default:obsidian_glass"}, + {"default:obsidian_glass", "default:steel_ingot"}, + }, + }) + + minetest.register_craft({ + output = "elevator:motor", + recipe = { + {"default:diamond", "default:copper_ingot", "default:diamond"}, + {"default:steelblock", "default:furnace", "default:steelblock"}, + {"farming:cotton", "default:diamond", "farming:cotton"} + }, + }) +end diff --git a/formspecs.lua b/formspecs.lua new file mode 100644 index 0000000..64bd020 --- /dev/null +++ b/formspecs.lua @@ -0,0 +1,90 @@ + +local punhash = elevator.punhash + +minetest.register_on_player_receive_fields(function(sender, formname, fields) + if formname ~= "elevator:elevator" then + return + end + local pos = elevator.formspecs[sender:get_player_name()] and elevator.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.is_protected(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") + elevator.build_motor(elevator.motors[motorhash] and motorhash or elevator.locate_motor(pos)) + return true + end + -- Double check if it's ok to go. + if vector.distance(sender:getpos(), 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 = elevator.locate_motor(pos) + motor = elevator.motors[motorhash] + if motor then + meta:set_string("motor", "") + elevator.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 + if not elevator.formspecs[sender:get_player_name()][2] or not elevator.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 = elevator.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 elevator.boxes[motorhash] then + minetest.chat_send_player(sender:get_player_name(), "This elevator is in use.") + return true + end + local obj = elevator.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 + for _,object in ipairs(minetest.get_objects_inside_radius(punhash(p), 0.6)) do + if object.is_player and object:is_player() then + if object:get_player_name() ~= obj:get_luaentity().attached then + elevator.teleport_player_from_elevator(object) + end + end + end + end + else + minetest.chat_send_player(sender:get_player_name(), "This target is invalid.") + return true + end + return true + end + return true +end) diff --git a/helpers.lua b/helpers.lua new file mode 100644 index 0000000..ccd8ebb --- /dev/null +++ b/helpers.lua @@ -0,0 +1,38 @@ + +-- Try to teleport player away from any closed (on) elevator node. +elevator.teleport_player_from_elevator = function(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) + -- 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 + +elevator.phash = function(pos) + return minetest.pos_to_string(pos) +end + +elevator.punhash = function(pos) + return minetest.string_to_pos(pos) +end + +-- Helper function to read unloaded nodes. +elevator.get_node = function(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 diff --git a/hooks.lua b/hooks.lua index ff6d4cb..609f0e9 100644 --- a/hooks.lua +++ b/hooks.lua @@ -26,7 +26,7 @@ minetest.register_globalstep(function(dtime) 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)) + minetest.after(0, elevator.create_box, r.motor, r.pos, r.target, minetest.get_player_by_name(name)) end end -- Ensure boxes are deleted after seconds if there are no players nearby. @@ -34,13 +34,13 @@ minetest.register_globalstep(function(dtime) if type(obj) ~= "table" then return end - elevator.lastboxes[motor] = elevator.lastboxes[motor] and math.min(elevator.lastboxes[motor], PTIMEOUT) or PTIMEOUT + elevator.lastboxes[motor] = elevator.lastboxes[motor] and math.min(elevator.lastboxes[motor], elevator.PTIMEOUT) or elevator.PTIMEOUT elevator.lastboxes[motor] = math.max(elevator.lastboxes[motor] - 1, 0) local pos = obj:getpos() if pos then for _,object in ipairs(minetest.get_objects_inside_radius(pos, 5)) do if object.is_player and object:is_player() then - elevator.lastboxes[motor] = PTIMEOUT + elevator.lastboxes[motor] = elevator.PTIMEOUT break end end diff --git a/init.lua b/init.lua index 905e5e3..b462b5b 100644 --- a/init.lua +++ b/init.lua @@ -1,43 +1,36 @@ --- Initial speed of a box. -local SPEED = 10 --- 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 - -- Detect optional mods. local armor_path = minetest.get_modpath("3d_armor") -- global runtime storage for data and references --- contains .motors loaded from mod storage and api functions +-- contains .motors loaded from mod storage +-- runtime variables and api functions elevator = { + SPEED = 10, -- Initial speed of a box. + ACCEL = 0.1, -- Acceleration of a box. + VISUAL_INCREASE = 1.75, + VERSION = 8, -- Elevator interface/database version. + PTIMEOUT = 120, -- Maximum time a box can go without players nearby. + boxes = {}, -- Elevator boxes in action. lastboxes = {}, -- Player near box timeout. riding = {}, -- Players riding boxes. + formspecs = {}, -- Player formspecs. } local MP = minetest.get_modpath(minetest.get_current_modname()) +dofile(MP .. "/helpers.lua") dofile(MP .. "/storage.lua") -dofile(MP .. "/register.lua") +dofile(MP .. "/crafts.lua") +dofile(MP .. "/components.lua") dofile(MP .. "/hooks.lua") +dofile(MP .. "/formspecs.lua") --- Player formspecs. -local formspecs = {} - --- 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 - -local VISUAL_INCREASE = 1.75 +local phash = elevator.phash +local punhash = elevator.punhash +local get_node = elevator.get_node -- Cause to ride beginning at and targetting . -local function create_box(motorhash, pos, target, sender) +elevator.create_box = function(motorhash, pos, target, sender) -- First create the box. local obj = minetest.add_entity(pos, "elevator:box") obj:setpos(pos) @@ -45,7 +38,7 @@ local function create_box(motorhash, pos, target, sender) sender:setpos(pos) sender:set_attach(obj, "", {x=0, y=9, z=0}, {x=0, y=0, z=0}) sender:set_eye_offset({x=0, y=-9, z=0},{x=0, y=-9, z=0}) - sender:set_properties({visual_size = {x=VISUAL_INCREASE, y=VISUAL_INCREASE}}) + sender:set_properties({visual_size = {x=elevator.VISUAL_INCREASE, y=elevator.VISUAL_INCREASE}}) if armor_path then armor:update_player_visuals(sender) end @@ -58,8 +51,8 @@ local function create_box(motorhash, pos, target, sender) 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}) + obj:setvelocity({x=0, y=elevator.SPEED*obj:get_luaentity().vmult, z=0}) + obj:setacceleration({x=0, y=elevator.ACCEL*obj:get_luaentity().vmult, z=0}) -- Set the tables. elevator.boxes[motorhash] = obj elevator.riding[sender:get_player_name()] = { @@ -71,36 +64,6 @@ local function create_box(motorhash, pos, target, sender) return obj end --- Try to teleport player away from any closed (on) elevator node. -elevator.teleport_player_from_elevator = function(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) - -- 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 - -local function phash(pos) - return minetest.pos_to_string(pos) -end - -local function punhash(pos) - return minetest.string_to_pos(pos) -end - -- Starting from , locate a motor hash. elevator.locate_motor = function(pos) local p = vector.new(pos) @@ -158,7 +121,7 @@ elevator.build_motor = function(hash) for i,m in ipairs(motor.elevators) do local pos = punhash(m) local meta = minetest.get_meta(pos) - meta:set_int("version", VERSION) + meta:set_int("version", elevator.VERSION) if meta:get_string("motor") ~= hash then elevator.build_motor(meta:get_string("motor")) end @@ -213,319 +176,12 @@ elevator.unbuild = function(pos, add) 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 = {}, - } - elevator.save_elevator() - elevator.build_motor(phash(pos)) - end, - on_destruct = function(pos) - -- Destroy everything related to this motor. - elevator.boxes[phash(pos)] = nil - elevator.motors[phash(pos)] = nil - elevator.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="elevator:placeholder", paramtype2="facedir", param2=p2}) - - -- Try to build a motor above. - local motor = elevator.locate_motor(pos) - if motor then - elevator.build_motor(motor) - end - end, - - after_dig_node = function(pos, node, meta, digger) - elevator.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 ~= "elevator: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:getpos(), pos) > 1 or minetest.get_node(sender:getpos()).name ~= nodename then - minetest.chat_send_player(sender:get_player_name(), "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) - table.insert(tpnames_l, (motor.labels[ji] and motor.labels[ji] ~= "") and (jv.." - "..minetest.formspec_escape(motor.labels[ji])) or jv) - end - end - formspecs[sender:get_player_name()] = {pos, tpnames} - if #tpnames > 0 then - if not minetest.is_protected(pos, sender:get_player_name()) 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,4.4]" - .."label[0,0;Click once to travel.]" - .."textlist[-0.1,0.5;4,4;target;"..table.concat(tpnames_l, ",").."]" - end - else - if not minetest.is_protected(pos, sender:get_player_name()) then - 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]" - else - formspec = "size[4,0.4]" - .."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.is_protected(pos, sender:get_player_name()) then - formspec = "size[4,2]" - .."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]" - .."label[0,0;This elevator is inactive.]" - end - minetest.show_formspec(sender:get_player_name(), "elevator:elevator", formspec) - elseif elevator.boxes[meta:get_string("motor")] then - if not minetest.is_protected(pos, sender:get_player_name()) 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]" - else - formspec = "size[4,0.4]" - .."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 == "elevator: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.is_protected(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") - elevator.build_motor(elevator.motors[motorhash] and motorhash or elevator.locate_motor(pos)) - return true - end - -- Double check if it's ok to go. - if vector.distance(sender:getpos(), 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 = elevator.locate_motor(pos) - motor = elevator.motors[motorhash] - if motor then - meta:set_string("motor", "") - elevator.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 - 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 elevator.boxes[motorhash] then - minetest.chat_send_player(sender:get_player_name(), "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 - elevator.teleport_player_from_elevator(object) - end - end - end - end - else - minetest.chat_send_player(sender:get_player_name(), "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 + if meta:get_int("version") ~= elevator.VERSION then minetest.log("action", "[elevator] Updating elevator with old version at "..minetest.pos_to_string(pos)) minetest.after(0, function(pos) elevator.build_motor(elevator.locate_motor(pos)) end, pos) - meta:set_int("version", VERSION) + meta:set_int("version", elevator.VERSION) meta:set_string("formspec", "") meta:set_string("infotext", meta:get_string("label")) end diff --git a/register.lua b/register.lua deleted file mode 100644 index af8048b..0000000 --- a/register.lua +++ /dev/null @@ -1,201 +0,0 @@ --- Detect optional mods. -local technic_path = minetest.get_modpath("technic") -local chains_path = minetest.get_modpath("chains") -local technic_path = minetest.get_modpath("technic") -local chains_path = minetest.get_modpath("chains") -local homedecor_path = minetest.get_modpath("homedecor") - --- Use homedecor's placeholder if possible. -if homedecor_path then - minetest.register_alias("elevator:placeholder", "homedecor:expansion_placeholder") -else - -- Placeholder node, in the style of homedecor. - minetest.register_node("elevator: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="elevator:placeholder"}) - end - }) -end - -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"}, - }, - }) - - 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"} - }, - }) -elseif technic_path and farming and farming.mod and farming.mod == "redo" then - -- add alternative recipe with hemp rope - minetest.register_craft({ - output = "elevator:elevator", - recipe = { - {"technic:cast_iron_ingot", "farming:hemp_rope", "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", "farming:hemp_rope"}, - }, - }) - - minetest.register_craft({ - output = "elevator:motor", - recipe = { - {"default:diamond", "technic:control_logic_unit", "default:diamond"}, - {"default:steelblock", "technic:motor", "default:steelblock"}, - {"farming:hemp_rope", "default:diamond", "farming:hemp_rope"} - }, - }) - --- Recipes without technic & chains required. --- Recipes for default dependency fallback. -else - minetest.register_craft({ - output = "elevator:elevator", - recipe = { - {"default:steel_ingot", "farming:cotton", "default:steel_ingot"}, - {"default:steel_ingot", "default:mese_crystal", "default:steel_ingot"}, - {"xpanes:pane_flat", "default:glass", "xpanes:pane_flat"}, - }, - }) - - minetest.register_craft({ - output = "elevator:shaft", - recipe = { - {"default:steel_ingot", "default:obsidian_glass"}, - {"default:obsidian_glass", "default:steel_ingot"}, - }, - }) - - minetest.register_craft({ - output = "elevator:motor", - recipe = { - {"default:diamond", "default:copper_ingot", "default:diamond"}, - {"default:steelblock", "default:furnace", "default:steelblock"}, - {"farming:cotton", "default:diamond", "farming:cotton"} - }, - }) -end - -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. - elevator.build_motor(elevator.locate_motor(pos)) - end, - on_destruct = function(pos) - -- Remove boxes and deactivate elevators below us. - elevator.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, -}) -- cgit v1.2.3