forked from Reload/techage_modpack
318 lines
8.4 KiB
Lua
318 lines
8.4 KiB
Lua
--[[
|
|
|
|
Signs Bot
|
|
=========
|
|
|
|
Copyright (C) 2019 Joachim Stolberg
|
|
|
|
GPL v3
|
|
See LICENSE.txt for more information
|
|
|
|
Signs Bot: Library with helper functions
|
|
|
|
]]--
|
|
|
|
-- 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
|
|
|
|
signs_bot.lib = {}
|
|
|
|
local Face2Dir = {[0]=
|
|
{x=0, y=0, z=1},
|
|
{x=1, y=0, z=0},
|
|
{x=0, y=0, z=-1},
|
|
{x=-1, y=0, z=0},
|
|
{x=0, y=-1, z=0},
|
|
{x=0, y=1, z=0}
|
|
}
|
|
|
|
-- allowed for digging
|
|
local NotSoSimpleNodes = {}
|
|
|
|
function signs_bot.lib.register_node_to_be_dug(name)
|
|
NotSoSimpleNodes[name] = true
|
|
end
|
|
|
|
-- Determine the next robot position based on the robot position,
|
|
-- the robot param2.
|
|
function signs_bot.lib.next_pos(pos, param2)
|
|
return vector.add(pos, Face2Dir[param2])
|
|
end
|
|
|
|
-- Determine the destination position based on the robot position,
|
|
-- the robot param2, and a route table like : {0,0,3}
|
|
-- 0 = forward, 1 = right, 2 = backward, 3 = left
|
|
function signs_bot.lib.dest_pos(pos, param2, route)
|
|
local p2 = param2
|
|
for _,dir in ipairs(route) do
|
|
p2 = (param2 + dir) % 4
|
|
pos = vector.add(pos, Face2Dir[p2])
|
|
end
|
|
return pos, p2
|
|
end
|
|
|
|
function signs_bot.lib.get_node_lvm(pos)
|
|
local node = minetest.get_node_or_nil(pos)
|
|
if node then
|
|
return node
|
|
end
|
|
local vm = minetest.get_voxel_manip()
|
|
local MinEdge, MaxEdge = vm:read_from_map(pos, pos)
|
|
local data = vm:get_data()
|
|
local param2_data = vm:get_param2_data()
|
|
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
|
|
local idx = area:indexp(pos)
|
|
node = {
|
|
name = minetest.get_name_from_content_id(data[idx]),
|
|
param2 = param2_data[idx]
|
|
}
|
|
return node
|
|
end
|
|
|
|
local next_pos = signs_bot.lib.next_pos
|
|
local dest_pos = signs_bot.lib.dest_pos
|
|
local get_node_lvm = signs_bot.lib.get_node_lvm
|
|
|
|
local function poke_objects(pos, param2, objects)
|
|
minetest.sound_play('signs_bot_go_away', {pos = pos})
|
|
for _,obj in ipairs(objects) do
|
|
local pos1 = obj:get_pos()
|
|
pos1 = vector.add(pos1, vector.multiply(Face2Dir[param2], 0.2))
|
|
obj:move_to(pos1)
|
|
end
|
|
end
|
|
|
|
-- check if nodeA on posA == air-like and nodeB == solid and no player around
|
|
function signs_bot.lib.check_pos(posA, nodeA, nodeB, param2)
|
|
local ndefA = minetest.registered_nodes[nodeA.name]
|
|
local ndefB = minetest.registered_nodes[nodeB.name]
|
|
if ndefA and not ndefA.walkable and ndefB and ndefB.walkable then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function handle_drop(drop)
|
|
-- To keep it simple, return only the item with the lowest rarity
|
|
if drop.items then
|
|
local rarity = 9999
|
|
local name
|
|
for idx,item in ipairs(drop.items) do
|
|
if item.rarity and item.rarity < rarity then
|
|
rarity = item.rarity
|
|
name = item.items[1] -- take always the first item
|
|
else
|
|
return item.items[1] -- take always the first item
|
|
end
|
|
end
|
|
return name
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Has to be checked before a node is placed
|
|
function signs_bot.lib.is_air_like(pos)
|
|
local node = get_node_lvm(pos)
|
|
local ndef = minetest.registered_nodes[node.name]
|
|
if ndef and ndef.buildable_to then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Has to be checked before a node is dug
|
|
function signs_bot.lib.is_simple_node(node)
|
|
-- don't remove nodes with some intelligence or undiggable nodes
|
|
local ndef = minetest.registered_nodes[node.name]
|
|
if not NotSoSimpleNodes[node.name] then
|
|
if not ndef or node.name == "air" then return false end
|
|
if ndef.drop == "" then return false end
|
|
if ndef.diggable == false then return false end
|
|
if ndef.after_dig_node then return false end
|
|
end
|
|
if type(ndef.drop) == "table" then
|
|
return handle_drop(ndef.drop)
|
|
end
|
|
return ndef.drop or node.name
|
|
end
|
|
|
|
-- Check rights before node is dug or inventory is used
|
|
function signs_bot.lib.not_protected(base_pos, pos)
|
|
local me = M(base_pos):get_string("owner")
|
|
minetest.load_area(pos)
|
|
if minetest.is_protected(pos, me) then
|
|
return false
|
|
end
|
|
local you = M(pos):get_string("owner")
|
|
if you ~= "" and me ~= you then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
|
|
--
|
|
-- Access functions for chest like nodes
|
|
-- (bot inventory releated function are in basis.lua)
|
|
|
|
-- Search for items in the inventory and return the item_name or nil.
|
|
function signs_bot.lib.peek_inv(inv, listname)
|
|
if inv:is_empty(listname) then return nil end
|
|
|
|
local inv_size = inv:get_size(listname)
|
|
|
|
for idx in signs_bot.random(inv_size) do
|
|
local stack = inv:get_stack(listname, idx)
|
|
if stack:get_count() > 0 then
|
|
return stack:get_name()
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- In the case an inventory is full
|
|
function signs_bot.lib.drop_items(robot_pos, items)
|
|
local pos1 = {x=robot_pos.x-1, y=robot_pos.y, z=robot_pos.z-1}
|
|
local pos2 = {x=robot_pos.x+1, y=robot_pos.y, z=robot_pos.z+1}
|
|
for _,pos in ipairs(minetest.find_nodes_in_area(pos1, pos2, {"air"})) do
|
|
minetest.add_item(pos, items)
|
|
return
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- Place/dig signs
|
|
--
|
|
function signs_bot.lib.place_sign(pos, sign, param2)
|
|
if sign:get_name() then
|
|
minetest.set_node(pos, {name=sign:get_name(), param2=param2})
|
|
local ndef = minetest.registered_nodes[sign:get_name()]
|
|
if ndef and ndef.after_place_node then
|
|
ndef.after_place_node(pos, nil, sign)
|
|
end
|
|
end
|
|
end
|
|
|
|
function signs_bot.lib.dig_sign(pos, node)
|
|
node = node or get_node_lvm(pos)
|
|
local nmeta = minetest.get_meta(pos)
|
|
local cmnd = nmeta:get_string("signs_bot_cmnd")
|
|
local sign
|
|
if cmnd ~= "" then
|
|
if node.name == "signs_bot:sign_cmnd" then
|
|
local err_code = nmeta:get_int("err_code")
|
|
local err_msg = nmeta:get_string("err_msg")
|
|
local name = nmeta:get_string("sign_name")
|
|
sign = ItemStack("signs_bot:sign_cmnd")
|
|
local smeta = sign:get_meta()
|
|
smeta:set_string("cmnd", cmnd)
|
|
smeta:set_int("err_code", err_code)
|
|
smeta:set_string("err_msg", err_msg)
|
|
smeta:set_string("description", name)
|
|
else
|
|
sign = ItemStack(node.name)
|
|
end
|
|
minetest.remove_node(pos)
|
|
return sign
|
|
end
|
|
end
|
|
|
|
function signs_bot.lib.after_dig_sign_node(pos, oldnode, oldmetadata, digger)
|
|
local sign = ItemStack(oldnode.name)
|
|
local smeta = sign:get_meta()
|
|
smeta:set_string("cmnd", oldmetadata.fields.signs_bot_cmnd)
|
|
smeta:set_string("description", oldmetadata.fields.sign_name)
|
|
if oldmetadata.fields.err_code then
|
|
smeta:set_int("err_code", tonumber(oldmetadata.fields.err_code))
|
|
smeta:set_string("err_msg", oldmetadata.fields.err_msg or "")
|
|
end
|
|
local inv = minetest.get_inventory({type="player", name=digger:get_player_name()})
|
|
local left_over = inv:add_item("main", sign)
|
|
if left_over:get_count() > 0 then
|
|
minetest.add_item(pos, sign)
|
|
end
|
|
end
|
|
|
|
local function activate_extender_node(pos)
|
|
local node = get_node_lvm(pos)
|
|
if node.name == "signs_bot:sensor_extender" then
|
|
node.name = "signs_bot:sensor_extender_on"
|
|
minetest.swap_node(pos, node)
|
|
minetest.registered_nodes["signs_bot:sensor_extender_on"].after_place_node(pos)
|
|
end
|
|
end
|
|
|
|
local NestedCounter = 0
|
|
function signs_bot.lib.activate_extender_nodes(pos, is_sensor)
|
|
if is_sensor then
|
|
NestedCounter = 0
|
|
else
|
|
NestedCounter = NestedCounter + 1
|
|
if NestedCounter >= 5 then
|
|
return
|
|
end
|
|
end
|
|
activate_extender_node({x=pos.x-1, y=pos.y, z=pos.z})
|
|
activate_extender_node({x=pos.x+1, y=pos.y, z=pos.z})
|
|
activate_extender_node({x=pos.x, y=pos.y, z=pos.z-1})
|
|
activate_extender_node({x=pos.x, y=pos.y, z=pos.z+1})
|
|
end
|
|
|
|
--
|
|
-- Determine the field positions
|
|
--
|
|
local function start_pos(robot_pos, robot_param2, x_size, lvl_offs)
|
|
local pos = next_pos(robot_pos, robot_param2)
|
|
pos = {x=pos.x, y=pos.y+lvl_offs, z=pos.z}
|
|
if x_size == 5 then
|
|
return dest_pos(pos, robot_param2, {3,3})
|
|
else
|
|
return dest_pos(pos, robot_param2, {3})
|
|
end
|
|
end
|
|
|
|
--
|
|
-- Return a table with all positions to copy
|
|
--
|
|
function signs_bot.lib.gen_position_table(robot_pos, robot_param2, x_size, z_size, lvl_offs)
|
|
local tbl = {}
|
|
if robot_pos and robot_param2 and x_size and z_size and lvl_offs then
|
|
local pos = start_pos(robot_pos, robot_param2, x_size, lvl_offs)
|
|
tbl[#tbl+1] = pos
|
|
z_size = math.min(z_size, 5)
|
|
for z = 1,z_size do
|
|
for x = 1,x_size-1 do
|
|
local dir = (z % 2) == 0 and 3 or 1
|
|
pos = dest_pos(pos, robot_param2, {dir})
|
|
tbl[#tbl+1] = pos
|
|
end
|
|
if z < z_size then
|
|
pos = dest_pos(pos, robot_param2, {0})
|
|
tbl[#tbl+1] = pos
|
|
end
|
|
end
|
|
end
|
|
return tbl
|
|
end
|
|
|
|
function signs_bot.lib.trim_text(text)
|
|
local tbl = {}
|
|
for idx,line in ipairs(string.split(text, "[\r\n]+", true, -1, true)) do
|
|
tbl[#tbl+1] = line:trim()
|
|
end
|
|
return table.concat(tbl, "\n")
|
|
end
|
|
|
|
function signs_bot.lib.fake_player(name)
|
|
return {
|
|
get_player_name = function() return name end,
|
|
is_player = function() return false end,
|
|
}
|
|
end
|
|
|
|
signs_bot.lib.register_node_to_be_dug("default:cactus")
|
|
signs_bot.lib.register_node_to_be_dug("default:papyrus")
|