diff --git a/cmd_soup.lua b/cmd_soup.lua new file mode 100644 index 0000000..48054c3 --- /dev/null +++ b/cmd_soup.lua @@ -0,0 +1,323 @@ +--[[ + + Signs Bot + ========= + + Copyright (C) 2019-2021 Joachim Stolberg + Copyright (C) 2021 Michal 'Micu' Cieslakiewicz + + GPL v3 + See LICENSE.txt for more information + + Bot soup cooking commands + + Allows bot to use pot (xdecor:cauldron) to cook a vegetable soup + +]]-- + +if not minetest.global_exists("bucket") or not minetest.global_exists("fire") + or not minetest.global_exists("xdecor") then return end + +local M = minetest.get_meta + +-- Load support for I18n. +local S = signs_bot.S + +local lib = signs_bot.lib + +local bucket_empty, bucket_water = ItemStack("bucket:bucket_empty 1"), ItemStack("bucket:bucket_water 1") +local bowl_empty_farming, bowl_empty_xdecor = ItemStack("farming:bowl 1"), ItemStack("xdecor:bowl 1") +local bowl_soup = ItemStack("xdecor:bowl_soup 1") + +local function find_item_slot(base_pos, item) + local inv = M(base_pos):get_inventory() + local s = nil + for i = 1, 8 do + local t = inv:get_stack("main", i) + if t and t:get_name() == item:get_name() then + s = i + break + end + end + return s +end + +signs_bot.register_botcommand("take_water", { + mod = "soup", + params = "", + num_param = 1, + description = S("Take water into empty bucket when standing on a shore\n(use specified slot number or 0 for auto selection)"), + check = function(slot) + slot = tonumber(slot) + return slot and slot >= 0 and slot <= 8 + end, + cmnd = function(base_pos, mem, slot) + slot = tonumber(slot) + local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, { 0 }) + pos.y = pos.y - 1 + if not lib.not_protected(base_pos, pos) then + return signs_bot.ERROR, S("Error: Position protected") + end + local node = minetest.get_node_or_nil(pos) + if not node or node.name ~= "default:water_source" then + return signs_bot.ERROR, S("Error: No still water around") + end + local itemslot = slot + if slot == 0 then + itemslot = find_item_slot(base_pos, bucket_empty) + if not itemslot then + return signs_bot.ERROR, S("Error: No empty bucket in inventory") + end + end + local item = signs_bot.bot_inv_take_item(base_pos, itemslot, 1) + if not item or item:is_empty() or item:get_name() ~= bucket_empty:get_name() then + return signs_bot.ERROR, S("Error: No empty bucket in inventory slot " .. itemslot) + end + item = signs_bot.bot_inv_put_item(base_pos, slot, bucket_water) + if item:is_empty() then + minetest.remove_node(pos) + else + signs_bot.bot_inv_put_item(base_pos, itemslot, bucket_empty) + return signs_bot.ERROR, S("Error: No inventory space for full bucket") + end + return signs_bot.DONE + end, +}) + +signs_bot.register_botcommand("fill_cauldron", { + mod = "soup", + params = "", + num_param = 1, + description = S("Pour water from bucket to empty cauldron in front of a robot\n(use specified slot number or 0 for auto selection)"), + check = function(slot) + slot = tonumber(slot) + return slot and slot >= 0 and slot <= 8 + end, + cmnd = function(base_pos, mem, slot) + slot = tonumber(slot) + local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, { 0 }) + if not lib.not_protected(base_pos, pos) then + return signs_bot.ERROR, S("Error: Position protected") + end + local node = minetest.get_node_or_nil(pos) + if not node or node.name ~= "xdecor:cauldron_empty" then + return signs_bot.ERROR, S("Error: No empty cauldron in front of a robot") + end + local itemslot = slot + if slot == 0 then + itemslot = find_item_slot(base_pos, bucket_water) + if not itemslot then + return signs_bot.ERROR, S("Error: No full bucket in inventory") + end + end + local item = signs_bot.bot_inv_take_item(base_pos, itemslot, 1) + if not item or item:is_empty() or item:get_name() ~= bucket_water:get_name() then + return signs_bot.ERROR, S("Error: No full bucket in inventory slot " .. itemslot) + end + item = signs_bot.bot_inv_put_item(base_pos, slot, bucket_empty) + if item:is_empty() then + minetest.set_node(pos, { name = "xdecor:cauldron_idle", param2 = node.param2 }) + else + signs_bot.bot_inv_put_item(base_pos, itemslot, bucket_water) + return signs_bot.ERROR, S("Error: No inventory space for empty bucket") + end + return signs_bot.DONE + end, +}) + +signs_bot.register_botcommand("flame_on", { + mod = "soup", + params = "", + num_param = 0, + description = S("Set fire under cauldron (requires flammable material)\n(command is ignored when fire is already burning)"), + cmnd = function(base_pos, mem) + local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, { 0 }) + local fire_pos = { x = pos.x, y = pos.y - 1, z = pos.z } + local fuel_pos = { x = pos.x, y = pos.y - 2, z = pos.z } + if not (lib.not_protected(base_pos, pos) and lib.not_protected(base_pos, fire_pos) + and lib.not_protected(base_pos, fuel_pos)) then + return signs_bot.ERROR, S("Error: Position protected") + end + local node = minetest.get_node_or_nil(pos) + if not node or not node.name:find("xdecor:cauldron") then + return signs_bot.ERROR, S("Error: No cauldron in front of a robot") + end + local fire_node = minetest.get_node_or_nil(fire_pos) + if fire_node and fire_node.name:match("fire:[%w_]*flame") then + return signs_bot.DONE + elseif not fire_node or fire_node.name ~= "air" then + return signs_bot.ERROR, S("Error: No space for fire under cauldron") + end + local fuel_node = minetest.get_node_or_nil(fuel_pos) + if fuel_node and minetest.registered_nodes[fuel_node.name].on_ignite then + minetest.registered_nodes[fuel_node.name].on_ignite(fuel_pos) + elseif fuel_node and minetest.get_item_group(fuel_node.name, "flammable") >= 1 then + minetest.set_node(fire_pos, { name = "fire:basic_flame" }) + else + return signs_bot.ERROR, S("Error: No flammable material under cauldron") + end + return signs_bot.DONE + end, +}) + +signs_bot.register_botcommand("flame_off", { + mod = "soup", + params = "", + num_param = 0, + description = S("Put out (extinguish) fire under cauldron\n(command is ignored when there is no fire)"), + cmnd = function(base_pos, mem) + local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, { 0 }) + local fire_pos = { x = pos.x, y = pos.y - 1, z = pos.z } + if not (lib.not_protected(base_pos, pos) and lib.not_protected(base_pos, fire_pos)) then + return signs_bot.ERROR, S("Error: Position protected") + end + local node = minetest.get_node_or_nil(pos) + if not node or not node.name:find("xdecor:cauldron") then + return signs_bot.ERROR, S("Error: No cauldron in front of a robot") + end + local node = minetest.get_node_or_nil(fire_pos) + if node and node.name:match("fire:[%w_]*flame") then + minetest.remove_node(fire_pos) + end + return signs_bot.DONE + end, +}) + +signs_bot.register_botcommand("take_soup", { + mod = "soup", + params = "", + num_param = 1, + description = S("Take boiling soup into empty bowl from cauldron\nin front of a robot\n(use specified slot number or 0 for auto selection)"), + check = function(slot) + slot = tonumber(slot) + return slot and slot >= 0 and slot <= 8 + end, + cmnd = function(base_pos, mem, slot) + slot = tonumber(slot) + local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, { 0 }) + if not lib.not_protected(base_pos, pos) then + return signs_bot.ERROR, S("Error: Position protected") + end + local node = minetest.get_node_or_nil(pos) + if not node or node.name ~= "xdecor:cauldron_soup" then + return signs_bot.ERROR, S("Error: No cauldron with a soup in front of a robot") + end + local itemslot = slot + if slot == 0 then + itemslot = find_item_slot(base_pos, bowl_empty_xdecor) or find_item_slot(base_pos, bowl_empty_farming) + if not itemslot then + return signs_bot.ERROR, S("Error: No empty bowl in inventory") + end + end + local item = signs_bot.bot_inv_take_item(base_pos, itemslot, 1) + if not item or item:is_empty() or (item:get_name() ~= bowl_empty_farming:get_name() + and item:get_name() ~= bowl_empty_xdecor:get_name()) then + return signs_bot.ERROR, S("Error: No empty bowl in inventory slot " .. itemslot) + end + local item_full = signs_bot.bot_inv_put_item(base_pos, slot, bowl_soup) + if item_full:is_empty() then + minetest.set_node(pos, { name = "xdecor:cauldron_empty", param2 = node.param2 }) + else + signs_bot.bot_inv_put_item(base_pos, itemslot, item) + return signs_bot.ERROR, S("Error: No inventory space for full bowl") + end + return signs_bot.DONE + end, +}) + +local CMD_WATER = [[ +dig_sign 1 +move +take_water 1 +backward +place_sign 1 +turn_around +]] + +local CMD_SOUP = [[ +dig_sign 1 +move 2 +fill_cauldron 1 +flame_on +pause 11 +move_up +drop_items 1 2 +drop_items 1 3 +move_down +pause 6 +take_soup 4 +flame_off +backward +backward +place_sign 1 +turn_around +]] + +signs_bot.register_sign({ + name = "water", + description = S('Sign "take water"'), + commands = CMD_WATER, + image = "signs_bot_sign_water.png", +}) + + +signs_bot.register_sign({ + name = "soup", + description = S('Sign "cook soup"'), + commands = CMD_SOUP, + image = "signs_bot_sign_soup.png", +}) + +minetest.register_craft({ + output = "signs_bot:water", + recipe = { + {"group:wood", "bucket:bucket_empty", "group:wood"}, + {"dye:black", "default:stick", "dye:yellow"}, + {"dye:blue", "", ""} + } +}) + +minetest.register_craft({ + output = "signs_bot:soup", + recipe = { + {"group:wood", "xdecor:bowl", "group:wood"}, + {"dye:black", "default:stick", "dye:yellow"}, + {"dye:orange", "", ""} + } +}) + +if minetest.get_modpath("doc") then + doc.add_entry("signs_bot", "water", { + name = S("Sign 'take water'"), + data = { + item = "signs_bot:water", + text = table.concat({ + S("Used to take water into bucket."), + S("Place the sign on a shore, in front of the still water pool."), + S("Items in slots:"), + S(" 1 - empty bucket"), + S("The result is one bucket with water in selected inventory slot."), + S("When finished, the bot turns around."), + }, "\n") + }, + }) + doc.add_entry("signs_bot", "soup", { + name = S("Sign 'cook soup'"), + data = { + item = "signs_bot:soup", + text = table.concat({ + S("Used to cook a vegetable soup in cauldron."), + S("Cauldon should be empty and located above flammable material."), + S("Place the sign in front of the cauldron with one field space,"), + S("to prevent wooden sign from catching fire."), + S("Items in slots:"), + S(" 1 - water bucket"), + S(" 2 - vegetable #1 (i.e. tomato)"), + S(" 3 - vegetable #2 (i.e. carrot)"), + S(" 4 - empty bowl (from farming or xdecor mods)"), + S("The result is one bowl with vegetable soup in selected inventory slot."), + S("When finished, the bot turns around."), + }, "\n") + }, + }) +end diff --git a/init.lua b/init.lua index 98a8ea8..492fd82 100644 --- a/init.lua +++ b/init.lua @@ -52,6 +52,7 @@ dofile(MP.."/cmd_sign.lua") dofile(MP.."/cmd_pattern.lua") dofile(MP.."/cmd_farming.lua") dofile(MP.."/cmd_flowers.lua") +dofile(MP.."/cmd_soup.lua") dofile(MP.."/signal.lua") dofile(MP.."/extender.lua") diff --git a/mod.conf b/mod.conf index fb50f78..7e84aab 100644 --- a/mod.conf +++ b/mod.conf @@ -1,4 +1,4 @@ name=signs_bot depends = default,farming,basic_materials,tubelib2 -optional_depends = node_io,techage,doc,minecart -description = A robot controlled by signs \ No newline at end of file +optional_depends = node_io,techage,doc,minecart,bucket,fire,xdecor +description = A robot controlled by signs diff --git a/textures/signs_bot_sign_soup.png b/textures/signs_bot_sign_soup.png new file mode 100644 index 0000000..b60f960 Binary files /dev/null and b/textures/signs_bot_sign_soup.png differ diff --git a/textures/signs_bot_sign_water.png b/textures/signs_bot_sign_water.png new file mode 100644 index 0000000..272ef34 Binary files /dev/null and b/textures/signs_bot_sign_water.png differ