mirror of
https://github.com/Astatin3/meteorbot-old.git
synced 2026-06-09 00:28:06 -06:00
Initial commit
This commit is contained in:
BIN
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
project(6_DispenserFarmExample)
|
||||
|
||||
set(${PROJECT_NAME}_SOURCE_FILES
|
||||
${PROJECT_SOURCE_DIR}/include/DispenserFarmTasks.hpp
|
||||
|
||||
${PROJECT_SOURCE_DIR}/src/DispenserFarmTasks.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/main.cpp
|
||||
)
|
||||
set(${PROJECT_NAME}_INCLUDE_FOLDERS
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
add_example("${${PROJECT_NAME}_INCLUDE_FOLDERS}" "${${PROJECT_NAME}_SOURCE_FILES}")
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
#include "botcraft/AI/BehaviourClient.hpp"
|
||||
#include "botcraft/AI/Status.hpp"
|
||||
|
||||
/// @brief Check condition
|
||||
/// @param client The client performing the action
|
||||
/// @param radius The max distance to search blocks
|
||||
/// @return Always return Success
|
||||
Botcraft::Status InitializeBlocks(Botcraft::BehaviourClient& client, const int radius = 50);
|
||||
|
||||
/// @brief Turn off the lights of the spawners if not already done
|
||||
/// @param client The client performing the action
|
||||
/// @return Success if the lever is correctly pulled, Failure otherwise
|
||||
Botcraft::Status StartSpawners(Botcraft::BehaviourClient& client);
|
||||
|
||||
/// @brief Check if any mob is wearing frost walker boots
|
||||
/// @param client The client performing the action
|
||||
/// @return Success if there is one such mob, Failure otherwise
|
||||
Botcraft::Status CheckFrostWalkerMob(Botcraft::BehaviourClient& client);
|
||||
|
||||
/// @brief Remove all items except item_to_keep from a chest, destroying the others on a cactus
|
||||
/// @param client The client performing the action
|
||||
/// @param chest_pos_blackboard String name of the position of the chest to process in the blackboard
|
||||
/// @param item_to_keep Name of the item we want to keep
|
||||
/// @return Success if the operation went smoothly, Failure otherwise
|
||||
Botcraft::Status CleanChest(Botcraft::BehaviourClient& client, const std::string& chest_pos_blackboard, const std::string& item_to_keep);
|
||||
|
||||
/// @brief Store a dispenser in the chest found during initialization
|
||||
/// @param client The client performing the action
|
||||
/// @return Success if the dispenser is placed in the chest, Failure otherwise
|
||||
Botcraft::Status StoreDispenser(Botcraft::BehaviourClient& client);
|
||||
|
||||
/// @brief Get one cobblestone from the stone generator
|
||||
/// @param client The client performing the action
|
||||
/// @return Success if one cobblestone is acquired, Failure otherwise
|
||||
Botcraft::Status MineCobblestone(Botcraft::BehaviourClient& client);
|
||||
|
||||
/// @brief Take item in chest until there are N in inventory
|
||||
/// @param client The client performing the action
|
||||
/// @param item_name The item to take
|
||||
/// @param N Number in inventory
|
||||
/// @return Success if N in inventory, Failure otherwise
|
||||
Botcraft::Status TakeFromChest(Botcraft::BehaviourClient& client, const std::string& item_name, const int N);
|
||||
|
||||
/// @brief Copy a random element of a std::vector<T> to a blackboard location
|
||||
/// @tparam T Type of the element
|
||||
/// @param client The client performing the action
|
||||
/// @param src Blackboard source vector location
|
||||
/// @param dst Blackboard single element destination
|
||||
/// @return Always return success
|
||||
template<typename T>
|
||||
Botcraft::Status CopyRandomFromVectorBlackboardData(Botcraft::BehaviourClient& client, const std::string& src, const std::string& dst)
|
||||
{
|
||||
Botcraft::Blackboard& blackboard = client.GetBlackboard();
|
||||
|
||||
const std::vector<T>& source = blackboard.Get<std::vector<T>>(src);
|
||||
if (source.size() == 1)
|
||||
{
|
||||
blackboard.Set<T>(dst, source[0]);
|
||||
return Botcraft::Status::Success;
|
||||
}
|
||||
|
||||
std::mt19937 random_engine(static_cast<unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count()));
|
||||
blackboard.Set<T>(dst, source[std::uniform_int_distribution<>(0, static_cast<int>(source.size()) - 1)(random_engine)]);
|
||||
|
||||
return Botcraft::Status::Success;
|
||||
}
|
||||
|
||||
/// @brief Break carrots/potatoes, collect the items, replant
|
||||
/// @param client The client performing the action
|
||||
/// @param blocks_pos_blackboard Blackboard address of the vector of positions of blocks
|
||||
/// @param item_name Name of the item we want to collect
|
||||
/// @return Success if blocks are broken, items gathered and crops replanted, Failure otherwise
|
||||
Botcraft::Status CollectCropsAndReplant(Botcraft::BehaviourClient& client, const std::string& blocks_pos_blackboard, const std::string& item_name);
|
||||
|
||||
/// @brief Use the cactus to destroy any of these items in the inventory
|
||||
/// @param client The client performing the action
|
||||
/// @param item_name The item to remove from the inventory
|
||||
/// @return Success if no more items in inventory, Failure otherwise
|
||||
Botcraft::Status DestroyItems(Botcraft::BehaviourClient& client, const std::string& item_name);
|
||||
+883
@@ -0,0 +1,883 @@
|
||||
#include "DispenserFarmTasks.hpp"
|
||||
|
||||
#include "botcraft/Game/World/World.hpp"
|
||||
#include "botcraft/Game/Inventory/InventoryManager.hpp"
|
||||
#include "botcraft/Game/Inventory/Window.hpp"
|
||||
#include "botcraft/Game/Entities/EntityManager.hpp"
|
||||
#include "botcraft/Game/Entities/LocalPlayer.hpp"
|
||||
#include "botcraft/Game/Entities/entities/npc/VillagerEntity.hpp"
|
||||
#include "botcraft/Game/Entities/entities/item/ItemEntity.hpp"
|
||||
#include "botcraft/Game/AssetsManager.hpp"
|
||||
#include "botcraft/Utilities/Logger.hpp"
|
||||
|
||||
#include "botcraft/AI/Tasks/AllTasks.hpp"
|
||||
|
||||
using namespace Botcraft;
|
||||
using namespace ProtocolCraft;
|
||||
|
||||
Status InitializeBlocks(BehaviourClient& client, const int radius)
|
||||
{
|
||||
Blackboard& b = client.GetBlackboard();
|
||||
|
||||
Position bed_position;
|
||||
Position crafting_table_position;
|
||||
Position buying_standing_position;
|
||||
Position cactus_position;
|
||||
Position cactus_standing_position;
|
||||
Position output_shulker_position;
|
||||
Position bones_shulker_position;
|
||||
Position rotten_flesh_shulker_position;
|
||||
Position stone_shulker_position;
|
||||
Position stone_standing_position;
|
||||
Position note_block_position;
|
||||
Position despawn_position;
|
||||
Position stone_lever_position;
|
||||
|
||||
std::vector<Position> potato_positions;
|
||||
std::vector<Position> carrot_positions;
|
||||
std::vector<Position> stone_positions;
|
||||
|
||||
const Position my_pos = client.GetLocalPlayer()->GetPosition();
|
||||
|
||||
std::shared_ptr<World> world = client.GetWorld();
|
||||
Position current_pos;
|
||||
for (int x = -radius; x < radius + 1; ++x)
|
||||
{
|
||||
current_pos.x = my_pos.x + x;
|
||||
for (int y = std::max(-radius, world->GetMinY()); y < std::min(world->GetMinY() + world->GetHeight(), radius + 1); ++y)
|
||||
{
|
||||
current_pos.y = my_pos.y + y;
|
||||
for (int z = -radius; z < radius + 1; ++z)
|
||||
{
|
||||
current_pos.z = my_pos.z + z;
|
||||
|
||||
const Blockstate* block = world->GetBlock(current_pos);
|
||||
if (block == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const std::string& block_name = block->GetName();
|
||||
if (block_name == "minecraft:red_bed")
|
||||
{
|
||||
bed_position = current_pos;
|
||||
LOG_INFO("Bed found at: " << current_pos << "!");
|
||||
despawn_position = current_pos + Position(0, 0, 140);
|
||||
LOG_INFO("Despawn position found at: " << current_pos + Position(0, 0, 140) << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:crafting_table")
|
||||
{
|
||||
crafting_table_position = current_pos;
|
||||
buying_standing_position = current_pos + Position(0, 1, 0);
|
||||
LOG_INFO("Crafting table found at: " << current_pos << "!");
|
||||
LOG_INFO("Buying standing found at: " << current_pos + Position(0, 1, 0) << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:cactus")
|
||||
{
|
||||
cactus_position = current_pos;
|
||||
LOG_INFO("Cactus found at: " << current_pos << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:yellow_concrete")
|
||||
{
|
||||
cactus_standing_position = current_pos + Position(0, 1, 0);
|
||||
LOG_INFO("Cactus standing found at: " << current_pos + Position(0, 1, 0) << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:red_shulker_box")
|
||||
{
|
||||
output_shulker_position = current_pos;
|
||||
LOG_INFO("Output shulker position found at: " << current_pos << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:white_shulker_box")
|
||||
{
|
||||
bones_shulker_position = current_pos;
|
||||
LOG_INFO("Bones shulker position found at: " << current_pos << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:orange_shulker_box")
|
||||
{
|
||||
rotten_flesh_shulker_position = current_pos;
|
||||
LOG_INFO("Rotten flesh shulker position found at: " << current_pos << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:light_gray_shulker_box")
|
||||
{
|
||||
stone_shulker_position = current_pos;
|
||||
LOG_INFO("Stone shulker position found at: " << current_pos << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:light_gray_concrete")
|
||||
{
|
||||
stone_standing_position = current_pos + Position(0, 1, 0);
|
||||
LOG_INFO("Stone standing position found at: " << current_pos + Position(0, 1, 0) << "!");
|
||||
for (int i = 1; i < 5; ++i)
|
||||
{
|
||||
stone_positions.push_back(current_pos + Position(0, 2, i));
|
||||
LOG_INFO("Stone position found at: " << current_pos + Position(0, 2, i) << "!");
|
||||
}
|
||||
}
|
||||
else if (block_name == "minecraft:lever")
|
||||
{
|
||||
stone_lever_position = current_pos;
|
||||
LOG_INFO("Stone lever position found at: " << current_pos << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:note_block")
|
||||
{
|
||||
note_block_position = current_pos;
|
||||
LOG_INFO("Note block position found at: " << current_pos << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:potatoes")
|
||||
{
|
||||
potato_positions.push_back(current_pos);
|
||||
LOG_INFO("Potatoes found at: " << current_pos << "!");
|
||||
}
|
||||
else if (block_name == "minecraft:carrots")
|
||||
{
|
||||
carrot_positions.push_back(current_pos);
|
||||
LOG_INFO("Carrots found at: " << current_pos << "!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort stone positions
|
||||
std::sort(stone_positions.begin(), stone_positions.end(), [&](const Position& a, const Position& b)
|
||||
{
|
||||
return a.SqrDist(stone_standing_position) < b.SqrDist(stone_standing_position);
|
||||
});
|
||||
// Sort potato and carrot positions
|
||||
std::sort(potato_positions.begin(), potato_positions.end(), [&](const Position& a, const Position& b)
|
||||
{
|
||||
return a.SqrDist(stone_standing_position) < b.SqrDist(stone_standing_position);
|
||||
});
|
||||
std::sort(carrot_positions.begin(), carrot_positions.end(), [&](const Position& a, const Position& b)
|
||||
{
|
||||
return a.SqrDist(stone_standing_position) < b.SqrDist(stone_standing_position);
|
||||
});
|
||||
|
||||
std::vector<int> farmer_id;
|
||||
std::vector<int> fletcher_id;
|
||||
std::vector<int> cleric_id;
|
||||
std::vector<int> shepperd_id;
|
||||
std::vector<int> toolsmith_id;
|
||||
{
|
||||
std::shared_ptr<EntityManager> entity_manager = client.GetEntityManager();
|
||||
auto entities = entity_manager->GetEntities();
|
||||
for (const auto& [id, entity] : *entities)
|
||||
{
|
||||
if (entity->GetType() == EntityType::Villager)
|
||||
{
|
||||
std::shared_ptr<VillagerEntity> villager = std::static_pointer_cast<VillagerEntity>(entity);
|
||||
if (villager->GetDataVillagerData().profession == 4)
|
||||
{
|
||||
cleric_id.push_back(id);
|
||||
LOG_INFO("Cleric found with id: " << id);
|
||||
}
|
||||
else if (villager->GetDataVillagerData().profession == 5)
|
||||
{
|
||||
farmer_id.push_back(id);
|
||||
LOG_INFO("Farmer found with id: " << id);
|
||||
}
|
||||
else if (villager->GetDataVillagerData().profession == 7)
|
||||
{
|
||||
fletcher_id.push_back(id);
|
||||
LOG_INFO("Fletcher found with id: " << id);
|
||||
}
|
||||
else if (villager->GetDataVillagerData().profession == 12)
|
||||
{
|
||||
shepperd_id.push_back(id);
|
||||
LOG_INFO("Shepperd found with id: " << id);
|
||||
}
|
||||
else if (villager->GetDataVillagerData().profession == 13)
|
||||
{
|
||||
toolsmith_id.push_back(id);
|
||||
LOG_INFO("Toolsmith found with id: " << id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
b.Set("DispenserFarmBot.bed_position", bed_position);
|
||||
b.Set("DispenserFarmBot.crafting_table_position", crafting_table_position);
|
||||
b.Set("DispenserFarmBot.buying_standing_position", buying_standing_position);
|
||||
b.Set("DispenserFarmBot.cactus_position", cactus_position);
|
||||
b.Set("DispenserFarmBot.cactus_standing_position", cactus_standing_position);
|
||||
b.Set("DispenserFarmBot.output_shulker_position", output_shulker_position);
|
||||
b.Set("DispenserFarmBot.bones_shulker_position", bones_shulker_position);
|
||||
b.Set("DispenserFarmBot.rotten_flesh_shulker_position", rotten_flesh_shulker_position);
|
||||
b.Set("DispenserFarmBot.stone_shulker_position", stone_shulker_position);
|
||||
b.Set("DispenserFarmBot.stone_standing_position", stone_standing_position);
|
||||
b.Set("DispenserFarmBot.note_block_position", note_block_position);
|
||||
b.Set("DispenserFarmBot.despawn_position", despawn_position);
|
||||
b.Set("DispenserFarmBot.stone_lever_position", stone_lever_position);
|
||||
|
||||
b.Set("DispenserFarmBot.potato_positions", potato_positions);
|
||||
b.Set("DispenserFarmBot.carrot_positions", carrot_positions);
|
||||
b.Set("DispenserFarmBot.stone_positions", stone_positions);
|
||||
|
||||
b.Set("DispenserFarmBot.farmer_id", farmer_id);
|
||||
b.Set("DispenserFarmBot.fletcher_id", fletcher_id);
|
||||
b.Set("DispenserFarmBot.cleric_id", cleric_id);
|
||||
b.Set("DispenserFarmBot.shepperd_id", shepperd_id);
|
||||
b.Set("DispenserFarmBot.toolsmith_id", toolsmith_id);
|
||||
|
||||
|
||||
b.Set("DispenserFarmBot.initialized", true);
|
||||
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
Status StartSpawners(BehaviourClient& client)
|
||||
{
|
||||
std::shared_ptr<World> world = client.GetWorld();
|
||||
Blackboard& blackboard = client.GetBlackboard();
|
||||
|
||||
const Position& lever_pos = blackboard.Get<Position>("DispenserFarmBot.stone_lever_position");
|
||||
|
||||
const Blockstate* block = world->GetBlock(lever_pos);
|
||||
|
||||
if (block == nullptr || block->GetName() != "minecraft:lever")
|
||||
{
|
||||
LOG_WARNING("Error trying to start spawners, no lever at " << lever_pos);
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
if (block->GetVariableValue("powered") == "true")
|
||||
{
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
// PULL THE LEVER, KRONK!
|
||||
if (InteractWithBlock(client, lever_pos, Direction::Down, true) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to pull the lever during spawners activation");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
client.Yield();
|
||||
}
|
||||
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
Status CheckFrostWalkerMob(BehaviourClient& client)
|
||||
{
|
||||
std::shared_ptr<EntityManager> entity_manager = client.GetEntityManager();
|
||||
auto entities = entity_manager->GetEntities();
|
||||
for (const auto& [id, entity] : *entities)
|
||||
{
|
||||
if (entity->IsMonster())
|
||||
{
|
||||
const Slot boots = entity->GetEquipment(EquipmentSlot::Boots);
|
||||
if (!boots.IsEmptySlot())
|
||||
{
|
||||
if (boots.GetNBT().contains("Enchantments") && boots.GetNBT()["Enchantments"].is_list_of<NBT::TagCompound>())
|
||||
{
|
||||
for (const auto& enchantment : boots.GetNBT()["Enchantments"].as_list_of<NBT::TagCompound>())
|
||||
{
|
||||
if (enchantment.contains("id") &&
|
||||
enchantment["id"].is<std::string>() &&
|
||||
enchantment["id"].get<std::string>() == "minecraft:frost_walker")
|
||||
{
|
||||
return Status::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
Status CleanChest(BehaviourClient& client, const std::string& chest_pos_blackboard, const std::string& item_to_keep)
|
||||
{
|
||||
Blackboard& blackboard = client.GetBlackboard();
|
||||
|
||||
// Set input parameters for debugger
|
||||
blackboard.Set<std::string>("CleanChest.chest_pos_blackboard", chest_pos_blackboard);
|
||||
blackboard.Set<std::string>("CleanChest.item_to_keep", item_to_keep);
|
||||
|
||||
std::shared_ptr<InventoryManager> inventory_manager = client.GetInventoryManager();
|
||||
|
||||
const Position& pos = blackboard.Get<Position>(chest_pos_blackboard);
|
||||
const Position& cactus_standing_pos = blackboard.Get<Position>("DispenserFarmBot.cactus_standing_position");
|
||||
const Position& cactus_pos = blackboard.Get<Position>("DispenserFarmBot.cactus_position");
|
||||
|
||||
bool has_wrong_items = true;
|
||||
while (has_wrong_items)
|
||||
{
|
||||
if (OpenContainer(client, pos) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Failed to open container for cleaning");
|
||||
return Status::Failure;
|
||||
}
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
client.Yield();
|
||||
}
|
||||
|
||||
const short container_id = inventory_manager->GetFirstOpenedWindowId();
|
||||
const std::shared_ptr<Window> container = inventory_manager->GetWindow(container_id);
|
||||
|
||||
if (container == nullptr)
|
||||
{
|
||||
LOG_WARNING("Container closed during chest cleaning");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
std::vector<short> slots_to_remove;
|
||||
std::vector<short> free_slots_inventory;
|
||||
std::vector<short> trash_inventory_slots;
|
||||
// Get slots to remove and free slots in inventory
|
||||
for (const auto& [id, slot] : container->GetSlots())
|
||||
{
|
||||
if (id < container->GetFirstPlayerInventorySlot() && !slot.IsEmptySlot() &&
|
||||
#if PROTOCOL_VERSION < 340 /* < 1.12.2 */
|
||||
AssetsManager::getInstance().Items().at(slot.GetBlockID()).at(slot.GetItemDamage())->GetName() != item_to_keep
|
||||
#else
|
||||
AssetsManager::getInstance().Items().at(slot.GetItemID())->GetName() != item_to_keep
|
||||
#endif
|
||||
)
|
||||
{
|
||||
slots_to_remove.push_back(id);
|
||||
}
|
||||
else if (id >= container->GetFirstPlayerInventorySlot() &&
|
||||
slot.IsEmptySlot())
|
||||
{
|
||||
free_slots_inventory.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (free_slots_inventory.size() == 0)
|
||||
{
|
||||
LOG_WARNING("Can't clean chest, no free slot in inventory to take items");
|
||||
CloseContainer(client, container_id);
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
// Go through as many slots as possible and transfer them in the inventory
|
||||
int index = 0;
|
||||
while (index < slots_to_remove.size() && index < free_slots_inventory.size())
|
||||
{
|
||||
if (SwapItemsInContainer(client, container_id, slots_to_remove[index], free_slots_inventory[index]) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to swap transfer items from chest to inventory");
|
||||
CloseContainer(client, container_id);
|
||||
return Status::Failure;
|
||||
}
|
||||
trash_inventory_slots.push_back(free_slots_inventory[index] - container->GetFirstPlayerInventorySlot() + 9/*Window::INVENTORY_STORAGE_START*/);
|
||||
index++;
|
||||
}
|
||||
|
||||
CloseContainer(client, container_id);
|
||||
|
||||
if (trash_inventory_slots.size() == 0)
|
||||
{
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
has_wrong_items = index < slots_to_remove.size();
|
||||
|
||||
// Now go to the cactus to destroy them
|
||||
if (GoTo(client, cactus_standing_pos, 0, 0, 0) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to go to the cactus standing position");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
LookAt(client, Vector3<double>(0.5) + cactus_pos, false);
|
||||
|
||||
for (int i = 0; i < trash_inventory_slots.size(); ++i)
|
||||
{
|
||||
if (DropItemsFromContainer(client, 0/*Window::PLAYER_INVENTORY_INDEX*/, trash_inventory_slots[i]) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to drop the items on the cactus");
|
||||
return Status::Failure;
|
||||
}
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
client.Yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
Status StoreDispenser(BehaviourClient& client)
|
||||
{
|
||||
Blackboard& b = client.GetBlackboard();
|
||||
std::shared_ptr<InventoryManager> inventory_manager = client.GetInventoryManager();
|
||||
|
||||
if (OpenContainer(client, b.Get<Position>("DispenserFarmBot.output_shulker_position")) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Can't open output chest to store crafted dispenser");
|
||||
return Status::Failure;
|
||||
}
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
client.Yield();
|
||||
}
|
||||
|
||||
const short container_id = inventory_manager->GetFirstOpenedWindowId();
|
||||
const std::shared_ptr<Window> container = inventory_manager->GetWindow(container_id);
|
||||
|
||||
if (container == nullptr)
|
||||
{
|
||||
LOG_WARNING("Container closed during dispenser storing");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
short dst_slot = -1;
|
||||
short src_slot = -1;
|
||||
const auto dispenser_id = AssetsManager::getInstance().GetItemID("minecraft:dispenser");
|
||||
for (const auto& [idx, slot] : container->GetSlots())
|
||||
{
|
||||
if (dst_slot == -1 && idx < container->GetFirstPlayerInventorySlot() &&
|
||||
(slot.IsEmptySlot() || (
|
||||
#if PROTOCOL_VERSION < 340 /* < 1.12.2 */
|
||||
slot.GetBlockID() == dispenser_id.first && slot.GetItemDamage() == dispenser_id.second
|
||||
#else
|
||||
slot.GetItemID() == dispenser_id
|
||||
#endif
|
||||
&& slot.GetItemCount() < AssetsManager::getInstance().Items().at(dispenser_id)->GetStackSize() - 1))
|
||||
)
|
||||
{
|
||||
dst_slot = idx;
|
||||
}
|
||||
else if (src_slot == -1 && idx >= container->GetFirstPlayerInventorySlot() &&
|
||||
#if PROTOCOL_VERSION < 340 /* < 1.12.2 */
|
||||
slot.GetBlockID() == dispenser_id.first && slot.GetItemDamage() == dispenser_id.second
|
||||
#else
|
||||
slot.GetItemID() == dispenser_id
|
||||
#endif
|
||||
)
|
||||
{
|
||||
src_slot = idx;
|
||||
}
|
||||
else if (src_slot != -1 && dst_slot != -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No dispenser in inventory, nothing to do
|
||||
if (src_slot == -1)
|
||||
{
|
||||
CloseContainer(client);
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
if (dst_slot == -1)
|
||||
{
|
||||
LOG_WARNING("Can't find a place for the dispenser in the chest");
|
||||
CloseContainer(client);
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
if (PutOneItemInContainerSlot(client, container_id, src_slot, dst_slot) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to transfer dispenser into chest");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
CloseContainer(client);
|
||||
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
Status MineCobblestone(BehaviourClient& client)
|
||||
{
|
||||
std::shared_ptr<World> world = client.GetWorld();
|
||||
Blackboard& blackboard = client.GetBlackboard();
|
||||
|
||||
const std::vector<Position>& stone_positions = blackboard.Get<std::vector<Position>>("DispenserFarmBot.stone_positions");
|
||||
|
||||
const Position* mining_pos = nullptr;
|
||||
{
|
||||
for (int i = 0; i < stone_positions.size(); ++i)
|
||||
{
|
||||
const Blockstate* block = world->GetBlock(stone_positions[i]);
|
||||
// Depending on the tick on which lava flows we could also get some cobble
|
||||
if (block != nullptr && (block->GetName() == "minecraft:stone" || block->GetName() == "minecraft:cobblestone"))
|
||||
{
|
||||
mining_pos = stone_positions.data() + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No stone, trigger the note block
|
||||
if (!mining_pos)
|
||||
{
|
||||
const Position& note_block_pos = blackboard.Get<Position>("DispenserFarmBot.note_block_position");
|
||||
if (InteractWithBlock(client, note_block_pos, Direction::Down, true) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to activate the stone note block");
|
||||
return Status::Failure;
|
||||
}
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
client.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until we have one stone available (should be quick)
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (!mining_pos)
|
||||
{
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() >= 5000)
|
||||
{
|
||||
LOG_WARNING("Error waiting for stone block (Timeout).");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
for (int i = 0; i < stone_positions.size(); ++i)
|
||||
{
|
||||
const Blockstate* block = world->GetBlock(stone_positions[i]);
|
||||
// Depending on the tick on which lava flows we could also get some cobble
|
||||
if (block != nullptr && (block->GetName() == "minecraft:stone" || block->GetName() == "minecraft:cobblestone"))
|
||||
{
|
||||
mining_pos = stone_positions.data() + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mining_pos)
|
||||
{
|
||||
client.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
if (Dig(client, *mining_pos, true, Direction::North) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error digging stone.");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
const Position& stone_shulker_position = blackboard.Get<Position>("DispenserFarmBot.stone_shulker_position");
|
||||
if (OpenContainer(client, stone_shulker_position) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to open stone shulker.");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
std::shared_ptr<InventoryManager> inventory_manager = client.GetInventoryManager();
|
||||
const short container_id = inventory_manager->GetFirstOpenedWindowId();
|
||||
const std::shared_ptr<Window> shulker_container = inventory_manager->GetWindow(container_id);
|
||||
|
||||
if (shulker_container == nullptr)
|
||||
{
|
||||
LOG_WARNING("Container closed during stone gathering");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
short src_slot = -1;
|
||||
short dst_slot = -1;
|
||||
start = std::chrono::steady_clock::now();
|
||||
// Wait for a cobblestone stack to be in the shulker
|
||||
while (src_slot == -1)
|
||||
{
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() >= 5000)
|
||||
{
|
||||
LOG_WARNING("Error waiting for stone item in shulker (Timeout).");
|
||||
CloseContainer(client, container_id);
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
dst_slot = -1;
|
||||
int quantity = 0;
|
||||
for (const auto& [idx, slot] : shulker_container->GetSlots())
|
||||
{
|
||||
if (src_slot == -1 && idx < shulker_container->GetFirstPlayerInventorySlot())
|
||||
{
|
||||
#if PROTOCOL_VERSION < 340 /* < 1.12.2 */
|
||||
if (!slot.IsEmptySlot() && AssetsManager::getInstance().Items().at(slot.GetBlockID()).at(slot.GetItemDamage())->GetName() == "minecraft:cobblestone")
|
||||
#else
|
||||
if (!slot.IsEmptySlot() && AssetsManager::getInstance().Items().at(slot.GetItemID())->GetName() == "minecraft:cobblestone")
|
||||
#endif
|
||||
{
|
||||
src_slot = idx;
|
||||
quantity = slot.GetItemCount();
|
||||
if (dst_slot != -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (src_slot != -1 && idx >= shulker_container->GetFirstPlayerInventorySlot())
|
||||
{
|
||||
if (slot.IsEmptySlot() || (slot.GetItemCount() < 64 - quantity &&
|
||||
#if PROTOCOL_VERSION < 340 /* < 1.12.2 */
|
||||
AssetsManager::getInstance().Items().at(slot.GetBlockID()).at(slot.GetItemDamage())->GetName() == "minecraft:cobblestone"
|
||||
#else
|
||||
AssetsManager::getInstance().Items().at(slot.GetItemID())->GetName() == "minecraft:cobblestone"
|
||||
#endif
|
||||
))
|
||||
{
|
||||
dst_slot = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (src_slot != -1 && dst_slot == -1)
|
||||
{
|
||||
LOG_WARNING("Error trying to take stone from shulker, no slot available");
|
||||
CloseContainer(client, container_id);
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
if (src_slot != -1 && dst_slot != -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
client.Yield();
|
||||
}
|
||||
|
||||
if (SwapItemsInContainer(client, container_id, src_slot, dst_slot) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error getting the stone from the shulker");
|
||||
CloseContainer(client, container_id);
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
CloseContainer(client, container_id);
|
||||
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
Status TakeFromChest(BehaviourClient& client, const std::string& item_name, const int N)
|
||||
{
|
||||
Blackboard& blackboard = client.GetBlackboard();
|
||||
|
||||
// Set input parameters for debugger
|
||||
blackboard.Set<std::string>("TakeFromChest.item_name", item_name);
|
||||
blackboard.Set<int>("TakeFromChest.N", N);
|
||||
|
||||
std::shared_ptr<InventoryManager> inventory_manager = client.GetInventoryManager();
|
||||
|
||||
const short container_id = inventory_manager->GetFirstOpenedWindowId();
|
||||
const std::shared_ptr<Window> container = inventory_manager->GetWindow(container_id);
|
||||
|
||||
if (container == nullptr)
|
||||
{
|
||||
LOG_WARNING("Container closed during in chest item taking");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
std::vector<short> available_slots;
|
||||
std::vector<short> to_take_slots;
|
||||
int num_in_inventory = 0;
|
||||
for (const auto& [idx, slot] : container->GetSlots())
|
||||
{
|
||||
if (!slot.IsEmptySlot() &&
|
||||
#if PROTOCOL_VERSION < 340 /* < 1.12.2 */
|
||||
AssetsManager::getInstance().Items().at(slot.GetBlockID()).at(slot.GetItemDamage())->GetName() == item_name
|
||||
#else
|
||||
AssetsManager::getInstance().Items().at(slot.GetItemID())->GetName() == item_name
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (idx < container->GetFirstPlayerInventorySlot())
|
||||
{
|
||||
to_take_slots.push_back(idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
num_in_inventory += slot.GetItemCount();
|
||||
}
|
||||
}
|
||||
else if (idx >= container->GetFirstPlayerInventorySlot() &&
|
||||
slot.IsEmptySlot())
|
||||
{
|
||||
available_slots.push_back(idx);
|
||||
}
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
while (num_in_inventory < N && index < to_take_slots.size() && index < available_slots.size())
|
||||
{
|
||||
if (SwapItemsInContainer(client, container_id, to_take_slots[index], available_slots[index]) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to take an item from a chest");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
num_in_inventory += container->GetSlot(available_slots[index]).GetItemCount();
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return num_in_inventory >= N ? Status::Success : Status::Failure;
|
||||
}
|
||||
|
||||
Status CollectCropsAndReplant(BehaviourClient& client, const std::string& blocks_pos_blackboard, const std::string& item_name)
|
||||
{
|
||||
Blackboard& blackboard = client.GetBlackboard();
|
||||
|
||||
// Set input parameters for debugger
|
||||
blackboard.Set<std::string>("CollectCropsAndReplant.blocks_pos_blackboard", blocks_pos_blackboard);
|
||||
blackboard.Set<std::string>("CollectCropsAndReplant.item_name", item_name);
|
||||
|
||||
std::shared_ptr<World> world = client.GetWorld();
|
||||
|
||||
const std::vector<Position>& positions = blackboard.Get<std::vector<Position>>(blocks_pos_blackboard);
|
||||
|
||||
for (int i = 0; i < positions.size(); ++i)
|
||||
{
|
||||
const Blockstate* block = world->GetBlock(positions[i]);
|
||||
const bool ready_to_harvest = block != nullptr && !block->IsAir() && block->GetVariableValue("age") == "7";
|
||||
|
||||
if (ready_to_harvest)
|
||||
{
|
||||
if (Dig(client, positions[i], true) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to break a crop");
|
||||
return Status::Failure;
|
||||
}
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
client.Yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<EntityManager> entity_manager = client.GetEntityManager();
|
||||
|
||||
std::map<int, Vector3<double>> entity_positions;
|
||||
|
||||
bool moving_items = true;
|
||||
while (moving_items)
|
||||
{
|
||||
moving_items = false;
|
||||
entity_positions.clear();
|
||||
{
|
||||
auto entities = entity_manager->GetEntities();
|
||||
for (const auto& [id, entity] : *entities)
|
||||
{
|
||||
if (entity->GetType() == EntityType::ItemEntity)
|
||||
{
|
||||
std::shared_ptr<ItemEntity> item = std::static_pointer_cast<ItemEntity>(entity);
|
||||
#if PROTOCOL_VERSION < 340 /* < 1.12.2 */
|
||||
if (AssetsManager::getInstance().Items().at(item->GetDataItem().GetBlockID()).at(item->GetDataItem().GetItemDamage())->GetName() == item_name)
|
||||
#else
|
||||
if (AssetsManager::getInstance().Items().at(item->GetDataItem().GetItemID())->GetName() == item_name)
|
||||
#endif
|
||||
{
|
||||
entity_positions[id] = entity->GetPosition();
|
||||
moving_items = entity->GetSpeed().SqrNorm() > 0.0001;
|
||||
if (moving_items)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
client.Yield();
|
||||
}
|
||||
|
||||
for (const auto& e : entity_positions)
|
||||
{
|
||||
if (GoTo(client, e.second, 2) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to pick up a crop item (can't get close enough to " << e.second << ")");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (true)
|
||||
{
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() >= 5000)
|
||||
{
|
||||
LOG_WARNING("Error waiting for crop item pick-up (Timeout).");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
{
|
||||
if (entity_manager->GetEntity(e.first) == nullptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
client.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = positions.size() - 1; i >= 0; --i)
|
||||
{
|
||||
const Blockstate* block = world->GetBlock(positions[i]);
|
||||
const bool to_replant = block != nullptr && block->IsAir();
|
||||
|
||||
if (to_replant)
|
||||
{
|
||||
if (PlaceBlock(client, item_name, positions[i]) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to replant a crop");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
client.Yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
Status DestroyItems(BehaviourClient& client, const std::string& item_name)
|
||||
{
|
||||
Blackboard& blackboard = client.GetBlackboard();
|
||||
|
||||
// Set input parameters for debugger
|
||||
blackboard.Set<std::string>("DestroyItems.item_name", item_name);
|
||||
|
||||
std::shared_ptr<InventoryManager> inventory_manager = client.GetInventoryManager();
|
||||
std::shared_ptr<Window> inventory = inventory_manager->GetPlayerInventory();
|
||||
|
||||
const Position& cactus_standing_pos = blackboard.Get<Position>("DispenserFarmBot.cactus_standing_position");
|
||||
const Position& cactus_pos = blackboard.Get<Position>("DispenserFarmBot.cactus_position");
|
||||
|
||||
std::vector<short> slots_to_remove;
|
||||
for (const auto& [idx, slot] : inventory->GetSlots())
|
||||
{
|
||||
if (idx >= inventory->GetFirstPlayerInventorySlot() && !slot.IsEmptySlot() &&
|
||||
#if PROTOCOL_VERSION < 340 /* < 1.12.2 */
|
||||
AssetsManager::getInstance().Items().at(slot.GetBlockID()).at(slot.GetItemDamage())->GetName() == item_name
|
||||
#else
|
||||
AssetsManager::getInstance().Items().at(slot.GetItemID())->GetName() == item_name
|
||||
#endif
|
||||
)
|
||||
{
|
||||
slots_to_remove.push_back(idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (slots_to_remove.size() == 0)
|
||||
{
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
|
||||
if (GoTo(client, cactus_standing_pos, 0, 0, 0) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to go to the cactus standing position");
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
LookAt(client, Vector3<double>(0.5) + cactus_pos, false);
|
||||
|
||||
for (int i = 0; i < slots_to_remove.size(); ++i)
|
||||
{
|
||||
if (DropItemsFromContainer(client, Window::PLAYER_INVENTORY_INDEX, slots_to_remove[i]) == Status::Failure)
|
||||
{
|
||||
LOG_WARNING("Error trying to drop the items on the cactus");
|
||||
return Status::Failure;
|
||||
}
|
||||
}
|
||||
return Status::Success;
|
||||
}
|
||||
@@ -0,0 +1,494 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "botcraft/AI/SimpleBehaviourClient.hpp"
|
||||
#include "botcraft/AI/Tasks/AllTasks.hpp"
|
||||
#include "botcraft/Utilities/Logger.hpp"
|
||||
#include "botcraft/Utilities/StdAnyUtilities.hpp"
|
||||
|
||||
#include "DispenserFarmTasks.hpp"
|
||||
|
||||
|
||||
void ShowHelp(const char* argv0)
|
||||
{
|
||||
std::cout << "Usage: " << argv0 << " <options>\n"
|
||||
<< "Options:\n"
|
||||
<< "\t-h, --help\tShow this help message\n"
|
||||
<< "\t--address\tAddress of the server you want to connect to, default: 127.0.0.1:25565\n"
|
||||
<< "\t--login\t\tPlayer name in offline mode, empty for Microsoft account, default: BCDispenserGuy\n"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void RegisterBlackboardTypesToDebugger();
|
||||
|
||||
struct Args
|
||||
{
|
||||
bool help = false;
|
||||
std::string address = "127.0.0.1:25565";
|
||||
std::string login = "BCDispenserGuy";
|
||||
|
||||
int return_code = 0;
|
||||
};
|
||||
|
||||
Args ParseCommandLine(int argc, char* argv[]);
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateTree();
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Init logging, log everything >= Info, only to console, no file
|
||||
Botcraft::Logger::GetInstance().SetLogLevel(Botcraft::LogLevel::Info);
|
||||
Botcraft::Logger::GetInstance().SetFilename("");
|
||||
// Add a name to this thread for logging
|
||||
Botcraft::Logger::GetInstance().RegisterThread("main");
|
||||
// Add some types to blackboard debugger
|
||||
RegisterBlackboardTypesToDebugger();
|
||||
|
||||
Args args;
|
||||
if (argc == 1)
|
||||
{
|
||||
LOG_WARNING("No command arguments. Using default options.");
|
||||
ShowHelp(argv[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
args = ParseCommandLine(argc, argv);
|
||||
if (args.help)
|
||||
{
|
||||
ShowHelp(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (args.return_code != 0)
|
||||
{
|
||||
return args.return_code;
|
||||
}
|
||||
}
|
||||
|
||||
auto dispenser_farm_tree = CreateTree();
|
||||
|
||||
Botcraft::SimpleBehaviourClient client(true);
|
||||
client.SetAutoRespawn(true);
|
||||
|
||||
LOG_INFO("Starting connection process");
|
||||
client.Connect(args.address, args.login);
|
||||
|
||||
// Wait 10 seconds then start executing the tree
|
||||
Botcraft::Utilities::SleepFor(std::chrono::seconds(10));
|
||||
client.SetBehaviourTree(dispenser_farm_tree);
|
||||
|
||||
client.RunBehaviourUntilClosed();
|
||||
|
||||
client.Disconnect();
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LOG_FATAL("Exception: " << e.what());
|
||||
return 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_FATAL("Unknown exception");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterBlackboardTypesToDebugger()
|
||||
{
|
||||
// Register a custom parser for small vectors of int (to print them in the blackboard debugger)
|
||||
Botcraft::Utilities::AnyParser::RegisterType<std::vector<int>>([](const std::any& f) {
|
||||
const std::vector<int>& v = std::any_cast<const std::vector<int>&>(f);
|
||||
if (v.size() > 10)
|
||||
{
|
||||
return Botcraft::Utilities::AnyParser::DefaultToString(f);
|
||||
}
|
||||
std::stringstream s;
|
||||
s << '[';
|
||||
for (size_t i = 0; i < v.size(); ++i)
|
||||
{
|
||||
s << v[i] << (i == v.size() - 1 ? ']' : ',');
|
||||
}
|
||||
return s.str();
|
||||
});
|
||||
// Register a custom parser for small vectors of Position (to print them in the blackboard debugger)
|
||||
Botcraft::Utilities::AnyParser::RegisterType(std::type_index(typeid(std::vector<Botcraft::Position>)), [](const std::any& f) {
|
||||
const std::vector<Botcraft::Position>& v = std::any_cast<const std::vector<Botcraft::Position>&>(f);
|
||||
if (v.size() > 10)
|
||||
{
|
||||
return "Vector of " + std::to_string(v.size()) + " block Positions";
|
||||
}
|
||||
std::stringstream s;
|
||||
s << "[\n";
|
||||
for (size_t i = 0; i < v.size(); ++i)
|
||||
{
|
||||
s << "\t" << v[i] << (i == v.size() - 1 ? "\n]" : ",\n");
|
||||
}
|
||||
return s.str();
|
||||
});
|
||||
}
|
||||
|
||||
Args ParseCommandLine(int argc, char* argv[])
|
||||
{
|
||||
Args args;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
std::string arg = argv[i];
|
||||
if (arg == "-h" || arg == "--help")
|
||||
{
|
||||
ShowHelp(argv[0]);
|
||||
args.help = true;
|
||||
return args;
|
||||
}
|
||||
else if (arg == "--address")
|
||||
{
|
||||
if (i + 1 < argc)
|
||||
{
|
||||
args.address = argv[++i];
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_FATAL("--address requires an argument");
|
||||
args.return_code = 1;
|
||||
return args;
|
||||
}
|
||||
}
|
||||
else if (arg == "--login")
|
||||
{
|
||||
if (i + 1 < argc)
|
||||
{
|
||||
args.login = argv[++i];
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_FATAL("--login requires an argument");
|
||||
args.return_code = 1;
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateBuyTree(const std::string& item_name, const std::string& blackboard_entity_location)
|
||||
{
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("buy " + item_name + " tree")
|
||||
// Buy an item if we don't have one already
|
||||
.selector()
|
||||
.leaf(Botcraft::HasItemInInventory, item_name, 1)
|
||||
.sequence()
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.buying_standing_position", "GoTo.goal")
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.dist_tolerance", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist_xz", 0)
|
||||
.leaf("go to buying position", Botcraft::GoToBlackboard)
|
||||
.leaf(CopyRandomFromVectorBlackboardData<int>, blackboard_entity_location, "InteractEntity.entity_id")
|
||||
.leaf(Botcraft::SetBlackboardData<bool>, "InteractEntity.swing", true)
|
||||
.leaf("open trading interface", Botcraft::InteractEntityBlackboard)
|
||||
.repeater(100).leaf(Botcraft::Yield)
|
||||
.selector()
|
||||
// If trading fail, we still want to close the container
|
||||
.leaf("buy item", Botcraft::TradeName, item_name, true, -1)
|
||||
.leaf([](Botcraft::SimpleBehaviourClient& c) { Botcraft::CloseContainer(c, -1); return Botcraft::Status::Failure;})
|
||||
.end()
|
||||
.leaf("close trading interface", Botcraft::CloseContainer, -1)
|
||||
.end()
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateRottenFleshEmeraldTree()
|
||||
{
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("collect and sell rotten flesh")
|
||||
.sequence()
|
||||
.selector()
|
||||
// If we don't have rotten flesh, take some in the chest
|
||||
.leaf(Botcraft::HasItemInInventory, "minecraft:rotten_flesh", 64)
|
||||
.sequence()
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.buying_standing_position", "GoTo.goal")
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.dist_tolerance", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist_xz", 0)
|
||||
.leaf(Botcraft::GoToBlackboard)
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.rotten_flesh_shulker_position", "OpenContainer.pos")
|
||||
.leaf(Botcraft::OpenContainerBlackboard)
|
||||
.repeater(100).leaf(Botcraft::Yield)
|
||||
.succeeder().leaf("take rotten flesh", TakeFromChest, "minecraft:rotten_flesh", 64)
|
||||
.leaf("close rotten flesh container", Botcraft::CloseContainer, -1)
|
||||
.end()
|
||||
.end()
|
||||
// Trade some rotten flesh
|
||||
.repeater(0).inverter().sequence()
|
||||
.leaf(Botcraft::HasItemInInventory, "minecraft:rotten_flesh", 32)
|
||||
.leaf(CopyRandomFromVectorBlackboardData<int>, "DispenserFarmBot.cleric_id", "InteractEntity.entity_id")
|
||||
.leaf(Botcraft::SetBlackboardData<bool>, "InteractEntity.swing", true)
|
||||
.leaf("open trading interface", Botcraft::InteractEntityBlackboard)
|
||||
.selector()
|
||||
// If trading fail, we still want to close the container
|
||||
.leaf("sell rotten flesh", Botcraft::TradeName, "minecraft:rotten_flesh", false, -1)
|
||||
.leaf([](Botcraft::SimpleBehaviourClient& c) { Botcraft::CloseContainer(c, -1); return Botcraft::Status::Failure;})
|
||||
.end()
|
||||
.leaf("close trading interface", Botcraft::CloseContainer, -1)
|
||||
.repeater(50).leaf(Botcraft::Yield)
|
||||
.end()
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateBonesEmeraldTree()
|
||||
{
|
||||
std::array<std::array<std::string, 3>, 3> recipe_bone_meal, recipe_white_dye;
|
||||
recipe_bone_meal[0][0] = "minecraft:bone";
|
||||
recipe_white_dye[0][0] = "minecraft:bone_meal";
|
||||
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("collect and sell bones")
|
||||
.sequence()
|
||||
.selector()
|
||||
// If we don't have white dye, take some bones in the chest
|
||||
.leaf(Botcraft::HasItemInInventory, "minecraft:white_dye", 64)
|
||||
.sequence()
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.buying_standing_position", "GoTo.goal")
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.dist_tolerance", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist_xz", 0)
|
||||
.leaf(Botcraft::GoToBlackboard)
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.bones_shulker_position", "OpenContainer.pos")
|
||||
.leaf("open bones container", Botcraft::OpenContainerBlackboard)
|
||||
.repeater(100).leaf(Botcraft::Yield)
|
||||
.succeeder().leaf("take bones", TakeFromChest, "minecraft:bone", 32)
|
||||
.leaf("close bones container", Botcraft::CloseContainer, -1)
|
||||
.end()
|
||||
.end()
|
||||
// While we have at least one bone
|
||||
// Craft as many bone meal as possible
|
||||
.repeater(0).inverter().sequence()
|
||||
.leaf(Botcraft::HasItemInInventory, "minecraft:bone", 1)
|
||||
.leaf("craft bone meal", Botcraft::CraftNamed, recipe_bone_meal, true)
|
||||
.end()
|
||||
// While we have at least one bone meal
|
||||
// Craft as many white dye as possible
|
||||
.repeater(0).inverter().sequence()
|
||||
.leaf(Botcraft::HasItemInInventory, "minecraft:bone_meal", 1)
|
||||
.leaf("craft white dye", Botcraft::CraftNamed, recipe_white_dye, true)
|
||||
.end()
|
||||
// While we have some white dye, sell them
|
||||
.repeater(0).inverter().sequence()
|
||||
.leaf(Botcraft::HasItemInInventory, "minecraft:white_dye", 12)
|
||||
.leaf(CopyRandomFromVectorBlackboardData<int>, "DispenserFarmBot.shepperd_id", "InteractEntity.entity_id")
|
||||
.leaf(Botcraft::SetBlackboardData<bool>, "InteractEntity.swing", true)
|
||||
.leaf("open trading interface", Botcraft::InteractEntityBlackboard)
|
||||
.selector()
|
||||
// If trading fail, we still want to close the container
|
||||
.leaf("sell white dye", Botcraft::TradeName, "minecraft:white_dye", false, -1)
|
||||
.leaf([](Botcraft::SimpleBehaviourClient& c) { Botcraft::CloseContainer(c, -1); return Botcraft::Status::Failure;})
|
||||
.end()
|
||||
.leaf("close trading interface", Botcraft::CloseContainer, -1)
|
||||
.repeater(50).leaf(Botcraft::Yield)
|
||||
.end()
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateCropEmeraldTree(const std::string& item_name, const std::string& blackboard_crops_location)
|
||||
{
|
||||
int min_item_to_sell = item_name == "minecraft:carrot" ? 22 : 26;
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("collect and sell " + item_name)
|
||||
.sequence()
|
||||
// Collect crops if we don't have some
|
||||
.selector()
|
||||
.leaf(Botcraft::HasItemInInventory, item_name, 64)
|
||||
.leaf("collect crops and replant", CollectCropsAndReplant, blackboard_crops_location, item_name)
|
||||
.end()
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.buying_standing_position", "GoTo.goal")
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.dist_tolerance", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist_xz", 0)
|
||||
.leaf("go to selling position", Botcraft::GoToBlackboard)
|
||||
// Sell crops
|
||||
.repeater(0).inverter().sequence()
|
||||
.leaf("check if enough crop to sell", Botcraft::HasItemInInventory, item_name, min_item_to_sell)
|
||||
.leaf(CopyRandomFromVectorBlackboardData<int>, "DispenserFarmBot.farmer_id", "InteractEntity.entity_id")
|
||||
.leaf(Botcraft::SetBlackboardData<bool>, "InteractEntity.swing", true)
|
||||
.leaf("interact with villager", Botcraft::InteractEntityBlackboard)
|
||||
.repeater(100).leaf(Botcraft::Yield)
|
||||
.selector()
|
||||
// If trading fail, we still want to close the container
|
||||
.leaf("trade", Botcraft::TradeName, item_name, false, -1)
|
||||
.leaf([](Botcraft::SimpleBehaviourClient& c) { Botcraft::CloseContainer(c, -1); return Botcraft::Status::Failure;})
|
||||
.end()
|
||||
.leaf("close trading interface", Botcraft::CloseContainer, -1)
|
||||
.end()
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateEatTree()
|
||||
{
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("eat")
|
||||
.selector()
|
||||
// If hungry
|
||||
.inverter().leaf("check is hungry", Botcraft::IsHungry, 20)
|
||||
// Go buy some food, then eat
|
||||
.sequence()
|
||||
.leaf("check if has 3 emeralds in inventory", Botcraft::HasItemInInventory, "minecraft:emerald", 3)
|
||||
.tree(CreateBuyTree("minecraft:golden_carrot", "DispenserFarmBot.farmer_id"))
|
||||
.leaf("eat", Botcraft::Eat, "minecraft:golden_carrot", true)
|
||||
.end()
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateCollectCobblestoneTree()
|
||||
{
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("collect cobblestone")
|
||||
.selector()
|
||||
// If we already have engouh, don't enter the loop
|
||||
.leaf("check if >=7 cobblestone in inventory", Botcraft::HasItemInInventory, "minecraft:cobblestone", 7)
|
||||
// Repeat 10 times (7 + 3 in case something goes wrong)
|
||||
.repeater(10).selector()
|
||||
// If already 7 in inventory, no need to get more
|
||||
.leaf("check if >=7 cobblestone in inventory", Botcraft::HasItemInInventory, "minecraft:cobblestone", 7)
|
||||
.sequence()
|
||||
// Get a pickaxe if don't have one already
|
||||
.selector()
|
||||
.leaf("set pickaxe in hand", Botcraft::SetItemInHand, "minecraft:stone_pickaxe", Botcraft::Hand::Right)
|
||||
.sequence()
|
||||
.leaf("check if has 1 emerald in inventory", Botcraft::HasItemInInventory, "minecraft:emerald", 1)
|
||||
.tree(CreateBuyTree("minecraft:stone_pickaxe", "DispenserFarmBot.toolsmith_id"))
|
||||
.leaf("set pick in hand", Botcraft::SetItemInHand, "minecraft:stone_pickaxe", Botcraft::Hand::Right)
|
||||
.end()
|
||||
.end()
|
||||
// Go to the mining position
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.stone_standing_position", "GoTo.goal")
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.dist_tolerance", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist_xz", 0)
|
||||
.leaf("go to mining position", Botcraft::GoToBlackboard)
|
||||
// Get cobblestone
|
||||
.leaf("mine cobblestone", MineCobblestone)
|
||||
.end()
|
||||
.end()
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateCraftDispenserTree()
|
||||
{
|
||||
std::array<std::array<std::string, 3>, 3> dispenser_recipe;
|
||||
dispenser_recipe[0][0] = "minecraft:cobblestone";
|
||||
dispenser_recipe[0][1] = "minecraft:cobblestone";
|
||||
dispenser_recipe[0][2] = "minecraft:cobblestone";
|
||||
dispenser_recipe[1][0] = "minecraft:cobblestone";
|
||||
dispenser_recipe[1][1] = "minecraft:bow";
|
||||
dispenser_recipe[1][2] = "minecraft:cobblestone";
|
||||
dispenser_recipe[2][0] = "minecraft:cobblestone";
|
||||
dispenser_recipe[2][1] = "minecraft:redstone";
|
||||
dispenser_recipe[2][2] = "minecraft:cobblestone";
|
||||
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("craft dispenser")
|
||||
.sequence()
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.buying_standing_position", "GoTo.goal")
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.dist_tolerance", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist_xz", 0)
|
||||
.leaf("go to crafting table", Botcraft::GoToBlackboard)
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.crafting_table_position", "OpenContainer.pos")
|
||||
.leaf("open crafting table interface", Botcraft::OpenContainerBlackboard)
|
||||
.repeater(100).leaf(Botcraft::Yield)
|
||||
.leaf("craft dispenser", Botcraft::CraftNamed, dispenser_recipe, false)
|
||||
.leaf("close container", Botcraft::CloseContainer, -1)
|
||||
.selector()
|
||||
.leaf("store dispenser", StoreDispenser)
|
||||
// Stop all behaviour if output chest is potentially full
|
||||
.sequence()
|
||||
.leaf(Botcraft::Say, "Error trying to put dispenser in output chest, stopping behaviour...")
|
||||
.leaf([](Botcraft::SimpleBehaviourClient& c) { c.SetBehaviourTree(nullptr); return Botcraft::Status::Success; })
|
||||
.leaf(Botcraft::Yield)
|
||||
.end()
|
||||
.end()
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateCleanStorageTree()
|
||||
{
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("clean storage")
|
||||
.sequence()
|
||||
.leaf("clean bones chest", CleanChest, "DispenserFarmBot.bones_shulker_position", "minecraft:bone")
|
||||
.leaf("clean rotten flesh chest", CleanChest, "DispenserFarmBot.rotten_flesh_shulker_position", "minecraft:rotten_flesh")
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateDespawnFrostWalkerTree()
|
||||
{
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("despawn frost walker mobs")
|
||||
.selector()
|
||||
.inverter().leaf("check for frost walker mobs", CheckFrostWalkerMob)
|
||||
.sequence()
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.despawn_position", "GoTo.goal")
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.dist_tolerance", 1)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist_xz", 0)
|
||||
.leaf("go to despawn position", Botcraft::GoToBlackboard)
|
||||
.end()
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateSleepTree()
|
||||
{
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("sleep")
|
||||
.selector()
|
||||
// If it's night
|
||||
.inverter().leaf("check if night", Botcraft::IsNightTime)
|
||||
.sequence()
|
||||
// Go to the bed
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.bed_position", "GoTo.goal")
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.dist_tolerance", 2)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist", 0)
|
||||
.leaf(Botcraft::SetBlackboardData<int>, "GoTo.min_end_dist_xz", 0)
|
||||
.leaf("go to bed", Botcraft::GoToBlackboard)
|
||||
// Right click the bed every second until it's day time
|
||||
.repeater(0).sequence()
|
||||
.leaf(Botcraft::CopyBlackboardData, "DispenserFarmBot.bed_position", "InteractWithBlock.pos")
|
||||
.leaf(Botcraft::SetBlackboardData<bool>, "InteractWithBlock.animation", true)
|
||||
.leaf("interact with bed", Botcraft::InteractWithBlockBlackboard)
|
||||
// Wait ~1s
|
||||
.repeater(100).leaf(Botcraft::Yield)
|
||||
.inverter().leaf("check if night", Botcraft::IsNightTime)
|
||||
.end()
|
||||
.end()
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateCollectEmeraldsTree()
|
||||
{
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("collect emeralds")
|
||||
.sequence()
|
||||
.succeeder().tree(CreateBonesEmeraldTree())
|
||||
.succeeder().tree(CreateRottenFleshEmeraldTree())
|
||||
.succeeder().tree(CreateCropEmeraldTree("minecraft:potato", "DispenserFarmBot.potato_positions"))
|
||||
.succeeder().tree(CreateCropEmeraldTree("minecraft:carrot", "DispenserFarmBot.carrot_positions"))
|
||||
.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Botcraft::BehaviourTree<Botcraft::SimpleBehaviourClient>> CreateTree()
|
||||
{
|
||||
return Botcraft::Builder<Botcraft::SimpleBehaviourClient>("main")
|
||||
.sequence()
|
||||
.selector()
|
||||
.leaf("check if initialized in blackboard", Botcraft::CheckBlackboardBoolData, "DispenserFarmBot.initialized")
|
||||
.sequence()
|
||||
.leaf("initialize blocks", InitializeBlocks, 50)
|
||||
.leaf("start spawners", StartSpawners)
|
||||
.end()
|
||||
.end()
|
||||
.tree(CreateCollectEmeraldsTree())
|
||||
.tree(CreateCleanStorageTree())
|
||||
.leaf("sort inventory", Botcraft::SortInventory)
|
||||
.leaf("destroy poisonous potatoes", DestroyItems, "minecraft:poisonous_potato")
|
||||
.tree(CreateDespawnFrostWalkerTree())
|
||||
.tree(CreateSleepTree())
|
||||
.tree(CreateEatTree())
|
||||
.leaf("check 2 emeralds in inventory", Botcraft::HasItemInInventory, "minecraft:emerald", 2)
|
||||
.tree(CreateBuyTree("minecraft:bow", "DispenserFarmBot.fletcher_id"))
|
||||
.leaf("check 1 emerald in inventory", Botcraft::HasItemInInventory, "minecraft:emerald", 1)
|
||||
.tree(CreateBuyTree("minecraft:redstone", "DispenserFarmBot.cleric_id"))
|
||||
.tree(CreateCollectCobblestoneTree())
|
||||
.tree(CreateCraftDispenserTree())
|
||||
.end();
|
||||
}
|
||||
Reference in New Issue
Block a user