signs_bot-cd2025/commands.lua
2019-03-31 20:35:58 +02:00

601 lines
16 KiB
Lua

--[[
Signs Bot
=========
Copyright (C) 2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
Signs Bot: Robot command interpreter
]]--
-- for lazy programmers
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for intllib.
local MP = minetest.get_modpath("signs_bot")
local I,_ = dofile(MP.."/intllib.lua")
local lib = signs_bot.lib
local tPos2Dir = {l = "l", r = "r", L = "l", R = "l", f = "f", F = "f"}
local tValidLevels = {["-1"] = -1, ["0"] = 0, ["+1"] = 1}
local tCommands = {}
local SortedKeys = {}
local SortedMods = {}
--
-- Command register API function
--
-- def = {
-- mod = "my_mod",
-- params = "<lvl> <slot>",
-- description = "...",
-- check = function(param1 param2) ... return true/false end,
-- cmnd = function(base_pos, mem, param1, param2) ... return true/false end,
-- }
function signs_bot.register_botcommand(name, def)
tCommands[name] = def
tCommands[name].name = name
if not SortedKeys[def.mod] then
SortedKeys[def.mod] = {}
SortedMods[#SortedMods+1] = def.mod
end
local idx = #SortedKeys[def.mod] + 1
SortedKeys[def.mod][idx] = name
end
local function check_cmnd_block(pos, mem, meta)
local cmnd = meta:get_string("signs_bot_cmnd")
if cmnd ~= "" then -- command block?
if meta:get_int("err_code") ~= 0 then -- code not valid?
return false
end
local node = lib.get_node_lvm(pos)
if node.name ~= "signs_bot:box" and mem.robot_param2 ~= node.param2 then -- wrong sign direction?
return false
end
-- read code
mem.lCmnd = {}
for _,s in ipairs(string.split(cmnd, "\n")) do
table.insert(mem.lCmnd, s)
end
return true
end
return false
end
local function trigger_sensor(pos, node)
local meta = M(pos)
local dest_pos = meta:get_string("dest_pos")
local dest_idx = meta:get_int("dest_idx")
if dest_pos ~= "" and dest_idx ~= 0 then
minetest.registered_nodes[node.name].switch_sign_changer(minetest.string_to_pos(dest_pos), dest_idx)
end
end
local function activate_sensor(pos, param2)
local pos1 = lib.next_pos(pos, param2)
local node = lib.get_node_lvm(pos1)
if node.name == "signs_bot:bot_sensor" then
node.name = "signs_bot:bot_sensor_on"
minetest.swap_node(pos1, node)
minetest.registered_nodes[node.name].after_place_node(pos1)
trigger_sensor(pos1, node)
end
end
local function no_sign_around(mem)
if minetest.find_node_near(mem.robot_pos, 1, {
"signs_bot:box", "signs_bot:bot_sensor", "group:sign_bot_sign"}) then -- something around?
local pos1 = lib.next_pos(mem.robot_pos, mem.robot_param2)
local meta = M(pos1)
if check_cmnd_block(pos1, mem, meta) then
return false, true
end
local pos2 = {x=pos1.x, y=pos1.y+1, z=pos1.z}
meta = M(pos2)
if check_cmnd_block(pos2, mem, meta) then
return false, true
end
return true, true
end
return true, false
end
signs_bot.register_botcommand("move", {
mod = "core",
params = "<steps>",
description = I("Move the robot 1..99 steps forward."),
check = function(steps)
steps = tonumber(steps or "1")
return steps ~= nil and steps > 0 and steps < 100
end,
cmnd = function(base_pos, mem, steps)
steps = tonumber(steps or 1)
local no_sign, has_sensor = no_sign_around(mem)
if no_sign then
local new_pos = signs_bot.move_robot(mem.robot_pos, mem.robot_param2)
if new_pos then -- not blocked?
mem.robot_pos = new_pos
if has_sensor then
activate_sensor(mem.robot_pos, (mem.robot_param2 + 1) % 4)
activate_sensor(mem.robot_pos, (mem.robot_param2 + 3) % 4)
end
end
-- more than one move step?
if steps and steps > 1 then
steps = steps - 1
-- add to the command table again
table.insert(mem.lCmnd, 1, "move "..steps)
end
end
return true
end,
})
signs_bot.register_botcommand("turn_left", {
mod = "core",
params = "",
description = I("Turn the robot to the left"),
cmnd = function(base_pos, mem)
mem.robot_param2 = signs_bot.turn_robot(mem.robot_pos, mem.robot_param2, "L")
return true
end,
})
signs_bot.register_botcommand("turn_right", {
mod = "core",
params = "",
description = I("Turn the robot to the right"),
cmnd = function(base_pos, mem)
mem.robot_param2 = signs_bot.turn_robot(mem.robot_pos, mem.robot_param2, "R")
return true
end,
})
signs_bot.register_botcommand("turn_around", {
mod = "core",
params = "",
description = I("Turn the robot around"),
cmnd = function(base_pos, mem)
mem.robot_param2 = signs_bot.turn_robot(mem.robot_pos, mem.robot_param2, "R")
mem.robot_param2 = signs_bot.turn_robot(mem.robot_pos, mem.robot_param2, "R")
return true
end,
})
signs_bot.register_botcommand("backward", {
mod = "core",
params = "",
description = I("Move the robot one step back"),
cmnd = function(base_pos, mem)
local new_pos = signs_bot.backward_robot(mem.robot_pos, mem.robot_param2)
if new_pos then -- not blocked?
mem.robot_pos = new_pos
end
return true
end,
})
signs_bot.register_botcommand("turn_off", {
mod = "core",
params = "",
description = I("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 false
end,
})
signs_bot.register_botcommand("pause", {
mod = "core",
params = "<sec>",
description = I("Stop the robot for <sec> 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)
-- more than one second?
sec = tonumber(sec or 1)
if sec and sec > 1 then
sec = sec - 1
-- add to the command table again
table.insert(mem.lCmnd, 1, "pause "..sec)
end
return true
end,
})
signs_bot.register_botcommand("move_up", {
mod = "core",
params = "",
description = I("Move the robot upwards"),
cmnd = function(base_pos, mem)
local new_pos = signs_bot.robot_up(mem.robot_pos, mem.robot_param2)
if new_pos then -- not blocked?
mem.robot_pos = new_pos
end
return true
end,
})
signs_bot.register_botcommand("move_down", {
mod = "core",
params = "",
description = I("Move the robot down"),
cmnd = function(base_pos, mem)
local new_pos = signs_bot.robot_down(mem.robot_pos, mem.robot_param2)
if new_pos then -- not blocked?
mem.robot_pos = new_pos
end
return true
end,
})
signs_bot.register_botcommand("take_item", {
mod = "core",
params = "<num> <slot>",
description = I("Take <num> items from a chest like node\nand put it into the item inventory.\n"..
"<slot> is the inventory slot (1..8)"),
check = function(num, slot)
num = tonumber(num or 1)
if not num or num < 1 or num > 99 then
return false
end
slot = tonumber(slot or 1)
if not slot or slot < 1 or slot > 8 then
return false
end
return true
end,
cmnd = function(base_pos, mem, num, slot)
num = tonumber(num or 1)
slot = tonumber(slot or 1)
signs_bot.robot_take(base_pos, mem.robot_pos, mem.robot_param2, num, slot)
return true
end,
})
signs_bot.register_botcommand("add_item", {
mod = "core",
params = "<num> <slot>",
description = I("Add <num> items to a chest like node\ntaken from the item inventory.\n"..
"<slot> is the inventory slot (1..8)"),
check = function(num, slot)
num = tonumber(num or 1)
if not num or num < 1 or num > 99 then
return false
end
slot = tonumber(slot or 1)
if not slot or slot < 1 or slot > 8 then
return false
end
return true
end,
cmnd = function(base_pos, mem, num, slot)
num = tonumber(num or 1)
slot = tonumber(slot or 1)
signs_bot.robot_add(base_pos, mem.robot_pos, mem.robot_param2, num, slot)
return true
end,
})
signs_bot.register_botcommand("add_fuel", {
mod = "core",
params = "<num> <slot>",
description = I("Add <num> fuel items to a furnace like node\ntaken from the item inventory.\n"..
"<slot> is the inventory slot (1..8)"),
check = function(num, slot)
num = tonumber(num or 1)
if not num or num < 1 or num > 99 then
return false
end
slot = tonumber(slot or 1)
if not slot or slot < 1 or slot > 8 then
return false
end
return true
end,
cmnd = function(base_pos, mem, num, slot)
num = tonumber(num or 1)
slot = tonumber(slot or 1)
signs_bot.robot_add(base_pos, mem.robot_pos, mem.robot_param2, num, slot)
return true
end,
})
signs_bot.register_botcommand("place_front", {
mod = "core",
params = "<slot> <lvl>",
description = I("Place an item in front of the robot\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot or 1)
if not slot or slot < 1 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot or 1)
local level = tValidLevels[lvl]
signs_bot.place_item(base_pos, mem.robot_pos, mem.robot_param2, slot, "f", level)
return true
end,
})
signs_bot.register_botcommand("place_left", {
mod = "core",
params = "<slot> <lvl>",
description = I("Place an item on the left side\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot or 1)
if not slot or slot < 1 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot or 1)
local level = tValidLevels[lvl]
signs_bot.place_item(base_pos, mem.robot_pos, mem.robot_param2, slot, "l", level)
return true
end,
})
signs_bot.register_botcommand("place_right", {
mod = "core",
params = "<slot> <lvl>",
description = I("Place an item on the right side\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot or 1)
if not slot or slot < 1 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot or 1)
local level = tValidLevels[lvl]
signs_bot.place_item(base_pos, mem.robot_pos, mem.robot_param2, slot, "r", level)
return true
end,
})
signs_bot.register_botcommand("dig_front", {
mod = "core",
params = "<slot> <lvl>",
description = I("Dig an item in front of the robot\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot or 1)
if not slot or slot < 1 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot or 1)
local level = tValidLevels[lvl]
signs_bot.dig_item(base_pos, mem.robot_pos, mem.robot_param2, slot, "f", level)
return true
end,
})
signs_bot.register_botcommand("dig_left", {
mod = "core",
params = "<slot> <lvl>",
description = I("Dig an item on the left side\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot or 1)
if not slot or slot < 1 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot or 1)
local level = tValidLevels[lvl]
signs_bot.dig_item(base_pos, mem.robot_pos, mem.robot_param2, slot, "l", level)
return true
end,
})
signs_bot.register_botcommand("dig_right", {
mod = "core",
params = "<slot> <lvl>",
description = I("Dig an item on the right side\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot or 1)
if not slot or slot < 1 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot or 1)
local level = tValidLevels[lvl]
signs_bot.dig_item(base_pos, mem.robot_pos, mem.robot_param2, slot, "r", level)
return true
end,
})
signs_bot.register_botcommand("rotate_item", {
mod = "core",
params = "<lvl> <steps>",
description = I("Rotate an item in front of the robot\n"..
"<lvl> is one of: -1 0 +1\n"..
"<steps> is one of: 1 2 3"),
check = function(lvl, steps)
steps = tonumber(steps or 1)
if not steps or steps < 1 or steps > 4 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, lvl, steps)
local level = tValidLevels[lvl]
steps = tonumber(steps or 1)
signs_bot.rotate_item(base_pos, mem.robot_pos, mem.robot_param2, "f", level, steps)
return true
end,
})
signs_bot.register_botcommand("place_sign", {
mod = "core",
params = "<slot>",
description = I("Place a sign in front of the robot\ntaken from the signs inventory\n"..
"<slot> is the inventory slot (1..6)"),
check = function(slot)
slot = tonumber(slot or 1)
return slot and slot > 0 and slot < 7
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot or 1)
signs_bot.place_sign(base_pos, mem.robot_pos, mem.robot_param2, slot)
return true
end,
})
signs_bot.register_botcommand("place_sign_behind", {
mod = "core",
params = "<slot>",
description = I("Place a sign behind the robot\ntaken from the signs inventory\n"..
"<slot> is the inventory slot (1..6)"),
check = function(slot)
slot = tonumber(slot or 1)
return slot and slot > 0 and slot < 7
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot or 1)
signs_bot.place_sign_behind(base_pos, mem.robot_pos, mem.robot_param2, slot)
return true
end,
})
signs_bot.register_botcommand("dig_sign", {
mod = "core",
params = "<slot>",
description = I("Dig the sign in front of the robot\n"..
"and add it to the signs inventory.\n"..
"<slot> is the inventory slot (1..6)"),
check = function(slot)
slot = tonumber(slot or 1)
return slot and slot > 0 and slot < 7
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot or 1)
signs_bot.dig_sign(base_pos, mem.robot_pos, mem.robot_param2, slot)
return true
end,
})
signs_bot.register_botcommand("trash_sign", {
mod = "core",
params = "<slot>",
description = I("Dig the sign in front of the robot\n"..
"and add the cleared sign to\nthe item iventory.\n"..
"<slot> is the inventory slot (1..8)"),
check = function(slot)
slot = tonumber(slot or 1)
return slot and slot > 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot or 1)
signs_bot.trash_sign(base_pos, mem.robot_pos, mem.robot_param2, slot)
return true
end,
})
signs_bot.register_botcommand("stop_robot", {
mod = "core",
params = "",
description = I("Stop the robot."),
cmnd = function(base_pos, mem, slot)
mem.lCmnd = {"stop_robot"}
return true
end,
})
function signs_bot.check_commands(pos, text)
--local idx = 1
for idx,line in ipairs(string.split(text, "\n", true)) do
local cmnd, param1, param2, param3 = unpack(string.split(line, " "))
if cmnd ~= "--" and cmnd ~= nil then -- No comment or empty line?
if tCommands[cmnd] then
if tCommands[cmnd].check and not tCommands[cmnd].check(param1, param2, param3) then
return false, I("Parameter error in line ")..idx..":\n"..
cmnd.." "..tCommands[cmnd].params
end
else
return false, I("Command error in line ")..idx..":\n"..line
end
end
--idx = idx + 1
end
return true, I("Checked and approved")
end
function signs_bot.run_next_command(base_pos, mem)
mem.lCmnd = mem.lCmnd or {}
local res = nil
local sts
while res == nil do
local line = table.remove(mem.lCmnd, 1)
if line then
local cmnd, param1, param2 = unpack(string.split(line, " "))
if cmnd ~= "--" and tCommands[cmnd] then -- Valid command?
--sts,res = true, tCommands[cmnd].cmnd(base_pos, mem, param1, param2)
sts, res = pcall(tCommands[cmnd].cmnd, base_pos, mem, param1, param2)
if not sts then
minetest.sound_play('signs_bot_error', {pos = base_pos})
minetest.sound_play('signs_bot_error', {pos = mem.robot_pos})
signs_bot.infotext(base_pos, I("error"))
return false
end
end
else
res = tCommands["move"].cmnd(base_pos, mem)
end
end
return res
end
function signs_bot.get_help_text()
local tbl = {}
for _,mod in ipairs(SortedMods) do
for _,cmnd in ipairs(SortedKeys[mod]) do
local item = tCommands[cmnd]
tbl[#tbl+1] = item.name.." "..item.params
local text = string.gsub(item.description, "\n", "\n -- ")
tbl[#tbl+1] = " -- "..text
end
end
return table.concat(tbl, "\n")
end