------------------------------------------------------------------------------------------------------------------------------------ -- BASIC MACHINES MOD by rnd -- mod with basic simple automatization for minetest. No background processing, just one abm with 5s timer (clock generator), no other lag causing background processing. ------------------------------------------------------------------------------------------------------------------------------------ -- *** SETTINGS *** -- basic_machines.timer = 5 -- main timestep basic_machines.machines_minstep = 1 -- minimal allowed activation timestep, if faster machines overheat basic_machines.max_range = 10 -- machines normal range of operation basic_machines.machines_operations = 10 -- 1 coal will provide 10 mover basic operations ( moving dirt 1 block distance) basic_machines.machines_TTL = 16 -- time to live for signals, how many hops before signal dissipates basic_machines.version = "12/04/2018a" basic_machines.clockgen = 1 -- if 0 all background continuously running activity (clockgen/keypad) repeating is disabled -- how hard it is to move blocks, default factor 1, note fuel cost is this multiplied by distance and divided by machine_operations.. basic_machines.hardness = { ["default:stone"] = 4, ["default:tree"] = 2, ["default:jungletree"] = 2, ["default:pine_tree"] = 2, ["default:aspen_tree"] = 2, ["default:acacia_tree"] = 2, ["default:lava_source"] = 5950, ["default:water_source"] = 5950, ["default:river_water_source"] = 5950, ["default:obsidian"] = 20, ["bedrock2:bedrock"] = 999999, ["spawners_env:mobs_spider_spawner"] = 999999, ["spawners_env:mobs_spider_spawner_active"] = 999999, ["spawners_env:mobs_spider_spawner_waiting"] = 999999, ["spawners_env:spawners_mobs_uruk_hai_spawner"] = 999999, ["spawners_env:spawners_mobs_uruk_hai_spawner_active"] = 999999, ["spawners_env:spawners_mobs_uruk_hai_spawner_waiting"] = 999999, ["telemosaic:extender_one"] = 999999, ["telemosaic:extender_two"] = 999999, ["telemosaic:extender_three"] = 999999, ["telemosaic:beacon_off"] = 999999, ["telemosaic:beacon"] = 999999, ["telemosaic:beacon_err"] = 999999, ["farming_addons:cocoa_1"] = 999999, ["farming_addons:cocoa_2"] = 999999, ["farming_addons:cocoa_3"] = 999999, ["x_default:treasure_chest"] = 999999, -- move machines for free ["basic_machines:mover"] = 0, ["basic_machines:keypad"] = 0, ["basic_machines:distributor"] = 0, ["basic_machines:battery"] = 0, ["basic_machines:detector"] = 0, ["basic_machines:generator"] = 0, ["basic_machines:clockgen"] = 0, ["basic_machines:ball_spawner"] = 0, ["basic_machines:light_on"] = 0, ["basic_machines:light_off"] = 0, ["farming:wheat_8"] = 1, ["farming:cotton_8"] = 1, ["farming:seed_wheat"] = 0.5, ["farming:seed_cotton"] = 0.5, } -- define which nodes are dug up completely, like a tree basic_machines.dig_up_table = { ["default:cactus"] = true, ["default:tree"] = true, ["default:jungletree"] = true, ["default:pine_tree"] = true, ["default:acacia_tree"] = true, ["default:aspen_tree"] = true, ["default:papyrus"] = true } -- set up nodes for harvest when digging: [nodename] = {what remains after harvest, harvest result} basic_machines.harvest_table = { ["mese_crystals:mese_crystal_ore4"] = {"mese_crystals:mese_crystal_ore1", "default:mese_crystal 3"}, -- harvesting mese crystals ["mese_crystals:mese_crystal_ore3"] = {"mese_crystals:mese_crystal_ore1", "default:mese_crystal 2"}, ["mese_crystals:mese_crystal_ore2"] = {"mese_crystals:mese_crystal_ore1", "default:mese_crystal 1"}, ["mese_crystals:mese_crystal_ore1"] = {"mese_crystals:mese_crystal_ore1", ""}, } -- list of objects that cant be teleported with mover basic_machines.no_teleport_table = { ["itemframes:item"] = true, ["signs:text"] = true, ["spawners_mobs:dummy_mobs_sheep_white"] = true, ["spawners_mobs:dummy_mobs_chicken"] = true, ["spawners_mobs:dummy_mobs_cow"] = true, ["spawners_mobs:dummy_mobs_oerkki"] = true, ["spawners_mobs:dummy_mobs_pumba"] = true, ["spawners_mobs:dummy_mobs_spider"] = true, ["spawners_mobs:dummy_mobs_stone_monster"] = true, ["spawners_mobs:dummy_mobs_tree_monster"] = true, ["spawners_mobs:dummy_spawners_mobs_balrog"] = true, ["spawners_mobs:dummy_spawners_mobs_bunny_evil"] = true, ["spawners_mobs:dummy_spawners_mobs_mummy"] = true, ["spawners_mobs:dummy_spawners_mobs_uruk_hai"] = true, ["spawners_ores:dummy_ore_stone_with_tin"] = true, ["spawners_ores:dummy_ore_stone_with_iron"] = true, ["spawners_ores:dummy_ore_stone_with_gold"] = true, ["spawners_ores:dummy_ore_stone_with_copper"] = true, ["spawners_env:dummy_spawners_mobs_uruk_hai"] = true, ["spawners_env:dummy_mobs_spider"] = true, ["spawners_env:dummy_spawners_mobs_balrog"] = true, ["xdecor:book_open"] = true } -- list of nodes mover cant take from in inventory mode basic_machines.limit_inventory_table = { -- node name = {list of bad inventories to take from} ["basic_machines:autocrafter"] = {["recipe"] = 1, ["output"] = 1}, ["basic_machines:constructor"] = {["recipe"] = 1}, ["basic_machines:battery_0"] = {["upgrade"] = 1}, ["basic_machines:battery_1"] = {["upgrade"] = 1}, ["basic_machines:battery_2"] = {["upgrade"] = 1}, ["basic_machines:generator"] = {["upgrade"] = 1}, ["basic_machines:mover"] = {["upgrade"] = 1}, ["moreblocks:circular_saw"] = {["input"] = 1, ["recycle"] = 1, ["micro"] = 1, ["output"] = 1 }, ["easyvend:depositor"] = {["item"] = 1, ["gold"] = 1}, ["easyvend:vendor"] = {["item"] = 1, ["gold"] = 1}, ["easyvend:depositor_on"] = {["item"] = 1, ["gold"] = 1}, ["easyvend:vendor_on"] = {["item"] = 1, ["gold"] = 1}, ["x_default:treasure_chest"] = {["main"] = 1}, } -- when activated with keypad these will be "punched" to update their text too basic_machines.signs = { ["default:sign_wall_wood"] = true, ["signs:sign_wall_green"] = true, ["signs:sign_wall_green"] = true, ["signs:sign_wall_yellow"] = true, ["signs:sign_wall_red"] = true, ["signs:sign_wall_red"] = true, ["signs:sign_wall_white_black"] = true, ["signs:sign_yard"] = true } -- *** END OF SETTINGS *** -- local machines_timer = basic_machines.timer local machines_minstep = basic_machines.machines_minstep local max_range = basic_machines.max_range local machines_operations = basic_machines.machines_operations local machines_TTL = basic_machines.machines_TTL local punchset = {} minetest.register_on_joinplayer(function(player) local name = player:get_player_name() if name == nil then return end punchset[name] = {} punchset[name].state = 0 end) local get_mover_form = function(pos,player) if not player then return end local meta = minetest.get_meta(pos); local x0, y0, z0, x1, y1, z1, x2, y2, z2, prefer, mode, mreverse -- pos1 x0 = meta:get_int("x0") y0 = meta:get_int("y0") z0 = meta:get_int("z0") -- pos11 x1 = meta:get_int("x1") y1 = meta:get_int("y1") z1 = meta:get_int("z1") -- pos2 x2 = meta:get_int("x2") y2 = meta:get_int("y2") z2 = meta:get_int("z2") machines.pos1[player:get_player_name()] = {x = pos.x + x0, y = pos.y + y0, z = pos.z + z0} machines.mark_pos1(player:get_player_name()) -- mark pos1 machines.pos11[player:get_player_name()] = {x = pos.x + x1, y = pos.y + y1, z = pos.z + z1} machines.mark_pos11(player:get_player_name()) -- mark pos11 machines.pos2[player:get_player_name()] = {x = pos.x + x2, y = pos.y + y2, z = pos.z + z2} machines.mark_pos2(player:get_player_name()) -- mark pos2 prefer = meta:get_string("prefer") local mreverse = meta:get_int("reverse") local list_name = "nodemeta:"..pos.x..","..pos.y..","..pos.z local mode_list = { ["normal"] = 1, ["dig"] = 2, ["drop"] = 3, ["object"] = 4, ["inventory"] = 5, ["transport"] = 6 } local mode_string = meta:get_string("mode") or "" -- source meta local meta1 = minetest.get_meta({x = pos.x + x0, y = pos.y + y0, z = pos.z + z0}) -- target meta local meta2 = minetest.get_meta({x = pos.x + x2, y = pos.y + y2, z = pos.z + z2}) local inv1 = 1 local inv2 = 1 local inv1m = meta:get_string("inv1") local inv2m = meta:get_string("inv2") local list1 = meta1:get_inventory():get_lists() local inv_list1 = "" -- stupid dropdown requires item index but returns string on receive so we have to find index.. grrr, one other solution: invert the table: key <-> value local j = 1 for i in pairs( list1) do inv_list1 = inv_list1 .. i .. "," if i == inv1m then inv1 = j end j = j + 1 end local list2 = meta2:get_inventory():get_lists() local inv_list2 = "" j = 1 if list2 then for i in pairs( list2) do inv_list2 = inv_list2 .. i .. "," if i == inv2m then inv2 = j end j = j + 1 end end local upgrade = meta:get_float("upgrade") if upgrade > 0 then upgrade = upgrade - 1 end if upgrade > 10 then upgrade = 10 meta:set_float("upgrade", 10) end local seltab = meta:get_int("seltab") local form -- MODE -- if seltab == 1 then local mode_description = { ["normal"] = "This will move blocks as they are - without change.", ["dig"] = "This will transform blocks as if player digged them.", ["drop"] = "This will take block/item out of chest (you need to set filter) and will drop it", ["object"] = "make TELEPORTER/ELEVATOR. This will move any object inside sphere (with center source1 and radius defined by source2) to target position. For ELEVATOR teleport points need to be placed exactly vertically in line with mover and you need to upgrade with 1 diamondblock for every 100 height difference. ", ["inventory"] = "This will move items from inventory of any block at source position to any inventory of block at target position", ["transport"] = "This will move all blocks at source area to new area starting at target position. This mode preserves all inventories and other metadata", } local text = mode_description[mode_string] or "description" local mode_list = { ["normal"] = 1, ["dig"] = 2, ["drop"] = 3, ["object"] = 4, ["inventory"] = 5, ["transport"] = 6 } mode = mode_list[mode_string] or 1 form = "size[8,8.25]" .. -- width, height --"size[6,10]" .. -- width, height default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "tabheader[0,0;tabs;MODE OF OPERATION,WHERE TO MOVE;".. seltab .. ";true;true]".. "label[0.,0;MODE selection]".."button[3,0.25;1,1;help;help]".. "dropdown[0.,0.35;3,1;mode;normal,dig,drop,object,inventory,transport;".. mode .."]".. "textarea[0.25,1.25;8,2.;description;;".. text.."]".. "field[0.25,3.5;3,1;prefer;FILTER;"..prefer.."]".. "list[nodemeta:"..pos.x..','..pos.y..','..pos.z ..";filter;3,3.4;1,1;]".. "list[nodemeta:"..pos.x..','..pos.y..','..pos.z ..";upgrade;5,3.4;1,1;]".."label[4,3;UPGRADE LVL ".. upgrade .."]" .. "list[current_player;main;0,4.5;8,4;]".. "listring[nodemeta:"..pos.x..','..pos.y..','..pos.z ..";upgrade]".. "listring[current_player;main]".. "listring[nodemeta:"..pos.x..','..pos.y..','..pos.z ..";filter]".. "listring[current_player;main] button_exit[5,0.25;1,1;OK;OK]" else -- POSITIONS local inventory_list1, inventory_list2 if mode_string == "inventory" then inventory_list1 = "label[4.5,0.25;source inventory] dropdown[4.5,0.75;1.5,1;inv1;".. inv_list1 ..";" .. inv1 .."]" inventory_list2 = "label[4.5,3.;target inventory] dropdown[4.5,3.5;1.5,1;inv2;".. inv_list2 .. ";" .. inv2 .."]" else inventory_list1 = "" inventory_list2 = "" end form = "size[6,5.5]" .. -- width, height --"size[6,10]" .. -- width, height default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "tabheader[0,0;tabs;MODE OF OPERATION,WHERE TO MOVE;".. seltab .. ";true;true]".. "label[0.,0;" .. minetest.colorize("lawngreen","INPUT AREA - mover will dig here").."]".. "field[0.25,1.;1,1;x0;source1;"..x0.."] field[1.25,1.;1,1;y0;;"..y0.."] field[2.25,1.;1,1;z0;;"..z0.."]".. "image[3,0.75;1,1;machines_pos1.png]".. inventory_list1.. "field[0.25,2;1,1;x1;source2;"..x1.."] field[1.25,2;1,1;y1;;"..y1.."] field[2.25,2;1,1;z1;;"..z1.."]".. "image[3,1.75;1,1;machines_pos11.png]".. "label[0.,2.75;" .. minetest.colorize("red","TARGET POSITION - mover will move to here").."]".. "field[0.25,3.75;1,1;x2;Target;"..x2.."] field[1.25,3.75;1,1;y2;;"..y2.."] field[2.25,3.75;1,1;z2;;"..z2.."]".. "image[3,3.5;1,1;machines_pos2.png]".. inventory_list2 .. "label[0.,4.25;REVERSE source and target (0/1/2)]".. "field[0.25,5;1.,1;reverse;;"..mreverse.."]" .. "listring[current_player;main] button[4,4.75;1,1;help;help] button_exit[5,4.75;1,1;OK;OK]" end return form end local find_and_connect_battery = function(pos) local r = 1 for i = 0, 2 do local positions = minetest.find_nodes_in_area( --find battery {x = pos.x - r, y = pos.y - r, z = pos.z - r}, {x = pos.x + r, y = pos.y + r, z = pos.z + r}, "basic_machines:battery_" .. i ) if #positions > 0 then local meta = minetest.get_meta(pos) local fpos = positions[1] meta:set_int("batx", fpos.x) meta:set_int("baty", fpos.y) meta:set_int("batz", fpos.z) return fpos end -- pick first battery we found end return nil end -- MOVER -- minetest.register_node("basic_machines:mover", { description = "Mover - universal digging/harvesting/teleporting/transporting machine, its upgradeable.", tiles = {"basic_machine_mover_side.png"}, groups = {cracky = 3, mesecon_effector_on = 1}, sounds = default.node_sound_wood_defaults(), after_place_node = function(pos, placer) local meta = minetest.env:get_meta(pos) meta:set_string("infotext", "Mover block. Set it up by punching or right click. Activate it by keypad signal.") meta:set_string("owner", placer:get_player_name()) meta:set_int("public", 0) meta:set_int("x0", 0) meta:set_int("y0", -1) -- source1 meta:set_int("z0", 0) meta:set_int("x1", 0) meta:set_int("y1", -1) -- source2: defines cube meta:set_int("z1", 0) meta:set_int("pc", 0) -- current cube position and dimensions meta:set_int("dim", 1) meta:set_int("pc", 0) -- current cube position and dimensions meta:set_int("dim", 1) meta:set_int("x2", 0) meta:set_int("y2", 1) meta:set_int("z2", 0) meta:set_float("fuel", 0) meta:set_string("prefer", "") meta:set_string("mode", "normal") meta:set_float("upgrade", 1) meta:set_int("seltab", 1) local privs = minetest.get_player_privs(placer:get_player_name()) -- means operation will be for free if privs.privs then meta:set_float("upgrade", -1) end local inv = meta:get_inventory() inv:set_size("upgrade", 1 * 1) inv:set_size("filter", 1 * 1) local name = placer:get_player_name() punchset[name].state = 0 local text = "This machine can move anything. General idea is the following : \n\n".. "First you need to define rectangle work area (where it takes, marked by two number 1 boxes that appear in world) and target area (where it puts, marked by one number 2 box) by punching mover then following CHAT instructions exactly.\n\n".. "CHECK why it doesnt work: 1. did you click OK in mover after changing setting 2. does it have battery, 3. does battery have enough fuel\n\n".. "IMPORTANT: Please read the help button inside machine before first use." local form = "size [5.5,5.5] textarea[0,0;6,7;help;MOVER INTRODUCTION;".. text.."]" -- minetest.show_formspec(name, "basic_machines:intro_mover", form) end, -- dont dig if upgrades inside, cause they will be destroyed can_dig = function(pos, player) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() return inv:is_empty("upgrade") end, on_rightclick = function(pos, node, player, itemstack, pointed_thing) local privs = minetest.get_player_privs(player:get_player_name()) local cant_build = minetest.is_protected(pos,player:get_player_name()) -- only ppl sharing protection can setup if not privs.privs and cant_build then return end local form = get_mover_form(pos, player) minetest.show_formspec(player:get_player_name(), "basic_machines:mover_"..minetest.pos_to_string(pos), form) end, allow_metadata_inventory_put = function(pos, listname, index, stack, player) if minetest.is_protected(pos, player:get_player_name()) then minetest.record_protection_violation(pos, player:get_player_name()) return 0 end local stack_name = stack:get_name() if listname == "filter" then local meta = minetest.get_meta(pos) local form = get_mover_form(pos, player) meta:set_string("prefer", stack_name) minetest.show_formspec(player:get_player_name(), "basic_machines:mover_"..minetest.pos_to_string(pos), form) return 1 end if listname == "upgrade" then if not (stack_name == "default:diamondblock" or stack_name == "default:mese") then return 0 end end return stack:get_count() end, on_metadata_inventory_put = function(pos, listname, index, stack, player) if listname == "upgrade" then -- update upgrades local meta = minetest.get_meta(pos) local upgrade = 0 local inv = meta:get_inventory() local stack_name = stack:get_name() local upgrade_name = "default:mese" -- check if upgrade should be for elevator if meta:get_int("elevator") == 1 then upgrade_name = "default:diamondblock" end -- add upgrades if stack_name == upgrade_name then upgrade = inv:get_stack(listname, index):get_count() or 0 -- not more than 10 if upgrade > 10 then upgrade = 10 end meta:set_float("upgrade", upgrade + 1) local form = get_mover_form(pos, player) minetest.show_formspec(player:get_player_name(), "basic_machines:mover_"..minetest.pos_to_string(pos), form) -- reset upgrade level when switching the item else meta:set_float("upgrade", 1) local form = get_mover_form(pos, player) minetest.show_formspec(player:get_player_name(), "basic_machines:mover_"..minetest.pos_to_string(pos), form) end end end, allow_metadata_inventory_take = function(pos, listname, index, stack, player) local meta = minetest.get_meta(pos) -- reset upgrade meta:set_float("upgrade", 1) local form = get_mover_form(pos, player) minetest.show_formspec(player:get_player_name(), "basic_machines:mover_"..minetest.pos_to_string(pos), form) return stack:get_count() end, mesecons = { effector = { action_on = function(pos, node,ttl) if type(ttl) ~= "number" then ttl = 1 end local meta = minetest.get_meta(pos) local fuel = meta:get_float("fuel") local x0 = meta:get_int("x0") local y0 = meta:get_int("y0") local z0 = meta:get_int("z0") local x2 = meta:get_int("x2") local y2 = meta:get_int("y2") local z2 = meta:get_int("z2") local mode = meta:get_string("mode") local mreverse = meta:get_int("reverse") -- where to take from local pos1 = {x = x0 + pos.x, y = y0 + pos.y, z = z0 + pos.z} -- where to put local pos2 = {x = x2 + pos.x, y = y2 + pos.y, z = z2 + pos.z} local pc = meta:get_int("pc") local dim = meta:get_int("dim") pc = (pc+1) % dim -- cycle position meta:set_int("pc", pc) -- get dimensions local x1 = meta:get_int("x1") - x0 + 1 local y1 = meta:get_int("y1") - y0 + 1 local z1 = meta:get_int("z1") - z0 + 1 --pc = z*a*b+x*b+y, from x,y,z to pc -- set current input position pos1.y = y0 + (pc % y1) pc = (pc - (pc % y1)) / y1 pos1.x = x0 + (pc % x1) pc = (pc - (pc % x1)) / x1 pos1.z = z0 + pc pos1.x = pos.x + pos1.x pos1.y = pos.y + pos1.y pos1.z = pos.z + pos1.z -- special modes that use its own source/target positions: if mode == "transport" and mreverse < 2 then -- translation from pos1 pos2 = { x = meta:get_int("x2") - x0 + pos1.x, y = meta:get_int("y2") - y0 + pos1.y, z = meta:get_int("z2") - z0 + pos1.z } end -- reverse pos1, pos2 if mreverse ~= 0 and mreverse ~= 2 then if mode == "object" then x0 = pos2.x - pos.x y0 = pos2.y - pos.y z0 = pos2.z - pos.z pos2 = {x = pos1.x, y = pos1.y, z = pos1.z} else local post = {x = pos1.x, y = pos1.y, z = pos1.z} pos1 = {x = pos2.x, y = pos2.y, z = pos2.z} pos2 = {x = post.x, y = post.y, z = post.z} end end -- PROTECTION CHECK local owner = meta:get_string("owner") if (minetest.is_protected(pos1, owner) or minetest.is_protected(pos2, owner)) and mode ~= "object" then meta:set_string("infotext", "Mover block. Protection fail.") return end local node1 = minetest.get_node(pos1) local node2 = minetest.get_node(pos2) local prefer = meta:get_string("prefer") -- prevent mover from working when player offline and reverse seed planting is enabled if mode == "normal" or mode == "dig" then local placer = minetest.get_player_by_name(owner) local tempprefer = prefer if tempprefer == "" then tempprefer = ":" end local name_parts = tempprefer:split(":") if not placer and not (node2.name == "default:chest" or node2.name == "default:chest_locked") and (name_parts[1] == "farming" or name_parts[1] == "farming_addons") then return end end -- FUEL COST: calculate local dist = math.abs(pos2.x - pos1.x) + math.abs(pos2.y - pos1.y) + math.abs(pos2.z - pos1.z) local hardness = basic_machines.hardness[node1.name] -- no free teleports from machine blocks if hardness == 0 and mode == "object" then hardness = 1 end local fuel_cost = hardness or 1 local upgrade = meta:get_float("upgrade") or 1 -- taking items from chests/inventory move if node1.name == "default:chest_locked" or mode == "inventory" then fuel_cost = basic_machines.hardness[prefer] or 1 end fuel_cost = fuel_cost * dist / machines_operations -- machines_operations=10 by default, so 10 basic operations possible with 1 coal if mode == "object" then fuel_cost = fuel_cost * 0.1 -- check if elevator mode if x2 == 0 and z2 == 0 then local requirement = math.floor(math.abs(pos2.y - pos.y) / 100) + 1 if upgrade - 1 < requirement then meta:set_string("infotext","MOVER: Elevator error. Need at least "..requirement .. " diamond block(s) in upgrade (1 for every 100 height). ") return end fuel_cost = 0 end elseif mode == "inventory" then fuel_cost = fuel_cost * 0.1 end -- upgrade decreases fuel cost fuel_cost = fuel_cost / upgrade -- free operation for admin if upgrade == -1 then fuel_cost = 0 end -- FUEL OPERATIONS -- needs fuel to operate, find nearby battery if fuel < fuel_cost then local found_fuel = 0 -- cached battery position local fpos = { x = meta:get_int("batx"), y = meta:get_int("baty"), z = meta:get_int("batz") } -- check battery for power local power_draw = fuel_cost -- at least 10 one block operations with 1 refuel if power_draw < 1 then power_draw = 1 end local supply = basic_machines.check_power(fpos, power_draw) if supply > 0 then found_fuel = supply -- no battery at target location, try to find it! elseif supply < 0 then local fpos = find_and_connect_battery(pos) if not fpos then meta:set_string("infotext", "Can not find nearby battery to connect to!") minetest.sound_play("default_cool_lava", { pos = pos, gain = 1.0, max_hear_distance = 8 }) return end end if found_fuel ~= 0 then fuel = fuel + found_fuel meta:set_float("fuel", fuel) meta:set_string("infotext", "Mover block refueled. Fuel ".. fuel) end end if fuel < fuel_cost then meta:set_string("infotext", "Mover block. Energy ".. fuel ..", needed energy " .. fuel_cost .. ". Put nonempty battery next to mover.") return end -- teleport objects and return if mode == "object" then -- if target is chest put items in it local target_chest = false if node2.name == "default:chest" or node2.name == "default:chest_locked" then target_chest = true end local r = math.max(math.abs(x1), math.abs(y1), math.abs(z1)) r = math.min(r, 10) local teleport_any = false -- put objects in target chest if target_chest then local cmeta = minetest.get_meta(pos2) local inv = cmeta:get_inventory() for _,obj in pairs(minetest.get_objects_inside_radius({x=x0+pos.x,y=y0+pos.y,z=z0+pos.z}, r)) do local lua_entity = obj:get_luaentity() if not obj:is_player() and lua_entity and lua_entity.itemstring ~= "" then local detected_obj = lua_entity.name or "" -- object on no teleport list if not basic_machines.no_teleport_table[detected_obj] then -- put item in chest local stack = ItemStack(lua_entity.itemstring) if inv:room_for_item("main", stack) then teleport_any = true inv:add_item("main", stack) end -- patch for dupe, might not be needed if future minetest object management is better obj:setpos({x = 0, y = 0, z = 0}) obj:remove() end end end if teleport_any then fuel = fuel - fuel_cost meta:set_float("fuel", fuel) meta:set_string("infotext", "Mover block. Fuel "..fuel) minetest.sound_play("tng_transporter1", { pos = pos2, gain = 1.0, max_hear_distance = 8 }) end return end local times = basic_machines.tonumber(prefer) or 0 if times > 20 then times = 20 elseif times < 0.2 then times = 0 end local velocityv if times ~= 0 then velocityv = { x = pos2.x - x0 - pos.x, y = pos2.y - y0 - pos.y, z = pos2.z - z0 - pos.z } local vv = math.sqrt(velocityv.x * velocityv.x + velocityv.y * velocityv.y + velocityv.z * velocityv.z) local velocitys = 0 if times ~= 0 then velocitys = vv/times else vv = 0 end if vv ~= 0 then vv = velocitys/vv else vv = 0 end velocityv.x = velocityv.x * vv; velocityv.y = velocityv.y * vv; velocityv.z = velocityv.z* vv end --minetest.chat_send_all(" times ".. times .. " v " .. minetest.pos_to_string(velocityv)); -- move objects to another location local finalsound = true for _, obj in pairs(minetest.get_objects_inside_radius({x=x0+pos.x,y=y0+pos.y,z=z0+pos.z}, r)) do if obj:is_player() then -- move player only from owners land if not minetest.is_protected(obj:getpos(), owner) and (prefer == "" or obj:get_player_name() == prefer) then obj:moveto(pos2, false) teleport_any = true end else local lua_entity = obj:get_luaentity() local detected_obj = lua_entity.name or "" -- object on no teleport list if not basic_machines.no_teleport_table[detected_obj] then if times > 0 then local finalmove = true -- move objects with set velocity in target direction obj:setvelocity(velocityv); -- interaction with objects like carts if obj:get_luaentity() then if lua_entity.name then -- move balls for free if lua_entity.name == "basic_machines:ball" then lua_entity.velocity = {x=velocityv.x*times,y=velocityv.y*times,z=velocityv.z*times} finalmove = false finalsound = false end -- just accelerate cart if lua_entity.name == "carts:cart" then lua_entity.velocity = {x=velocityv.x*times,y=velocityv.y*times,z=velocityv.z*times} fuel = fuel - fuel_cost; meta:set_float("fuel",fuel) meta:set_string("infotext", "Mover block. Fuel "..fuel) return end end end -- dont move objects like balls to destination after delay if finalmove then minetest.after(times, function() if obj then obj:setvelocity({x = 0, y = 0, z = 0}) obj:moveto(pos2, false) end end) end else obj:moveto(pos2, false) end end teleport_any = true end end if teleport_any then fuel = fuel - fuel_cost meta:set_float("fuel", fuel) meta:set_string("infotext", "Mover block. Fuel "..fuel) if finalsound then minetest.sound_play("tng_transporter1", { pos = pos2, gain = 1.0, max_hear_distance = 8 }) end end return end local dig = false -- digs at target location if mode == "dig" then dig = true end local drop = false -- drops node instead of placing it if mode == "drop" then drop = true end -- harvest mode for special nodes: mese crystals local harvest = false -- decide what to do if source or target are chests local source_chest = false if string.find(node1.name, "default:chest") then source_chest = true end -- nothing to move if node1.name == "air" then return end local target_chest = false if node2.name == "default:chest" or node2.name == "default:chest_locked" then target_chest = true end -- do nothing if target nonempty and not chest if not target_chest and not (mode == "inventory") and minetest.get_node(pos2).name ~= "air" then return end local invName1 = "" local invName2 = "" if mode == "inventory" then invName1 = meta:get_string("inv1") invName2 = meta:get_string("inv2") -- reverse inventory names too if mreverse == 1 then local invNamet = invName1 invName1 = invName2 invName2 = invNamet end end -- inventory mode if mode == "inventory" then --if prefer == "" then meta:set_string("infotext", "Mover block. must set nodes to move (filter) in inventory mode."); return; end -- forbidden nodes to take from in inventory mode - to prevent abuses if basic_machines.limit_inventory_table[node1.name] then -- forbidden to take from this inventory if basic_machines.limit_inventory_table[node1.name][invName1] then return end end local stack, meta1, inv1 -- if prefer == "" then just pick one item from chest to transfer if prefer == "" then meta1 = minetest.get_meta(pos1) inv1 = meta1:get_inventory() -- nothing to move if inv1:is_empty(invName1) then return end local size = inv1:get_size(invName1) local found = false -- find item to move in inventory for i = 1, size do stack = inv1:get_stack(invName1, i) -- prevent item stacks with more than 'stack_max' items if stack:get_count() > stack:get_stack_max() then stack:set_count(stack:get_stack_max()) else stack:set_count(stack:get_count()) end if not stack:is_empty() then found = true break end end if not found then return end end -- can we move item to target inventory? if prefer ~= "" then meta1 = minetest.get_meta(pos1) inv1 = meta1:get_inventory() -- nothing to move if inv1:is_empty(invName1) then return end local size = inv1:get_size(invName1) local preferstack = ItemStack(prefer) local found = false -- find item to move in inventory for i = 1, size do stack = inv1:get_stack(invName1, i) if stack:get_name() == preferstack:get_name() then -- prevent item stacks with more than 'stack_max' items if preferstack:get_count() > preferstack:get_stack_max() then stack:set_count(preferstack:get_stack_max()) else stack:set_count(preferstack:get_count()) end found = true break end end if not found then return end end local meta2 = minetest.get_meta(pos2) local inv2 = meta2:get_inventory() if not inv2:room_for_item(invName2, stack) then return end -- add item to target inventory and remove item from source inventory if prefer ~= "" then meta1 = minetest.get_meta(pos1) inv1 = meta1:get_inventory() end if inv1:contains_item(invName1, stack) then inv2:add_item(invName2, stack) inv1:remove_item(invName1, stack) else -- admin is owner.. just add stuff if upgrade == -1 then inv2:add_item(invName2, stack) -- item not found in chest else return end end minetest.sound_play("chest_inventory_move", { pos = pos2, gain = 1.0, max_hear_distance = 8 }) fuel = fuel - fuel_cost meta:set_float("fuel", fuel) meta:set_string("infotext", "Mover block. Fuel "..fuel) return end -- filtering -- prefered node set if prefer ~= "" then -- only take prefered node or from chests/inventories if prefer ~= node1.name and not source_chest and mode ~= "inventory" then return end -- take stuff from chest if source_chest then local cmeta = minetest.get_meta(pos1) local inv = cmeta:get_inventory() local stack = ItemStack(prefer) -- prevent item stacks with more than 'stack_max' items if stack:get_count() > stack:get_stack_max() then stack:set_count(stack:get_stack_max()) else stack:set_count(stack:get_count()) end if inv:contains_item("main", stack) then inv:remove_item("main", stack) else return end end node1 = {} node1.name = prefer end -- doesnt know what to take out of chest/inventory if (prefer == "" and source_chest) then return end -- if target chest put in chest if target_chest then local cmeta = minetest.get_meta(pos2) local inv = cmeta:get_inventory() -- dig tree or cactus -- check for cactus or tree local count = 0 -- digs up node as a tree local dig_up = false if dig then if not source_chest and basic_machines.dig_up_table[node1.name] then dig_up = true end -- do we harvest the node? (only for mese crystals) if not source_chest then if basic_machines.harvest_table[node1.name] ~= nil then harvest = true local remains = basic_machines.harvest_table[node1.name][1] local result = basic_machines.harvest_table[node1.name][2] minetest.set_node(pos1,{name = remains}) inv:add_item("main", result) end end -- dig up to 16 nodes if dig_up == true then local r = 1 if node1.name == "default:cactus" or node1.name == "default:papyrus" then r = 0 end -- acacia trees grow wider than others if node1.name == "default:acacia_tree" then r = 2 end local positions = minetest.find_nodes_in_area({x = pos1.x - r, y = pos1.y, z = pos1.z - r}, {x = pos1.x + r, y = pos1.y + 16, z = pos1.z + r}, node1.name) local def = minetest.registered_nodes[node1.name] local distance_ok = vector.distance(pos1, pos2) <= 16 local texture = "default_dirt.png" -- try to find a node texture if def then if def.tiles then if #def.tiles > 0 then if type(def.tiles[1]) == "string" then texture = def.tiles[1] end end end end for _, pos3 in ipairs(positions) do minetest.set_node(pos3, {name = "air"}) count = count + 1 -- add particles only when not too far if distance_ok then minetest.add_particlespawner({ amount = math.random(1, 3), time = 0.5, minpos = {x=pos3.x-0.7, y=pos3.y, z=pos3.z-0.7}, maxpos = {x=pos3.x+0.7, y=pos3.y+0.75, z=pos3.z+0.7}, minvel = {x = -0.5, y = -4, z = -0.5}, maxvel = {x = 0.5, y = -2, z = 0.5}, minacc = {x = -0.5, y = -4, z = -0.5}, maxacc = {x = 0.5, y = -2, z = 0.5}, minexptime = 0.5, maxexptime = 1, minsize = 0.5, maxsize = 2, collisiondetection = true, texture = texture }) end end -- if tree or cactus was digged up inv:add_item("main", node1.name.." "..count - 1) end -- minetest drop code emulation if not harvest then local table = minetest.registered_items[node1.name] if table ~= nil then --put in chest if table.drop ~= nil then -- drop handling if table.drop.items then --handle drops better, emulation of drop code local max_items = table.drop.max_items or 0 if max_items == 0 then -- just drop all the items (taking the rarity into consideration) max_items = #table.drop.items or 0 end local drop = table.drop local i = 0 for k, v in pairs(drop.items) do if i > max_items then break end i = i + 1 local rare = v.rarity or 1 if math.random(1, rare) == 1 then node1 = {} -- pick item randomly from list node1.name = v.items[math.random(1, #v.items)] inv:add_item("main",node1.name) end end else inv:add_item("main", table.drop) end else inv:add_item("main", node1.name) end end end -- if not dig just put it in else inv:add_item("main", node1.name) end end minetest.sound_play("transporter", { pos = pos2, gain = 1.0, max_hear_distance = 8 }) -- chest to chest transport has lower cost, *0.1 if target_chest and source_chest then fuel_cost = fuel_cost * 0.1 end fuel = fuel - fuel_cost meta:set_float("fuel", fuel) meta:set_string("infotext", "Mover block. Fuel "..fuel) -- transport nodes parallel as defined by source1 and target, clone with complete metadata if mode == "transport" then local meta1 = minetest.get_meta(pos1):to_table() minetest.set_node(pos2, minetest.get_node(pos1)) minetest.get_meta(pos2):from_table(meta1) minetest.set_node(pos1,{name = "air"}) minetest.get_meta(pos1):from_table(nil) return end -- REMOVE DIGGED NODE if not target_chest then if not drop then -- get node (seed) table definition local def = minetest.registered_nodes[node1.name] local name_parts = node1.name:split(":") -- create pointed_thing table local pointed_thing = { type = "node", above = { y = pos2.y, x = pos2.x, z = pos2.z }, under = { y = pos2.y - 1, x = pos2.x, z = pos2.z } } -- create ItemStack table local stack = ItemStack(node1.name) local placer = minetest.get_player_by_name(owner) -- use default behaviour on_place if found in def if def and def.on_place and placer ~= nil and (name_parts[1] == "farming" or name_parts[1] == "farming_addons" ) then def.on_place(stack, placer, pointed_thing) -- add particles only when not too far if vector.distance(pos1, pos2) <= 16 then local texture = "default_dirt.png" -- try to find a node texture if def.tiles then if #def.tiles > 0 then if type(def.tiles[1]) == "string" then texture = def.tiles[1] end end end minetest.add_particlespawner({ amount = math.random(3, 6), time = 0.5, minpos = {x=pos2.x-0.7, y=pos2.y, z=pos2.z-0.7}, maxpos = {x=pos2.x+0.7, y=pos2.y+0.75, z=pos2.z+0.7}, minvel = {x = -0.5, y = -4, z = -0.5}, maxvel = {x = 0.5, y = -2, z = 0.5}, minacc = {x = -0.5, y = -4, z = -0.5}, maxacc = {x = 0.5, y = -2, z = 0.5}, minexptime = 0.5, maxexptime = 1, minsize = 0.5, maxsize = 2, collisiondetection = true, texture = texture }) end -- on_place not found in def - use set_node instead else minetest.set_node(pos2, {name = node1.name}) if def then -- add particles only when not too far if vector.distance(pos1, pos2) <= 16 then local texture = "default_dirt.png" -- try to find a node texture if def.tiles then if #def.tiles > 0 then if type(def.tiles[1]) == "string" then texture = def.tiles[1] end end end minetest.add_particlespawner({ amount = math.random(3, 6), time = 0.5, minpos = {x=pos2.x-0.7, y=pos2.y, z=pos2.z-0.7}, maxpos = {x=pos2.x+0.7, y=pos2.y+0.75, z=pos2.z+0.7}, minvel = {x = -0.5, y = -4, z = -0.5}, maxvel = {x = 0.5, y = -2, z = 0.5}, minacc = {x = -0.5, y = -4, z = -0.5}, maxacc = {x = 0.5, y = -2, z = 0.5}, minexptime = 0.5, maxexptime = 1, minsize = 0.5, maxsize = 2, collisiondetection = true, texture = texture }) end end end end if drop then local stack = ItemStack(node1.name) -- drops it minetest.add_item(pos2,stack) end end if not (source_chest) and not (harvest) then if dig then minetest.check_for_falling(pos1) end local def = minetest.registered_nodes[node1.name] minetest.set_node(pos1, {name = "air"}) if def then -- add particles only when not too far if vector.distance(pos1, pos2) <= 16 then local texture = "default_dirt.png" -- try to find a node texture if def.tiles then if #def.tiles > 0 then if type(def.tiles[1]) == "string" then texture = def.tiles[1] end end end minetest.add_particlespawner({ amount = math.random(3, 6), time = 0.5, minpos = {x=pos1.x-0.5, y=pos1.y, z=pos1.z-0.5}, maxpos = {x=pos1.x+0.5, y=pos1.y+0.75, z=pos1.z+0.5}, minvel = {x = -0.5, y = -4, z = -0.5}, maxvel = {x = 0.5, y = -2, z = 0.5}, minacc = {x = -0.5, y = -4, z = -0.5}, maxacc = {x = 0.5, y = -2, z = 0.5}, minexptime = 0.5, maxexptime = 1, minsize = 0.5, maxsize = 2, collisiondetection = true, texture = texture }) end end end end, -- /action_on - mover -- this toggles reverse option of mover action_off = function(pos, node,ttl) if type(ttl) ~= "number" then ttl = 1 end local meta = minetest.get_meta(pos) local mreverse = meta:get_int("reverse") local mode = meta:get_string("mode") -- reverse switching is not very helpful when auto harvest trees for example if mode ~= "dig" then if mreverse == 1 then mreverse = 0 elseif mreverse == 0 then mreverse = 1 end meta:set_int("reverse", mreverse) end end } -- /effector - mover } }) -- /mover - register_node -- KEYPAD -- local function use_keypad(pos,ttl, again) -- position, time to live ( how many times can signal travel before vanishing to prevent infinite recursion ), do we want to activate again if ttl<0 then return end; local meta = minetest.get_meta(pos); local t0 = meta:get_int("t"); local t1 = minetest.get_gametime(); local T = meta:get_int("T"); -- temperature if t0>t1-machines_minstep then -- activated before natural time T=T+1; else if T>0 then T=T-1 if t1-t0>5 then T = 0 end end end meta:set_int("T",T); meta:set_int("t",t1); -- update last activation time if T > 2 then -- overheat minetest.sound_play("default_cool_lava",{pos = pos, max_hear_distance = 16, gain = 0.25}) meta:set_string("infotext","overheat: temperature ".. T) return end local name = meta:get_string("owner"); if minetest.is_protected(pos,name) then meta:set_string("infotext", "Protection fail. reset."); meta:set_int("count",0); return end local count = meta:get_int("count") or 0; -- counts how many repeats left local repeating = meta:get_int("repeating"); if repeating==1 and again~=1 then -- stop it meta:set_int("repeating",0); meta:set_int("count", 0) meta:set_int("T",4); meta:set_string("infotext", "#KEYPAD: reseting. Punch again after 5s to activate") return; end if count>0 then -- this is keypad repeating its activation count = count - 1; meta:set_int("count",count); else meta:set_int("repeating",0); --return end if count>=0 then meta:set_string("infotext", "Keypad operation: ".. count .." cycles left") else meta:set_string("infotext", "Keypad operation: activation ".. -count) end if count>0 then -- only trigger repeat if count on if repeating == 0 then meta:set_int("repeating",1); end-- its repeating now if basic_machines.clockgen==0 then return end minetest.after(machines_timer, function() use_keypad(pos,machines_TTL,1) end ) end local x0,y0,z0,mode; x0=meta:get_int("x0");y0=meta:get_int("y0");z0=meta:get_int("z0"); x0=pos.x+x0;y0=pos.y+y0;z0=pos.z+z0; mode = meta:get_int("mode"); -- pass the signal on to target, depending on mode local tpos = {x=x0,y=y0,z=z0}; -- target position local node = minetest.get_node(tpos);if not node.name then return end -- error local text = meta:get_string("text"); if text ~= "" then -- TEXT MODE; set text on target if text == "@" then -- keyboard mode, set text from input text = meta:get_string("input") or ""; meta:set_string("input",""); -- clear input again end local bit = string.byte(text); if bit == 33 then -- if text starts with !, then we send chat text to all nearby players, radius 5 text = string.sub(text,2) ; if not text or text == "" then return end local players = minetest.get_connected_players(); for _,player in pairs(players) do local pos1 = player:getpos(); local dist = math.sqrt((pos1.x-tpos.x)^2 + (pos1.y-tpos.y)^2 + (pos1.z-tpos.z)^2 ); if dist<=5 then minetest.chat_send_player(player:get_player_name(), text) end end return elseif bit == 36 then-- text starts with $, play sound text = string.sub(text,2) ; if not text or text == "" then return end minetest.sound_play(text, {pos=pos,gain=1.0,max_hear_distance = 16,}) end local tmeta = minetest.get_meta(tpos);if not tmeta then return end if basic_machines.signs[node.name] then -- update text on signs with signs_lib tmeta:set_string("infotext", text); tmeta:set_string("text",text); local table = minetest.registered_nodes[node.name]; if not table.on_punch then return end -- error if signs_lib and signs_lib.update_sign then --signs_lib.update_sign(pos) table.on_punch(tpos, node, nil); -- warning - this can cause problems if no signs_lib installed end return end -- target is keypad, special functions: @, % that output to target keypad text if node.name == "basic_machines:keypad" then -- special modify of target keypad text and change its target x0=tmeta:get_int("x0");y0=tmeta:get_int("y0");z0=tmeta:get_int("z0"); x0=tpos.x+x0;y0=tpos.y+y0;z0=tpos.z+z0; tpos = {x=x0,y=y0,z=z0}; if string.byte(text) == 64 then -- target keypad's text starts with @ ( ascii code 64) -> character replacement text = string.sub(text,2); if not text or text == "" then return end --read words[j] from blocks above keypad: local j=0; text = string.gsub(text, "@", function() j=j+1; return minetest.get_meta({x=pos.x,y=pos.y+j,z=pos.z}):get_string("infotext") end ) ; -- replace every @ in ttext with string on blocks above -- set target keypad's text --tmeta = minetest.get_meta(tpos);if not tmeta then return end tmeta:set_string("text", text); elseif string.byte(text) == 37 then -- target keypad's text starts with % ( ascii code 37) -> word extraction local ttext = minetest.get_meta({x=pos.x,y=pos.y+1,z=pos.z}):get_string("infotext") local i = basic_machines.tonumber(string.sub(text,2,2)) or 1; --read the number following the % --extract i-th word from text local j = 0; for word in string.gmatch(ttext, "%S+") do j=j+1; if j == i then text = word; break; end end -- set target keypad's target's text --tmeta = minetest.get_meta(tpos); if not tmeta then return end tmeta:set_string("text", text); else if string.byte(text) == 64 then -- if text starts with @ clear target keypad text tmeta:set_string("text",""); return end -- just set text.. --tmeta = minetest.get_meta(tpos); if not tmeta then return end tmeta:set_string("infotext", text); end return end if node.name == "basic_machines:detector" then -- change filter on detector if string.byte(text) == 64 then -- if text starts with @ clear the filter tmeta:set_string("node",""); else -- prevent item stacks with more than 'stack_max' items local textstack = ItemStack(text) if textstack:get_count() > textstack:get_stack_max() then textstack:set_count(textstack:get_stack_max()) else textstack:set_count(textstack:get_count()) end tmeta:set_string("node", textstack:to_string()); end return end if node.name == "basic_machines:mover" then -- change filter on mover if string.byte(text) == 64 then -- if text starts with @ clear the filter tmeta:set_string("prefer",""); else -- prevent item stacks with more than 'stack_max' items local textstack = ItemStack(text) if textstack:get_count() > textstack:get_stack_max() then textstack:set_count(textstack:get_stack_max()) else textstack:set_count(textstack:get_count()) end tmeta:set_string("prefer", textstack:to_string()); end return end if node.name == "basic_machines:distributor" then local i = string.find(text," "); if i then local ti = basic_machines.tonumber(string.sub(text,1,i-1)) or 1; local tm = basic_machines.tonumber(string.sub(text,i+1)) or 1; if ti>=1 and ti<=16 and tm>=-2 and tm<=2 then tmeta:set_int("active"..ti,tm) end end return end tmeta:set_string("infotext", text); -- else just set text end --activate target local table = minetest.registered_nodes[node.name]; if not table then return end -- error if not table.mesecons then return end -- error if not table.mesecons.effector then return end -- error local effector=table.mesecons.effector; if mode == 3 then -- keypad in toggle mode local state = meta:get_int("state") or 0;state = 1-state; meta:set_int("state",state); if state == 0 then mode = 1 else mode = 2 end end -- pass the signal on to target if mode == 2 then -- on if not effector.action_on then return end effector.action_on(tpos,node,ttl-1); -- run elseif mode == 1 then -- off if not effector.action_off then return end effector.action_off(tpos,node,ttl-1); -- run end end local function check_keypad(pos,name,ttl) -- called only when manually activated via punch local meta = minetest.get_meta(pos); local pass = meta:get_string("pass"); if pass == "" then local iter = meta:get_int("iter"); local count = meta:get_int("count"); if count value for i in pairs( list1) do inv_list1 = inv_list1 .. i .. ","; if i == inv1m then inv1=j end; j=j+1; end node=meta:get_string("node") or ""; NOT=meta:get_int("NOT"); local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z local form = "size[4,6.25]" .. -- width, height default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "field[0.25,0.5;1,1;x0;source1;"..x0.."] field[1.25,0.5;1,1;y0;;"..y0.."] field[2.25,0.5;1,1;z0;;"..z0.."]".. "dropdown[3,0.25;1,1;op; ,AND,OR;".. op .."]".. "field[0.25,1.5;1,1;x1;source2;"..x1.."] field[1.25,1.5;1,1;y1;;"..y1.."] field[2.25,1.5;1,1;z1;;"..z1.."]".. "field[0.25,2.5;1,1;x2;target;"..x2.."] field[1.25,2.5;1,1;y2;;"..y2.."] field[2.25,2.5;1,1;z2;;"..z2.."]".. "field[0.25,3.5;2,1;node;Node/player/object: ;"..node.."]".."field[3.25,2.5;1,1;r;radius;"..r.."]".. "dropdown[0,4.5;3,1;mode;node,player,object,inventory,infotext,light;".. mode .."]".. "dropdown[0,5.5;3,1;inv1;"..inv_list1..";".. inv1 .."]".. "label[0.,4.0;" .. minetest.colorize("lawngreen", "MODE selection") .. "]".. "label[0.,5.2;inventory selection]".. "field[2.25,3.5;2,1;NOT;filter out -2/-1/0/1/2/3/4;"..NOT.."]".. "button[3.,4.4;1,1;help;help] button_exit[3.,5.4;1,1;OK;OK] " --if meta:get_string("owner")==player:get_player_name() then minetest.show_formspec(player:get_player_name(), "basic_machines:detector_"..minetest.pos_to_string(pos), form) -- else -- minetest.show_formspec(player:get_player_name(), "view_only_basic_machines_detector", form) -- end end, allow_metadata_inventory_put = function(pos, listname, index, stack, player) if minetest.is_protected(pos, player:get_player_name()) then minetest.record_protection_violation(pos, player:get_player_name()) return 0 end return 0 end, allow_metadata_inventory_take = function(pos, listname, index, stack, player) if minetest.is_protected(pos, player:get_player_name()) then minetest.record_protection_violation(pos, player:get_player_name()) return 0 end return 0 end, allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) local meta = minetest.get_meta(pos); local mode = "node"; if to_index == 2 then mode = "player"; meta:set_int("r",math.max(meta:get_int("r"),1)) end if to_index == 3 then mode = "object"; meta:set_int("r",math.max(meta:get_int("r"),1)) end meta:set_string("mode",mode) minetest.chat_send_player(player:get_player_name(), "DETECTOR: Mode of operation set to: "..mode) return count end, mesecons = {effector = { action_on = function (pos, node,ttl) if ttl<0 then return end local meta = minetest.get_meta(pos); local t0 = meta:get_int("t"); local t1 = minetest.get_gametime(); local T = meta:get_int("T"); -- temperature if t0>t1-machines_minstep then -- activated before natural time T=T+1; else if T>0 then T=T-1 end end meta:set_int("T",T); meta:set_int("t",t1); -- update last activation time if T > 2 then -- overheat minetest.sound_play("default_cool_lava",{pos = pos, max_hear_distance = 16, gain = 0.25}) meta:set_string("infotext","overheat: temperature ".. T) return end local x0,y0,z0,x1,y1,z1,x2,y2,z2,r,node,NOT,mode,op; x0=meta:get_int("x0")+pos.x;y0=meta:get_int("y0")+pos.y;z0=meta:get_int("z0")+pos.z; x2=meta:get_int("x2")+pos.x;y2=meta:get_int("y2")+pos.y;z2=meta:get_int("z2")+pos.z; r = meta:get_int("r") or 0; NOT = meta:get_int("NOT") node=meta:get_string("node") or ""; mode=meta:get_string("mode") or ""; op = meta:get_string("op") or ""; local trigger = false local detected_obj = ""; if mode == "node" then local tnode = minetest.get_node({x=x0,y=y0,z=z0}).name; -- read node at source position detected_obj = tnode; if node~="" and string.find(tnode,"default:chest") then -- if source is chest, look inside chest for items local cmeta = minetest.get_meta({x=x0,y=y0,z=z0}); local inv = cmeta:get_inventory(); local stack = ItemStack(node) if inv:contains_item("main", stack) then trigger = true end else -- source not a chest if (node=="" and tnode~="air") or node == tnode then trigger = true end if r>0 and node~="" then local found_node = minetest.find_node_near({x=x0, y=y0, z=z0}, r, {node}) if node ~= "" and found_node then trigger = true end end end -- operation: AND, OR... look at other source position too if op~= "" then local trigger1 = false; x1=meta:get_int("x1")+pos.x;y1=meta:get_int("y1")+pos.y;z1=meta:get_int("z1")+pos.z; tnode = minetest.get_node({x=x1,y=y1,z=z1}).name; -- read node at source position if node~="" and string.find(tnode,"default:chest") then -- it source is chest, look inside chest for items local cmeta = minetest.get_meta({x=x1,y=y1,z=z1}); local inv = cmeta:get_inventory(); local stack = ItemStack(node) if inv:contains_item("main", stack) then trigger1 = true end else -- source not a chest if (node=="" and tnode~="air") or node == tnode then trigger1 = true end if r>0 and node~="" then local found_node = minetest.find_node_near({x=x0, y=y0, z=z0}, r, {node}) if node ~= "" and found_node then trigger1 = true end end end if op == "AND" then trigger = trigger and trigger1; elseif op == "OR" then trigger = trigger or trigger1; end end elseif mode=="inventory" then local cmeta = minetest.get_meta({x = x0, y = y0, z = z0}) local inv = cmeta:get_inventory() local stack = ItemStack(node) local inv1m =meta:get_string("inv1") -- if there is item report name and trigger if node == "" then if inv:is_empty(inv1m) then trigger = false -- nonempty else trigger = true local size = inv:get_size(inv1m) -- find item to move in inventory for i = 1, size do local stack = inv:get_stack(inv1m, i) if not stack:is_empty() then detected_obj = stack:to_string() break end end end -- node name was set else local stack = ItemStack(node); if inv:contains_item(inv1m, stack) then trigger = true end end elseif mode == "infotext" then local cmeta = minetest.get_meta({x=x0,y=y0,z=z0}); detected_obj = cmeta:get_string("infotext"); if detected_obj == node or node =="" then trigger = true end elseif mode == "light" then detected_obj=minetest.get_node_light({x=x0,y=y0,z=z0}) or 0; if detected_obj>=(basic_machines.tonumber(node) or 0) or node == "" then trigger = true end else -- players/objects local objects = minetest.get_objects_inside_radius({x=x0,y=y0,z=z0}, r) local player_near=false; for _,obj in pairs(objects) do if mode == "player" then if obj:is_player() then player_near = true detected_obj = obj:get_player_name(); if (node=="" or detected_obj==node) then trigger = true break end end; elseif mode == "object" and not obj:is_player() then if obj:get_luaentity() then detected_obj = obj:get_luaentity().itemstring or ""; if detected_obj == "" then detected_obj = obj:get_luaentity().name or "" end if detected_obj==node then trigger=true break end end if node=="" then trigger = true break end end end if node~="" and NOT==-1 and not(trigger) and not(player_near) and mode == "player" then trigger = true end-- name specified, but noone around and negation -> 0 end -- negation and output filtering local state = meta:get_int("state"); if NOT == 1 then -- just go on normally -- -2: only false, -1: NOT, 0: no signal, 1: normal signal: 2: only true elseif NOT == -1 then trigger = not trigger -- NEGATION elseif NOT == -2 and trigger then return -- ONLY FALSE elseif NOT == 0 then return -- do nothing elseif NOT == 2 and not trigger then return -- ONLY TRUE elseif NOT == 3 and ((trigger and state == 1) or (not trigger and state == 0)) then return -- no change of state end local nstate; if trigger then nstate = 1 else nstate=0 end -- next detector output state if nstate~=state then meta:set_int("state",nstate) end -- update state if changed local node = minetest.get_node({x=x2,y=y2,z=z2});if not node.name then return end -- error local table = minetest.registered_nodes[node.name]; if not table then return end -- error if not table.mesecons then return end -- error if not table.mesecons.effector then return end -- error local effector=table.mesecons.effector; if trigger then -- activate target node if succesful meta:set_string("infotext", "detector: on"); if not effector.action_on then return end if NOT == 4 then -- set detected object name as target text (target must be keypad, if not changes infotext) if minetest.get_node({x=x2,y=y2,z=z2}).name == "basic_machines:keypad" then detected_obj = detected_obj or ""; local tmeta = minetest.get_meta({x=x2,y=y2,z=z2}); tmeta:set_string("text",detected_obj); end end effector.action_on({x=x2,y=y2,z=z2},node,ttl-1); -- run else meta:set_string("infotext", "detector: off"); if not effector.action_off then return end effector.action_off({x=x2,y=y2,z=z2},node,ttl-1); -- run end end } } }) -- minetest.register_chatcommand("clockgen", { -- test: toggle machine running with clockgens, useful for debugging -- -- i.e. seeing how machines running affect server performance -- description = "", -- privs = { -- interact = true -- }, -- func = function(name, param) -- local privs = minetest.get_player_privs(name); -- if not privs.privs then return end -- local player = minetest.get_player_by_name(name); -- if basic_machines.clockgen == 0 then basic_machines.clockgen = 1 else basic_machines.clockgen = 0 end -- minetest.chat_send_player(name, "#clockgen set to " .. basic_machines.clockgen); -- end -- }) -- CLOCK GENERATOR : periodically activates machine on top of it minetest.register_abm({ nodenames = {"basic_machines:clockgen"}, neighbors = {""}, interval = machines_timer, chance = 1, action = function(pos, node, active_object_count, active_object_count_wider) if basic_machines.clockgen == 0 then return end local meta = minetest.get_meta(pos); local machines = meta:get_int("machines"); if machines~=1 then -- no machines privilege if not minetest.get_player_by_name(meta:get_string("owner")) then -- owner not online return end end pos.y=pos.y+1; node = minetest.get_node(pos);if not node.name or node.name == "air" then return end local table = minetest.registered_nodes[node.name]; if table and table.mesecons and table.mesecons.effector then -- check if all elements exist, safe cause it checks from left to right else return end local effector=table.mesecons.effector; if effector.action_on then effector.action_on(pos,node,machines_TTL); end end }); minetest.register_node("basic_machines:clockgen", { description = "Clock generator - use sparingly, continually activates top block", tiles = {"basic_machine_clock_generator.png"}, groups = {cracky=3, mesecon_effector_on = 1}, sounds = default.node_sound_metal_defaults(), after_place_node = function(pos, placer) local meta = minetest.get_meta(pos); local owner = placer:get_player_name() or ""; local privs = minetest.get_player_privs(owner); if privs.machines then meta:set_int("machines",1) end meta:set_string("owner",owner); meta:set_string("infotext","clock generator (owned by " .. owner .. "): place machine to be activated on top of generator"); end }) -- DISTRIBUTOR -- local get_distributor_form = function(pos,player) if not player then return end local meta = minetest.get_meta(pos); local privs = minetest.get_player_privs(player:get_player_name()); local cant_build = minetest.is_protected(pos,player:get_player_name()); --meta:get_string("owner")~=player:get_player_name() and if not privs.privs and cant_build then return end local p = {}; local active = {}; local n = meta:get_int("n"); local delay = meta:get_float("delay"); for i =1,n do p[i]={x=meta:get_int("x"..i),y=meta:get_int("y"..i),z=meta:get_int("z"..i)}; active[i]=meta:get_int("active"..i); end local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z local form = "size[7,"..(0.75+(n)*0.75).."]" .. -- width, height default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "label[0,-0.25;" .. minetest.colorize("lawngreen","target: x y z, MODE -2=only OFF, -1=NOT input/0/1=input, 2 = only ON") .. "]"; for i =1,n do form = form.."field[0.25,"..(0.5+(i-1)*0.75)..";1,1;x"..i..";;"..p[i].x.."] field[1.25,"..(0.5+(i-1)*0.75)..";1,1;y"..i..";;"..p[i].y.."] field[2.25,"..(0.5+(i-1)*0.75)..";1,1;z"..i..";;"..p[i].z.."] field [ 3.25,"..(0.5+(i-1)*0.75)..";1,1;active"..i..";;" .. active[i] .. "]" form = form .. "button[4.,"..(0.25+(i-1)*0.75)..";1.5,1;SHOW"..i..";SHOW "..i.."]".."button_exit[5.25,"..(0.25+(i-1)*0.75)..";1,1;SET"..i..";SET]".."button[6.25,"..(0.25+(i-1)*0.75)..";1,1;X"..i..";X]" end form=form.."button_exit[4.25,"..(0.25+(n)*0.75)..";1,1;ADD;ADD]".."button_exit[3.,"..(0.25+(n)*0.75)..";1,1;OK;OK]".."field[0.25,"..(0.5+(n)*0.75)..";1,1;delay;delay;"..delay .. "]"; form = form.."button[6.25,"..(0.25+(n)*0.75)..";1,1;help;help]"; return form end minetest.register_node("basic_machines:distributor", { description = "Distributor - can forward signal up to 16 different targets", tiles = {"distributor.png"}, groups = {cracky=3, mesecon_effector_on = 1}, sounds = default.node_sound_wood_defaults(), after_place_node = function(pos, placer) local meta = minetest.env:get_meta(pos) meta:set_string("infotext", "Distributor. Right click/punch to set it up.") meta:set_string("owner", placer:get_player_name()); meta:set_int("public",0); for i=1,10 do meta:set_int("x"..i,0);meta:set_int("y"..i,1);meta:set_int("z"..i,0);meta:set_int("active"..i,1) -- target i end meta:set_int("n",2); -- how many targets initially meta:set_float("delay",0); -- delay when transmitting signal meta:set_int("public",0); -- can other ppl set it up? local name = placer:get_player_name();punchset[name] = {}; punchset[name].node = ""; punchset[name].state = 0 end, mesecons = {effector = { action_on = function (pos, node,ttl) if type(ttl)~="number" then ttl = 1 end if not(ttl>0) then return end local meta = minetest.get_meta(pos); local t0 = meta:get_int("t"); local t1 = minetest.get_gametime(); local T = meta:get_int("T"); -- temperature if t0>t1-machines_minstep then -- activated before natural time T=T+1; else if T>0 then T=T-1 if t1-t0>5 then T = 0 end -- reset temperature if more than 5s elapsed since last punch end end meta:set_int("T",T); meta:set_int("t",t1); -- update last activation time if T > 2 then -- overheat minetest.sound_play("default_cool_lava",{pos = pos, max_hear_distance = 16, gain = 0.25}) meta:set_string("infotext","overheat: temperature ".. T) return end local delay = minetest.get_meta(pos):get_float("delay"); local activate = function() local posf = {}; local active = {}; local n = meta:get_int("n");local delay = meta:get_float("delay"); for i =1,n do posf[i]={x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z}; active[i]=meta:get_int("active"..i); end local table,node; for i=1,n do if active[i]~=0 then node = minetest.get_node(posf[i]);if not node.name then return end -- error table = minetest.registered_nodes[node.name]; if table and table.mesecons and table.mesecons.effector then -- check if all elements exist, safe cause it checks from left to right -- alternative way: overkill --ret = pcall(function() if not table.mesecons.effector then end end); -- exception handling to determine if structure exists local effector=table.mesecons.effector; local active_i = active[i]; if (active_i == 1 or active_i == 2) and effector.action_on then -- normal OR only forward input ON effector.action_on(posf[i],node,ttl-1); elseif active_i == -1 and effector.action_off then effector.action_off(posf[i],node,ttl-1) end end end end end if delay>0 then minetest.after(delay, activate) elseif delay == 0 then activate() else -- delay <0 - do random activation: delay = -500 means 500/1000 chance to activate if math.random(1000)<=-delay then activate() end end end, action_off = function (pos, node,ttl) if type(ttl)~="number" then ttl = 1 end if not(ttl>0) then return end local meta = minetest.get_meta(pos); local t0 = meta:get_int("t"); local t1 = minetest.get_gametime(); local T = meta:get_int("T"); -- temperature if t0>t1-machines_minstep then -- activated before natural time T=T+1; else if T>0 then T=T-1 end end meta:set_int("T",T); meta:set_int("t",t1); -- update last activation time if T > 2 then -- overheat minetest.sound_play("default_cool_lava",{pos = pos, max_hear_distance = 16, gain = 0.25}) meta:set_string("infotext","overheat: temperature ".. T) return end local delay = minetest.get_meta(pos):get_float("delay"); local activate = function() local posf = {}; local active = {}; local n = meta:get_int("n"); for i =1,n do posf[i]={x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z}; active[i]=meta:get_int("active"..i); end local node, table for i=1,n do if active[i]~=0 then node = minetest.get_node(posf[i]);if not node.name then return end -- error table = minetest.registered_nodes[node.name]; if table and table.mesecons and table.mesecons.effector then local effector=table.mesecons.effector; if (active[i] == 1 or active[i]==-2) and effector.action_off then -- normal OR only forward input OFF effector.action_off(posf[i],node,ttl-1); elseif (active[i] == -1) and effector.action_on then effector.action_on(posf[i],node,ttl-1); end end end end end if delay>0 then minetest.after(delay, activate) else activate() end end } }, on_rightclick = function(pos, node, player, itemstack, pointed_thing) local form = get_distributor_form(pos,player) if form then minetest.show_formspec(player:get_player_name(), "basic_machines:distributor_"..minetest.pos_to_string(pos), form) end end, } ) -- LIGHT -- minetest.register_node("basic_machines:light_off", { description = "Light off", tiles = {"light_off.png"}, groups = {cracky=3, mesecon_effector_on = 1}, sounds = default.node_sound_glass_defaults(), mesecons = {effector = { action_on = function (pos, node,ttl) minetest.swap_node(pos,{name = "basic_machines:light_on"}); local meta = minetest.get_meta(pos); local deactivate = meta:get_int("deactivate"); if deactivate > 0 then --meta:set_int("active",0); minetest.after(deactivate, function() --if meta:get_int("active") ~= 1 then -- was not activated again, so turn it off minetest.swap_node(pos,{name = "basic_machines:light_off"}); -- turn off again --meta:set_int("active",0); --end end ) end end } }, }) minetest.register_node("basic_machines:light_on", { description = "Light on", tiles = {"light.png"}, groups = {cracky=3, mesecon_effector_on = 1}, sounds = default.node_sound_glass_defaults(), paramtype = "light", light_source = LIGHT_MAX, after_place_node = function(pos, placer) local meta = minetest.get_meta(pos); local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z local deactivate = meta:get_int("deactivate"); local form = "size[2,2]".. default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "field[0.25,0.5;2,1;deactivate;deactivate after ;"..deactivate.."]".."button_exit[0.,1;1,1;OK;OK]"; meta:set_string("formspec", form); end, on_receive_fields = function(pos, formname, fields, player) if minetest.is_protected(pos, player:get_player_name()) then minetest.record_protection_violation(pos, player:get_player_name()) return end if fields.deactivate then local meta = minetest.get_meta(pos); local deactivate = basic_machines.tonumber(fields.deactivate) or 0; if deactivate <0 or deactivate > 600 then deactivate = 0 end meta:set_int("deactivate",deactivate); local form = "size[2,2]".. default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "field[0.25,0.5;2,1;deactivate;deactivate after ;"..deactivate.."]".."button_exit[0.,1;1,1;OK;OK]"; meta:set_string("formspec", form); end end, mesecons = {effector = { action_off = function (pos, node,ttl) minetest.swap_node(pos,{name = "basic_machines:light_off"}); end, action_on = function (pos, node,ttl) local meta = minetest.get_meta(pos); local count = basic_machines.tonumber(meta:get_string("infotext")) or 0; meta:set_string("infotext",count+1); -- increase activate count end } }, }) punchset.known_nodes = {["basic_machines:mover"]=true,["basic_machines:keypad"]=true,["basic_machines:detector"]=true}; -- SETUP BY PUNCHING minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing) -- STRANGE PROBLEM: if player doesnt move it takes another punch at same block for this function to run again, and it works normally if player moved at least one block from his previous position -- it only happens with keypad - maybe caused by formspec displayed.. local name = puncher:get_player_name(); if name==nil then return end if punchset[name]== nil then -- set up punchstate punchset[name] = {} punchset[name].node = "" punchset[name].pos1 = {x=0,y=0,z=0};punchset[name].pos2 = {x=0,y=0,z=0};punchset[name].pos = {x=0,y=0,z=0}; punchset[name].state = 0; -- 0 ready for punch, 1 ready for start position, 2 ready for end position return end -- check for known node names in case of first punch if punchset[name].state == 0 and not punchset.known_nodes[node.name] then return end -- from now on only punches with mover/keypad/... or setup punches if punchset.known_nodes[node.name] then -- check if player is suppose to be able to punch interact if node.name~="basic_machines:keypad" then -- keypad is supposed to be punch interactive! if minetest.is_protected(pos, name) then return end end end if node.name == "basic_machines:mover" then -- mover init code if punchset[name].state == 0 then -- if not puncher:get_player_control().sneak then -- return -- end minetest.chat_send_player(name, "MOVER: Now punch source1, source2, end position to set up mover.") punchset[name].node = node.name;punchset[name].pos = {x=pos.x,y=pos.y,z=pos.z}; punchset[name].state = 1 return end end -- mover code, not first punch if punchset[name].node == "basic_machines:mover" then if minetest.is_protected(pos,name) then minetest.chat_send_player(name, "MOVER: Punched position is protected. aborting.") punchset[name].node = ""; punchset[name].state = 0; return end local meta = minetest.get_meta(punchset[name].pos) if not meta then return end local range = meta:get_float("upgrade") or 1 range = range * max_range if punchset[name].state == 1 then local privs = minetest.get_player_privs(puncher:get_player_name()); if not privs.privs and (math.abs(punchset[name].pos.x - pos.x)>range or math.abs(punchset[name].pos.y - pos.y)>range or math.abs(punchset[name].pos.z - pos.z)>range) then minetest.chat_send_player(name, "MOVER: Punch closer to mover. reseting.") punchset[name].state = 0; return end if punchset[name].pos.x==pos.x and punchset[name].pos.y==pos.y and punchset[name].pos.z==pos.z then minetest.chat_send_player(name, "MOVER: Punch something else. aborting.") punchset[name].state = 0; return end punchset[name].pos1 = {x=pos.x,y=pos.y,z=pos.z};punchset[name].state = 2; machines.pos1[name] = punchset[name].pos1;machines.mark_pos1(name) -- mark position minetest.chat_send_player(name, "MOVER: Source1 position for mover set. Punch again to set source2 position.") return end if punchset[name].state == 2 then local privs = minetest.get_player_privs(puncher:get_player_name()); if not privs.privs and (math.abs(punchset[name].pos.x - pos.x)>range or math.abs(punchset[name].pos.y - pos.y)>range or math.abs(punchset[name].pos.z - pos.z)>range) then minetest.chat_send_player(name, "MOVER: Punch closer to mover. reseting.") punchset[name].state = 0; return end if punchset[name].pos.x==pos.x and punchset[name].pos.y==pos.y and punchset[name].pos.z==pos.z then minetest.chat_send_player(name, "MOVER: Punch something else. aborting.") punchset[name].state = 0; return end punchset[name].pos11 = {x=pos.x,y=pos.y,z=pos.z};punchset[name].state = 3; machines.pos11[name] = {x=pos.x,y=pos.y,z=pos.z}; -- mark pos11 machines.mark_pos11(name) minetest.chat_send_player(name, "MOVER: Source2 position for mover set. Punch again to set target position.") return end if punchset[name].state == 3 then if punchset[name].node ~= "basic_machines:mover" then punchset[name].state = 0 return end local privs = minetest.get_player_privs(puncher:get_player_name()); local elevator_mode = false local meta = minetest.get_meta(punchset[name].pos) meta:set_int("elevator", 0) -- check if elevator mode if punchset[name].pos.x == pos.x and punchset[name].pos.z == pos.z then -- trying to make elevator? if math.abs(punchset[name].pos.y - pos.y) > 3 then local meta = minetest.get_meta(punchset[name].pos) -- only if object mode if meta:get_string("mode") == "object" then -- count number of diamond blocks to determine if elevator can be set up with this height distance local inv = meta:get_inventory() local upgrade = 0 if inv:get_stack("upgrade", 1):get_name() == "default:diamondblock" then local inv_stack = inv:get_stack("upgrade", 1) upgrade = inv_stack:get_count() if upgrade > 10 then upgrade = 10 end end local requirement = math.floor(math.abs(punchset[name].pos.y - pos.y) / 100) + 1 if upgrade < requirement then minetest.chat_send_player(name, "MOVER: Error while trying to make elevator. Need at least "..requirement .. " diamond block(s) in upgrade (1 for every 100 height). ") punchset[name].state = 0 return else elevator_mode = true meta:set_int("upgrade", upgrade + 1) meta:set_int("elevator", 1) minetest.chat_send_player(name, "MOVER: elevator setup completed, upgrade level " .. upgrade) meta:set_string("infotext", "ELEVATOR, activate to use.") end end end end if not privs.privs and not elevator_mode and (math.abs(punchset[name].pos.x - pos.x) > range or math.abs(punchset[name].pos.y - pos.y) > range or math.abs(punchset[name].pos.z - pos.z) > range) then minetest.chat_send_player(name, "MOVER: Punch closer to mover. aborting.") punchset[name].state = 0 return end punchset[name].pos2 = {x = pos.x, y = pos.y, z = pos.z} punchset[name].state = 0 -- mark pos2 machines.pos2[name] = punchset[name].pos2 machines.mark_pos2(name) minetest.chat_send_player(name, "MOVER: End position for mover set.") local x0 = punchset[name].pos1.x-punchset[name].pos.x local y0 = punchset[name].pos1.y-punchset[name].pos.y local z0 = punchset[name].pos1.z-punchset[name].pos.z local meta = minetest.get_meta(punchset[name].pos) local x1 = punchset[name].pos11.x-punchset[name].pos.x local y1 = punchset[name].pos11.y-punchset[name].pos.y local z1 = punchset[name].pos11.z-punchset[name].pos.z local x2 = punchset[name].pos2.x-punchset[name].pos.x local y2 = punchset[name].pos2.y-punchset[name].pos.y local z2 = punchset[name].pos2.z-punchset[name].pos.z -- this ensures that x0 <= x1 if x0 > x1 then x0, x1 = x1, x0 end if y0 > y1 then y0, y1 = y1, y0 end if z0 > z1 then z0, z1 = z1, z0 end meta:set_int("x1", x1) meta:set_int("y1", y1) meta:set_int("z1", z1) meta:set_int("x0", x0) meta:set_int("y0", y0) meta:set_int("z0", z0) meta:set_int("x2", x2) meta:set_int("y2", y2) meta:set_int("z2", z2) meta:set_int("pc", 0) meta:set_int("dim", (x1 - x0 + 1) * (y1 - y0 + 1) * (z1 - z0 + 1)) return end end -- KEYPAD if node.name == "basic_machines:keypad" then -- keypad init/usage code local meta = minetest.get_meta(pos); if not (meta:get_int("x0")==0 and meta:get_int("y0")==0 and meta:get_int("z0")==0) then -- already configured check_keypad(pos,name)-- not setup, just standard operation punchset[name].state = 0; return; else if minetest.is_protected(pos, name) then return minetest.chat_send_player(name, "KEYPAD: You must be able to build to set up keypad.") end --if meta:get_string("owner")~= name then minetest.chat_send_player(name, "KEYPAD: Only owner can set up keypad.") return end if punchset[name].state == 0 then minetest.chat_send_player(name, "KEYPAD: Now punch the target block.") punchset[name].node = node.name;punchset[name].pos = {x=pos.x,y=pos.y,z=pos.z}; punchset[name].state = 1 return end end end if punchset[name].node=="basic_machines:keypad" then -- keypad setup code if minetest.is_protected(pos,name) then minetest.chat_send_player(name, "KEYPAD: Punched position is protected. aborting.") punchset[name].node = ""; punchset[name].state = 0; return end if punchset[name].state == 1 then local meta = minetest.get_meta(punchset[name].pos); local x = pos.x-punchset[name].pos.x; local y = pos.y-punchset[name].pos.y; local z = pos.z-punchset[name].pos.z; if math.abs(x)>max_range or math.abs(y)>max_range or math.abs(z)>max_range then minetest.chat_send_player(name, "KEYPAD: Punch closer to keypad. reseting.") punchset[name].state = 0; return end machines.pos1[name] = pos; machines.mark_pos1(name) -- mark pos1 meta:set_int("x0",x);meta:set_int("y0",y);meta:set_int("z0",z); punchset[name].state = 0 minetest.chat_send_player(name, "KEYPAD: Keypad target set with coordinates " .. x .. " " .. y .. " " .. z) meta:set_string("infotext", "Punch keypad to use it."); return end end -- DETECTOR "basic_machines:detector" if node.name == "basic_machines:detector" then -- detector init code local meta = minetest.get_meta(pos); --meta:get_string("owner")~= name if minetest.is_protected(pos,name) then minetest.chat_send_player(name, "DETECTOR: You must be able to build to set up detector.") return end if punchset[name].state == 0 then minetest.chat_send_player(name, "DETECTOR: Now punch the source block.") punchset[name].node = node.name; punchset[name].pos = {x=pos.x,y=pos.y,z=pos.z}; punchset[name].state = 1 return end end if punchset[name].node == "basic_machines:detector" then if minetest.is_protected(pos,name) then minetest.chat_send_player(name, "DETECTOR: Punched position is protected. aborting.") punchset[name].node = ""; punchset[name].state = 0; return end if punchset[name].state == 1 then if math.abs(punchset[name].pos.x - pos.x)>max_range or math.abs(punchset[name].pos.y - pos.y)>max_range or math.abs(punchset[name].pos.z - pos.z)>max_range then minetest.chat_send_player(name, "DETECTOR: Punch closer to detector. aborting.") punchset[name].state = 0; return end minetest.chat_send_player(name, "DETECTOR: Now punch the target machine.") punchset[name].pos1 = {x=pos.x,y=pos.y,z=pos.z}; machines.pos1[name] = pos;machines.mark_pos1(name) -- mark pos1 punchset[name].state = 2 return end if punchset[name].state == 2 then if math.abs(punchset[name].pos.x - pos.x)>max_range or math.abs(punchset[name].pos.y - pos.y)>max_range or math.abs(punchset[name].pos.z - pos.z)>max_range then minetest.chat_send_player(name, "DETECTOR: Punch closer to detector. aborting.") punchset[name].state = 0; return end if punchset[name].pos.x == pos.x and punchset[name].pos.y == pos.y and punchset[name].pos.z == pos.z then minetest.chat_send_player(name, "DETECTOR: Punch something else. aborting.") punchset[name].state = 0; return end minetest.chat_send_player(name, "DETECTOR: Setup complete.") machines.pos2[name] = pos;machines.mark_pos2(name) -- mark pos2 local x = punchset[name].pos1.x-punchset[name].pos.x; local y = punchset[name].pos1.y-punchset[name].pos.y; local z = punchset[name].pos1.z-punchset[name].pos.z; local meta = minetest.get_meta(punchset[name].pos); meta:set_int("x0",x);meta:set_int("y0",y);meta:set_int("z0",z); x=pos.x-punchset[name].pos.x;y=pos.y-punchset[name].pos.y;z=pos.z-punchset[name].pos.z; meta:set_int("x2",x);meta:set_int("y2",y);meta:set_int("z2",z); punchset[name].state = 0 return end end if punchset[name].node == "basic_machines:distributor" then if minetest.is_protected(pos,name) then minetest.chat_send_player(name, "DISTRIBUTOR: Punched position is protected. aborting.") punchset[name].node = ""; punchset[name].state = 0; return end if punchset[name].state > 0 then if math.abs(punchset[name].pos.x - pos.x)>max_range or math.abs(punchset[name].pos.y - pos.y)>max_range or math.abs(punchset[name].pos.z - pos.z)>max_range then minetest.chat_send_player(name, "DISTRIBUTOR: Punch closer to distributor. aborting.") punchset[name].state = 0; return end minetest.chat_send_player(name, "DISTRIBUTOR: target set.") local meta = minetest.get_meta(punchset[name].pos); local x = pos.x-punchset[name].pos.x; local y = pos.y-punchset[name].pos.y; local z = pos.z-punchset[name].pos.z; local j = punchset[name].state; meta:set_int("x"..j,x);meta:set_int("y"..j,y);meta:set_int("z"..j,z); if x==0 and y==0 and z==0 then meta:set_int("active"..j,0) end machines.pos1[name] = pos;machines.mark_pos1(name) -- mark pos1 punchset[name].state = 0; return end end end) -- FORM PROCESSING for all machines minetest.register_on_player_receive_fields(function(player,formname,fields) -- MOVER local fname = "basic_machines:mover_" if string.sub(formname,0,string.len(fname)) == fname then local pos_s = string.sub(formname,string.len(fname)+1); local pos = minetest.string_to_pos(pos_s) local name = player:get_player_name(); if name==nil then return end local meta = minetest.get_meta(pos) local privs = minetest.get_player_privs(name); if (minetest.is_protected(pos,name) and not privs.privs) or not fields then return end -- only builder can interact if fields.help == "help" then local text = "version " .. basic_machines.version .. "\nSETUP: For interactive setup ".. "punch the mover and then punch source1, source2, target node (follow instructions). Put charged battery within distance 1 from mover. For advanced setup right click mover. Positions are defined by x y z coordinates (see top of mover for orientation). Mover itself is at coordinates 0 0 0. ".. "\n\nMODES of operation: normal (just teleport block), dig (digs and gives you resulted node - good for harvesting farms), drop ".. "(drops node on ground), object (teleportation of player and objects. distance between source1/2 defines teleport radius). by setting filter you can specify move time for objects or names for players. ".. "By setting 'filter' only selected nodes are moved.\nInventory mode can exchange items between node inventories. You need to select inventory name for source/target from the dropdown list on the right and enter node to be moved into filter.".. "\n*advanced* You can reverse start/end position by setting reverse nonzero. This is useful for placing stuff at many locations-planting. If you put reverse=2/3 in transport mode it will disable parallel transport but will still do reverse effect with 3. If you activate mover with OFF signal it will toggle reverse." .. "\n\n FUEL CONSUMPTION depends on blocks to be moved and distance. For example, stone or tree is harder to move than dirt, harvesting wheat is very cheap and and moving lava is very hard.".. "\n\n UPGRADE mover by moving mese blocks in upgrade inventory. Each mese block increases mover range by 10, fuel consumption is divided by (number of mese blocks)+1 in upgrade. Max 10 blocks are used for upgrade. Dont forget to click OK to refresh after upgrade. ".. "\n\n Activate mover by keypad/detector signal or mese signal (if mesecons mod) ."; local form = "size [6,7]".. default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "textarea[0,0;6.5,8.5;help;MOVER HELP;".. text.."]" minetest.show_formspec(name, "basic_machines:help_mover", form) return end if fields.tabs then meta:set_int("seltab", basic_machines.tonumber(fields.tabs) or 1) local form = get_mover_form(pos,player) minetest.show_formspec(player:get_player_name(), "basic_machines:mover_"..minetest.pos_to_string(pos), form) return end if fields.OK == "OK" then --yyy local seltab = meta:get_int("seltab"); if seltab == 2 then -- POSITIONS -- positions local x0, y0, z0, x1, y1, z1, x2, y2, z2 x0 = basic_machines.tonumber(fields.x0) or 0 y0 = basic_machines.tonumber(fields.y0) or -1 z0 = basic_machines.tonumber(fields.z0) or 0 x1 = basic_machines.tonumber(fields.x1) or 0 y1 = basic_machines.tonumber(fields.y1) or -1 z1 = basic_machines.tonumber(fields.z1) or 0 x2 = basic_machines.tonumber(fields.x2) or 0 y2 = basic_machines.tonumber(fields.y2) or 1 z2 = basic_machines.tonumber(fields.z2) or 0 -- did the numbers change from last time? if meta:get_int("x0") ~= x0 or meta:get_int("y0") ~= y0 or meta:get_int("z0") ~= z0 or meta:get_int("x1") ~= x1 or meta:get_int("y1") ~= y1 or meta:get_int("z1") ~= z1 or meta:get_int("x2") ~= x2 or meta:get_int("y2") ~= y2 or meta:get_int("z2") ~= z2 then -- are new numbers inside bounds? if not privs.privs and (math.abs(x1) > max_range or math.abs(y1) > max_range or math.abs(z1) > max_range or math.abs(x2) > max_range or math.abs(y2) > max_range or math.abs(z2) > max_range) then minetest.chat_send_player(name,"#mover: all coordinates must be between ".. -max_range .. " and " .. max_range .. ". For increased range set up positions by punching"); return end end --local range = meta:get_float("upgrade") or 1; range = range * max_range; local x = x0; x0 = math.min(x,x1); x1 = math.max(x,x1); local y = y0; y0 = math.min(y,y1); y1 = math.max(y,y1); local z = z0; z0 = math.min(z,z1); z1 = math.max(z,z1); if minetest.is_protected({x=pos.x+x0,y=pos.y+y0,z=pos.z+z0},name) then minetest.chat_send_player(name, "MOVER: position is protected. aborting.") return end if minetest.is_protected({x=pos.x+x1,y=pos.y+y1,z=pos.z+z1},name) then minetest.chat_send_player(name, "MOVER: position is protected. aborting.") return end meta:set_int("x0",x0);meta:set_int("y0",y0);meta:set_int("z0",z0); meta:set_int("x1",x1);meta:set_int("y1",y1);meta:set_int("z1",z1); meta:set_int("dim",(x1-x0+1)*(y1-y0+1)*(z1-z0+1)) meta:set_int("x2",x2);meta:set_int("y2",y2);meta:set_int("z2",z2); if fields.reverse then meta:set_string("reverse",fields.reverse); end if fields.inv1 then meta:set_string("inv1",fields.inv1); end if fields.inv2 then meta:set_string("inv2",fields.inv2); end --notification meta:set_string("infotext", "Mover block. Set up with source coordinates ".. x0 ..","..y0..","..z0.. " -> ".. x1 ..","..y1..","..z1.. " and target coord ".. x2 ..","..y2..",".. z2 .. ". Put charged battery next to it and start it with keypad/mese signal."); else -- MODE 1 if fields.mode then meta:set_string("mode",fields.mode); end --filter local prefer = fields.prefer or "" local meta_prefer = meta:get_string("prefer") if meta_prefer ~= prefer then meta_prefer = prefer end -- prevent item stacks with more than 'stack_max' items local preferstack = ItemStack(meta_prefer) if preferstack:get_count() > preferstack:get_stack_max() then preferstack:set_count(preferstack:get_stack_max()) else preferstack:set_count(preferstack:get_count()) end meta:set_string("prefer", preferstack:to_string()); --notification meta:set_string("infotext", "Mover block. Mode or filter changed."); end if meta:get_float("fuel")<0 then meta:set_float("fuel",0) end -- reset block -- display battery local fpos = find_and_connect_battery(pos); if not fpos then minetest.chat_send_player(name,"MOVER: please put battery nearby") else minetest.chat_send_player(name,"MOVER: battery found - displaying mark 1") machines.pos1[name] = fpos; machines.mark_pos1(name) end elseif fields.mode then meta:set_string("mode",fields.mode); local form = get_mover_form(pos,player) minetest.show_formspec(player:get_player_name(), "basic_machines:mover_"..minetest.pos_to_string(pos), form) return end return end -- KEYPAD fname = "basic_machines:keypad_" if string.sub(formname,0,string.len(fname)) == fname then local pos_s = string.sub(formname,string.len(fname)+1); local pos = minetest.string_to_pos(pos_s) local name = player:get_player_name(); if name==nil then return end local meta = minetest.get_meta(pos) local privs = minetest.get_player_privs(player:get_player_name()); if (minetest.is_protected(pos,name) and not privs.privs) or not fields then return end -- only builder can interact if fields.help then local text = "target : represents coordinates ( x, y, z ) relative to keypad. (0,0,0) is keypad itself, (0,1,0) is one node above, (0,-1,0) one node below. X coordinate axes goes from east to west, Y from down to up, Z from south to north.".. "\n\nPassword: enter password and press OK. Password will be encrypted. Next time you use keypad you will need to enter correct password to gain access.".. "\n\nrepeat: number to control how many times activation is repeated after initial punch".. "\n\ntext: if set then text on target node will be changed. In case target is detector/mover, filter settings will be changed. Can be used for special operations.".. "\n\n1=OFF/2=ON/3=TOGGLE control the way how target node is activated".. "\n**************************************************\nusage\n".. "\nJust punch ( left click ) keypad, then the target block will be activated.".. "\nTo set text on other nodes ( text shows when you look at node ) just target the node and set nonempty text. Upon activation text will be set. When target node is another keypad, its \"text\" field will be set. When targets is mover/detector, its \"filter\" field will be set. To clear \"filter\" set text to \"@\". When target is distributor, you can change i-th target of distributor to mode mode with \"i mode\"".. "\n\nkeyboard : to use keypad as keyboard for text input write \"@\" in \"text\" field and set any password. Next time keypad is used it will work as text input device.".. "\n\ndisplaying messages to nearby players ( up to 5 blocks around keypad's target ): set text to \"!text\". Upon activation player will see \"text\" in their chat.".. "\n\nplaying sound to nearby players : set text to \"$sound_name\"".. "\n\nadvanced: ".. "\ntext replacement : Suppose keypad A is set with text \"@some @. text @!\" and there are blocks on top of keypad A with infotext '1' and '2'. Suppose we target B with A and activate A. Then text of keypad B will be set to \"some 1. text 2!\"".. "\nword extraction: Suppose similiar setup but now keypad A is set with text \"%1\". Then upon activation text of keypad B will be set to 1.st word of infotext"; local form = "size[6,7]".. default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "textarea[0,0;6.5,8.5;help;KEYPAD HELP;".. text.."]" minetest.show_formspec(name, "basic_machines:help_keypad", form) return end if fields.OK == "OK" then local x0,y0,z0,pass,mode; x0=basic_machines.tonumber(fields.x0) or 0;y0=basic_machines.tonumber(fields.y0) or 1;z0=basic_machines.tonumber(fields.z0) or 0 pass = fields.pass or ""; mode = fields.mode or 1; if minetest.is_protected({x=pos.x+x0,y=pos.y+y0,z=pos.z+z0},name) then minetest.chat_send_player(name, "KEYPAD: position is protected. aborting.") return end if not privs.privs and (math.abs(x0)>max_range or math.abs(y0)>max_range or math.abs(z0)>max_range) then minetest.chat_send_player(name,"#keypad: all coordinates must be between ".. -max_range .. " and " .. max_range); return end meta:set_int("x0",x0);meta:set_int("y0",y0);meta:set_int("z0",z0); if fields.pass then if fields.pass~="" and string.len(fields.pass)<=16 then -- dont replace password with hash which is longer - 27 chars pass=minetest.get_password_hash(pos.x, pass..pos.y);pass=minetest.get_password_hash(pos.y, pass..pos.z); meta:set_string("pass",pass); end end if fields.text then meta:set_string("text", fields.text); if string.find(fields.text, "!") then minetest.log("action", string.format("%s set up keypad for message display at %s", name, minetest.pos_to_string(pos))) end end meta:set_int("iter",math.min(basic_machines.tonumber(fields.iter) or 1,500));meta:set_int("mode",basic_machines.tonumber(mode) or 2); meta:set_string("infotext", "Punch keypad to use it."); if pass~="" then if fields.text~="@" then meta:set_string("infotext",meta:get_string("infotext").. ". Password protected."); else meta:set_string("infotext","punch keyboard to use it."); end end end return end fname = "basic_machines:check_keypad_" if string.sub(formname,0,string.len(fname)) == fname then local pos_s = string.sub(formname,string.len(fname)+1); local pos = minetest.string_to_pos(pos_s) local name = player:get_player_name(); if name==nil then return end local meta = minetest.get_meta(pos) if fields.OK == "OK" then local pass; pass = fields.pass or ""; if meta:get_string("text")=="@" then -- keyboard mode meta:set_string("input", pass); meta:set_int("count",1); use_keypad(pos,machines_TTL,0); return end pass=minetest.get_password_hash(pos.x, pass..pos.y);pass=minetest.get_password_hash(pos.y, pass..pos.z); if pass~=meta:get_string("pass") then minetest.chat_send_player(name,"ACCESS DENIED. WRONG PASSWORD.") return end minetest.chat_send_player(name,"ACCESS GRANTED.") if meta:get_int("count")<=0 then -- only accept new operation requests if idle meta:set_int("count",meta:get_int("iter")); meta:set_int("active_repeats",0); use_keypad(pos,machines_TTL,0) else meta:set_int("count",0); meta:set_string("infotext","operation aborted by user. punch to activate.") -- reset end return end end -- DETECTOR local fname = "basic_machines:detector_" if string.sub(formname,0,string.len(fname)) == fname then local pos_s = string.sub(formname,string.len(fname)+1); local pos = minetest.string_to_pos(pos_s) local name = player:get_player_name(); if name==nil then return end local meta = minetest.get_meta(pos) local privs = minetest.get_player_privs(player:get_player_name()); if (minetest.is_protected(pos,name) and not privs.privs) or not fields then return end -- only builder --minetest.chat_send_all("formname " .. formname .. " fields " .. dump(fields)) if fields.help == "help" then local text = "SETUP: right click or punch and follow chat instructions. With detector you can detect nodes, objects, players, or items inside inventories.".. "If detector activates it will trigger machine at target position.\n\nThere are 4 modes of operation - node/player/object/inventory detection. Inside node/player/object ".. "write node/player/object name. If you detect players/objects you can specify range of detection. If you want detector to activate target precisely when its not triggered set NOT to 1\n\n".. "For example, to detect empty space write air, to detect tree write default:tree, to detect ripe wheat write farming:wheat_8, for flowing water write default:water_flowing ... ".. "If source position is chest it will look into it and check if there are items inside. If mode is inventory it will check for items in specified inventory of source node.".. "\n\nADVANCED: you can select second source and then select AND/OR from the right top dropdown list to do logical operations. You can also filter output signal:\n -2=only OFF,-1=NOT/0/1=normal,2=only ON, 3 only if changed".. " 4 = if target keypad set its text to detected object name" ; local form = "size[5.5,5.5]".. default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "textarea[0,0;6,7;help;DETECTOR HELP;".. text.."]" minetest.show_formspec(name, "basic_machines:help_detector", form) end if fields.OK == "OK" then local x0,y0,z0,x1,y1,z1,x2,y2,z2,r,node,NOT; x0=basic_machines.tonumber(fields.x0) or 0;y0=basic_machines.tonumber(fields.y0) or 0;z0=basic_machines.tonumber(fields.z0) or 0 x1=basic_machines.tonumber(fields.x1) or 0;y1=basic_machines.tonumber(fields.y1) or 0;z1=basic_machines.tonumber(fields.z1) or 0 x2=basic_machines.tonumber(fields.x2) or 0;y2=basic_machines.tonumber(fields.y2) or 0;z2=basic_machines.tonumber(fields.z2) or 0 r=basic_machines.tonumber(fields.r) or 1; NOT = basic_machines.tonumber(fields.NOT) if minetest.is_protected({x=pos.x+x0,y=pos.y+y0,z=pos.z+z0},name) then minetest.chat_send_player(name, "DETECTOR: position is protected. aborting.") return end if minetest.is_protected({x=pos.x+x2,y=pos.y+y2,z=pos.z+z2},name) then minetest.chat_send_player(name, "DETECTOR: position is protected. aborting.") return end if not privs.privs and (math.abs(x0)>max_range or math.abs(y0)>max_range or math.abs(z0)>max_range or math.abs(x1)>max_range or math.abs(y1)>max_range or math.abs(z1)>max_range) then minetest.chat_send_player(name,"#detector: all coordinates must be between ".. -max_range .. " and " .. max_range); return end if fields.inv1 then meta:set_string("inv1",fields.inv1); end meta:set_int("x0",x0);meta:set_int("y0",y0);meta:set_int("z0",z0); meta:set_int("x1",x1);meta:set_int("y1",y1);meta:set_int("z1",z1); meta:set_int("x2",x2);meta:set_int("y2",y2);meta:set_int("z2",z2); meta:set_int("r",math.min(r,10)); if NOT then meta:set_int("NOT",NOT); end meta:set_string("node",fields.node or ""); local mode = fields.mode or "node"; meta:set_string("mode",mode); local op = fields.op or ""; meta:set_string("op",op); end return end -- DISTRIBUTOR local fname = "basic_machines:distributor_" if string.sub(formname,0,string.len(fname)) == fname then local pos_s = string.sub(formname,string.len(fname)+1); local pos = minetest.string_to_pos(pos_s) local name = player:get_player_name(); if name==nil then return end local meta = minetest.get_meta(pos) local privs = minetest.get_player_privs(player:get_player_name()); if (minetest.is_protected(pos,name) and not privs.privs) or not fields then return end -- only builder --minetest.chat_send_all("formname " .. formname .. " fields " .. dump(fields)) if fields.OK == "OK" then local posf = {}; local active = {}; local n = meta:get_int("n"); for i = 1,n do posf[i]={x=basic_machines.tonumber(fields["x"..i]) or 0,y=basic_machines.tonumber(fields["y"..i]) or 0,z=basic_machines.tonumber(fields["z"..i]) or 0}; active[i]=basic_machines.tonumber(fields["active"..i]) or 0; if (not (privs.privs) and math.abs(posf[i].x)>max_range or math.abs(posf[i].y)>max_range or math.abs(posf[i].z)>max_range) then minetest.chat_send_player(name,"#distributor: all coordinates must be between ".. -max_range .. " and " .. max_range); return end meta:set_int("x"..i,posf[i].x);meta:set_int("y"..i,posf[i].y);meta:set_int("z"..i,posf[i].z); if posf[i].x==0 and posf[i].y==0 and posf[i].z==0 then meta:set_int("active"..i,0); -- no point in activating itself else meta:set_int("active"..i,active[i]); end if fields.delay then meta:set_float("delay", basic_machines.tonumber(fields.delay) or 0); end end end if fields["ADD"] then local n = meta:get_int("n"); if n<16 then meta:set_int("n",n+1); end -- max 16 outputs local form = get_distributor_form(pos,player) minetest.show_formspec(player:get_player_name(), "basic_machines:distributor_"..minetest.pos_to_string(pos), form) return end -- SHOWING TARGET local j=-1;local n = meta:get_int("n"); for i = 1,n do if fields["SHOW"..i] then j = i end end --show j-th point if j>0 then local posf={x=basic_machines.tonumber(fields["x"..j]) or 0,y=basic_machines.tonumber(fields["y"..j]) or 0,z=basic_machines.tonumber(fields["z"..j]) or 0}; machines.pos1[player:get_player_name()] = {x=posf.x+pos.x,y=posf.y+pos.y,z=posf.z+pos.z}; machines.mark_pos1(player:get_player_name()) return; end --SETUP TARGET j=-1; for i = 1,n do if fields["SET"..i] then j = i end end -- set up j-th point if j>0 then punchset[name].node = "basic_machines:distributor"; punchset[name].state = j punchset[name].pos = pos; minetest.chat_send_player(name,"[DISTRIBUTOR] punch the position to set target "..j); return; end -- REMOVE TARGET if n>0 then j=-1; for i = 1,n do if fields["X"..i] then j = i end end -- remove j-th point if j>0 then for i=j,n-1 do meta:set_int("x"..i, meta:get_int("x"..(i+1))) meta:set_int("y"..i, meta:get_int("y"..(i+1))) meta:set_int("z"..i, meta:get_int("z"..(i+1))) meta:set_int("active"..i, meta:get_int("active"..(i+1))) end meta:set_int("n",n-1); local form = get_distributor_form(pos,player) minetest.show_formspec(player:get_player_name(), "basic_machines:distributor_"..minetest.pos_to_string(pos), form) return; end end if fields.help == "help" then local text = "SETUP: to select target nodes for activation click SET then click target node.\n".. "You can add more targets with ADD. To see where target node is click SHOW button next to it.\n".. "Numbers in each row represent (from left to right) : first 3 numbers are target coordinates,\n".. "last number controls how signal is passed to target. For example, to only pass OFF signal use -2,\n".. "to only pass ON use 2, -1 negates the signal, 1 = pass original signal, 0 blocks signal\n".. "delay option adds delay to activations, in seconds. With negative delay activation is randomized with probability -delay/1000.\n\n".. "ADVANCED: you can use distributor as an event handler. First you must deactivate first target by putting 0 at\n".. "last place in first line. Meanings of first 2 numbers are as follows: first number 0/1 controls if node/n".. "listens to failed interact attempts around it, second number -1/1 listens to chat and can mute it"; local form = "size[5.5,5.5]".. default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "textarea[0,0;6,7;help;DISTRIBUTOR HELP;".. text.."]" minetest.show_formspec(name, "basic_machines:help_distributor", form) end end end) -- CRAFTS -- minetest.register_craft({ output = "basic_machines:keypad", recipe = { {"default:stick"}, {"default:wood"}, } }) minetest.register_craft({ output = "basic_machines:mover", recipe = { {"default:mese_crystal", "default:mese_crystal","default:mese_crystal"}, {"default:mese_crystal", "default:mese_crystal","default:mese_crystal"}, {"default:stone", "basic_machines:keypad", "default:stone"} } }) minetest.register_craft({ output = "basic_machines:detector", recipe = { {"default:mese_crystal", "default:mese_crystal"}, {"default:mese_crystal", "default:mese_crystal"}, {"basic_machines:keypad",""} } }) minetest.register_craft({ output = "basic_machines:light_on", recipe = { {"default:torch", "default:torch"}, {"default:torch", "default:torch"} } }) minetest.register_craft({ output = "basic_machines:distributor", recipe = { {"default:steel_ingot"}, {"default:mese_crystal"}, {"basic_machines:keypad"} } }) minetest.register_craft({ output = "basic_machines:clockgen", recipe = { {"default:diamondblock"}, {"basic_machines:keypad"} } })