--[[ Signs Bot ========= Copyright (C) 2019-2024 Joachim Stolberg GPL v3 See LICENSE.txt for more information Bot move commands ]]-- -- Load support for I18n. local S = signs_bot.S local lib = signs_bot.lib local get_node_lvm = tubelib2.get_node_lvm local function node_and_pos(pos) return get_node_lvm(pos), pos end -- Positions to check: -- 5 6 -- [R]1 -- 3 2 -- 4 function signs_bot.move_robot(mem) local param2 = mem.robot_param2 local pos = mem.robot_pos local node1, pos1 = node_and_pos(lib.next_pos(pos, param2)) local node2, pos2 = node_and_pos({x=pos1.x, y=pos1.y-1, z=pos1.z}) local node3, pos3 = node_and_pos({x=pos.x, y=pos.y-1, z=pos.z}) -- -- One step forward (pos1) -- if lib.check_pos(pos1, node1, node2, param2) or lib.check_pos(pos1, node1, node3, param2) then if node3.name == "signs_bot:robot_foot" then minetest.swap_node(pos3, mem.stored_node or {name = "air"}) minetest.remove_node(pos) elseif node3.name == "signs_bot:robot_leg" then local node4, pos4 = node_and_pos({x=pos.x, y=pos.y-2, z=pos.z}) if node4.name == "signs_bot:robot_foot" then minetest.swap_node(pos4, mem.stored_node or {name = "air"}) end minetest.swap_node(pos3, {name = "air"}) minetest.remove_node(pos) else minetest.swap_node(pos, mem.stored_node or {name = "air"}) end mem.stored_node = node1 minetest.set_node(pos1, {name="signs_bot:robot", param2=param2}) minetest.sound_play('signs_bot_step', {pos = pos1}) return pos1 end -- -- One step up (pos5) -- local node6, pos6 = node_and_pos({x=pos1.x, y=pos1.y+1, z=pos1.z}) if lib.check_pos(pos6, node6, node1, param2) then local node5, pos5 = node_and_pos({x=pos.x, y=pos.y+1, z=pos.z}) if node5.name == "air" then if node3.name == "signs_bot:robot_leg" then return nil elseif node3.name == "signs_bot:robot_foot" then minetest.swap_node(pos3, {name="signs_bot:robot_leg"}) else minetest.swap_node(pos, {name="signs_bot:robot_foot"}) end minetest.set_node(pos5, {name="signs_bot:robot", param2=param2}) minetest.sound_play('signs_bot_step', {pos = pos5}) return pos5 end end -- -- One step down I (pos3) -- local node4, pos4 = node_and_pos({x=pos.x, y=pos.y-2, z=pos.z}) if lib.check_pos(pos3, node3, node4, param2) then -- minetest.remove_node(pos) minetest.set_node(pos3, {name="signs_bot:robot", param2=param2}) minetest.sound_play('signs_bot_step', {pos = pos3}) mem.stored_node = node3 return pos3 end -- -- One step down II (pos3) -- if node3.name == "signs_bot:robot_foot" or node3.name == "signs_bot:robot_leg" then minetest.remove_node(pos) minetest.set_node(pos3, {name="signs_bot:robot", param2=param2}) minetest.sound_play('signs_bot_step', {pos = pos3}) return pos3 end end local function backward_robot(mem) local param2 = mem.robot_param2 local pos = mem.robot_pos local node1, pos1 = node_and_pos(lib.next_pos(pos, (param2 + 2) % 4)) local node2, pos2 = node_and_pos({x=pos1.x, y=pos1.y-1, z=pos1.z}) local node3, pos3 = node_and_pos({x=pos.x, y=pos.y-1, z=pos.z}) local node4, pos4 = node_and_pos({x=pos.x, y=pos.y-2, z=pos.z}) local new_pos = nil if lib.check_pos(pos1, node1, node2, param2) then if node3.name == "signs_bot:robot_foot" or node3.name == "signs_bot:robot_leg" then minetest.remove_node(pos3) if node4.name == "signs_bot:robot_foot" then minetest.remove_node(pos4) end end minetest.swap_node(pos, mem.stored_node or {name = "air"}) minetest.set_node(pos1, {name="signs_bot:robot", param2=param2}) minetest.sound_play('signs_bot_step', {pos = pos1}) mem.stored_node = node1 return pos1 end end signs_bot.register_botcommand("backward", { mod = "move", params = "", num_param = 0, description = S("Move the robot one step back"), cmnd = function(base_pos, mem) local new_pos = backward_robot(mem) if new_pos then -- not blocked? mem.robot_pos = new_pos end return signs_bot.DONE end, }) local function turn_robot(pos, param2, dir) if dir == "R" then param2 = (param2 + 1) % 4 else param2 = (param2 + 3) % 4 end minetest.swap_node(pos, {name="signs_bot:robot", param2=param2}) minetest.sound_play('signs_bot_step', {pos = pos, gain = 0.6}) return param2 end signs_bot.register_botcommand("turn_left", { mod = "move", params = "", num_param = 0, description = S("Turn the robot to the left"), cmnd = function(base_pos, mem) mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "L") return signs_bot.DONE end, }) signs_bot.register_botcommand("turn_right", { mod = "move", params = "", num_param = 0, description = S("Turn the robot to the right"), cmnd = function(base_pos, mem) mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "R") return signs_bot.DONE end, }) signs_bot.register_botcommand("turn_around", { mod = "move", params = "", num_param = 0, description = S("Turn the robot around"), cmnd = function(base_pos, mem) mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "R") mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "R") return signs_bot.DONE end, }) -- Positions to check: -- 1 -- [R] -- 2 local function robot_up(pos, param2) local node1, pos1 = node_and_pos({x=pos.x, y=pos.y+1, z=pos.z}) local node2, pos2 = node_and_pos({x=pos.x, y=pos.y-1, z=pos.z}) if lib.check_pos(pos1, node1, node2, param2) then if node2.name == "signs_bot:robot_leg" then return nil elseif node2.name == "signs_bot:robot_foot" then minetest.swap_node(pos, {name="signs_bot:robot_leg"}) else minetest.swap_node(pos, {name="signs_bot:robot_foot"}) end minetest.set_node(pos1, {name="signs_bot:robot", param2=param2}) minetest.sound_play('signs_bot_step', {pos = pos1}) return pos1 end return nil end signs_bot.register_botcommand("move_up", { mod = "move", params = "", num_param = 0, description = S("Move the robot upwards"), cmnd = function(base_pos, mem) local new_pos = robot_up(mem.robot_pos, mem.robot_param2) if new_pos then -- not blocked? mem.robot_pos = new_pos end return signs_bot.DONE end, }) -- Positions to check: -- [R] -- 1 -- 2 -- 3 local function robot_down(pos, param2) local node1, pos1 = node_and_pos({x=pos.x, y=pos.y-1, z=pos.z}) local node2, pos2 = node_and_pos({x=pos.x, y=pos.y-2, z=pos.z}) local node3, pos3 = node_and_pos({x=pos.x, y=pos.y-3, z=pos.z}) if lib.check_pos(pos1, node1, node2, param2) or (node1.name == "air" and lib.check_pos(pos2, node2, node3, param2)) or (node1.name == "signs_bot:robot_leg" or node1.name == "signs_bot:robot_foot") then minetest.remove_node(pos) minetest.set_node(pos1, {name="signs_bot:robot", param2=param2}) minetest.sound_play('signs_bot_step', {pos = pos1}) return pos1 end return nil end signs_bot.register_botcommand("move_down", { mod = "move", params = "", num_param = 0, description = S("Move the robot down"), cmnd = function(base_pos, mem) local new_pos = robot_down(mem.robot_pos, mem.robot_param2) if new_pos then -- not blocked? mem.robot_pos = new_pos end return signs_bot.DONE end, }) signs_bot.register_botcommand("fall_down", { mod = "move", params = "", num_param = 0, description = S("Fall into a hole/chasm (up to 10 blocks)"), cmnd = function(base_pos, mem) if not mem.bot_falling then --Run a while loop that checks the 10 nodes below bot for a node with the walkable property, breaking the loop once it finds a walkable node. local fallcounter = 0 local fallnode = {walkable = false} while fallcounter <= 9 and fallnode.walkable == false do fallcounter = fallcounter + 1 --Pulls the node name from the next position, then assigns the node definintion to the fallnode variable. fallnode = minetest.get_node_or_nil({x=mem.robot_pos.x, y=mem.robot_pos.y-fallcounter, z=mem.robot_pos.z}) fallnode = minetest.registered_nodes[fallnode.name] end --If the first nine nodes below the bot are not walkable, then it should assign the definintion to the 10th node. If it too is not walkable, then a "Too Deep" error is returned. if fallnode.walkable == false then return signs_bot.ERROR, "Too deep" end --Designates the node above the walkable node as the new location for the bot. local pos3 = {x=mem.robot_pos.x, y=mem.robot_pos.y-fallcounter+1, z=mem.robot_pos.z} --Turns the bot into a falling node. local sts, _ = minetest.spawn_falling_node(mem.robot_pos) --Stores the data for the node the bot will land in and replaces it with air. This way if the node the bot lands in is occupied by an unwalkable node, such as a rail or sign, --it will be repalced when the bot moves. mem.stored_node = get_node_lvm(pos3) minetest.swap_node(pos3, {name="air"}) if sts then mem.bot_falling = 2 mem.robot_pos = {x=pos3.x, y=pos3.y, z=pos3.z} return signs_bot.BUSY end else mem.bot_falling = mem.bot_falling - 1 if mem.bot_falling <= 0 then mem.bot_falling = nil return signs_bot.DONE end return signs_bot.BUSY end end, }) signs_bot.register_botcommand("pause", { mod = "move", params = "", num_param = 1, description = S("Stop the robot for seconds\n(1..9999)"), check = function(sec) sec = tonumber(sec) or 1 return sec and sec > 0 and sec < 10000 end, cmnd = function(base_pos, mem, sec) if not mem.steps then mem.steps = tonumber(sec) or 1 end mem.steps = mem.steps - 1 if mem.steps == 0 then mem.steps = nil return signs_bot.DONE end if mem.capa then mem.capa = mem.capa + 1 end return signs_bot.BUSY end, }) signs_bot.register_botcommand("stop", { mod = "move", params = "", num_param = 0, description = S("Stop the robot."), cmnd = function(base_pos, mem, slot) local param2 = mem.robot_param2 local pos = mem.robot_pos local node1, pos1 = node_and_pos(lib.next_pos(pos, param2)) if mem.capa then mem.capa = mem.capa + 2 end if node1 and node1.name ~= "signs_bot:sign_stop" then return signs_bot.DONE end return signs_bot.BUSY end, }) signs_bot.register_botcommand("turn_off", { mod = "move", params = "", num_param = 0, description = S("Turn the robot off\n".. "and put it back in the box."), cmnd = function(base_pos, mem) signs_bot.stop_robot(base_pos, mem) return signs_bot.TURN_OFF end, })