import * as mc from "../../utils/mcdata.js"; import * as world from "./world.js"; import pf from 'mineflayer-pathfinder'; import Vec3 from 'vec3'; export function log(bot, message, chat=false) { bot.output += message + '\n'; if (chat) bot.chat(message); } async function autoLight(bot) { if (bot.modes.isOn('torch_placing') && !bot.interrupt_code) { let nearest_torch = world.getNearestBlock(bot, 'torch', 6); if (!nearest_torch) { let has_torch = bot.inventory.items().find(item => item.name === 'torch'); const curr_block = agent.bot.blockAt(pos); if (has_torch && curr_block.name === 'air') { try { log(bot, `Placing torch at ${bot.entity.position}.`); return await placeBlock(bot, 'torch', bot.entity.position.x, bot.entity.position.y, bot.entity.position.z); } catch (err) {return true;} } } } return false; } async function equipHighestAttack(bot) { let weapons = bot.inventory.items().filter(item => item.name.includes('sword') || (item.name.includes('axe') && !item.name.includes('pickaxe'))); if (weapons.length === 0) weapons = bot.inventory.items().filter(item => item.name.includes('pickaxe') || item.name.includes('shovel')); if (weapons.length === 0) return; weapons.sort((a, b) => a.attackDamage < b.attackDamage); let weapon = weapons[0]; if (weapon) await bot.equip(weapon, 'hand'); } export async function craftRecipe(bot, itemName, num=1) { /** * Attempt to craft the given item name from a recipe. May craft many items. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} itemName, the item name to craft. * @returns {Promise} true if the recipe was crafted, false otherwise. * @example * await skills.craftRecipe(bot, "stick"); **/ let placedTable = false; // get recipes that don't require a crafting table let recipes = bot.recipesFor(mc.getItemId(itemName), null, 1, null); let craftingTable = null; if (!recipes || recipes.length === 0) { // Look for crafting table craftingTable = world.getNearestBlock(bot, 'crafting_table', 8); if (craftingTable === null){ // Try to place crafting table let hasTable = world.getInventoryCounts(bot)['crafting_table'] > 0; if (hasTable) { let pos = world.getNearestFreeSpace(bot, 1, 6); await placeBlock(bot, 'crafting_table', pos.x, pos.y, pos.z); craftingTable = world.getNearestBlock(bot, 'crafting_table', 8); if (craftingTable) { recipes = bot.recipesFor(mc.getItemId(itemName), null, 1, craftingTable); placedTable = true; } } else { log(bot, `You either do not have enough resources to craft ${itemName} or it requires a crafting table.`) return false; } } else { recipes = bot.recipesFor(mc.getItemId(itemName), null, 1, craftingTable); } } if (!recipes || recipes.length === 0) { log(bot, `You do not have the resources to craft a ${itemName}.`); if (placedTable) { await collectBlock(bot, 'crafting_table', 1); } return false; } const recipe = recipes[0]; console.log('crafting...'); await bot.craft(recipe, num, craftingTable); log(bot, `Successfully crafted ${itemName}, you now have ${world.getInventoryCounts(bot)[itemName]} ${itemName}.`); if (placedTable) { await collectBlock(bot, 'crafting_table', 1); } return true; } export async function smeltItem(bot, itemName, num=1) { /** * Puts 1 coal in furnace and smelts the given item name, waits until the furnace runs out of fuel or input items. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} itemName, the item name to smelt. Ores must contain "raw" like raw_iron. * @param {number} num, the number of items to smelt. Defaults to 1. * @returns {Promise} true if the item was smelted, false otherwise. Fail * @example * await skills.smeltItem(bot, "raw_iron"); * await skills.smeltItem(bot, "beef"); **/ const foods = ['beef', 'chicken', 'cod', 'mutton', 'porkchop', 'rabbit', 'salmon', 'tropical_fish']; if (!itemName.includes('raw') && !foods.includes(itemName)) { log(bot, `Cannot smelt ${itemName}, must be a "raw" item, like "raw_iron".`); return false; } // TODO: allow cobblestone, sand, clay, etc. let placedFurnace = false; let furnaceBlock = undefined; furnaceBlock = world.getNearestBlock(bot, 'furnace', 6); if (!furnaceBlock){ // Try to place furnace let hasFurnace = world.getInventoryCounts(bot)['furnace'] > 0; if (hasFurnace) { let pos = world.getNearestFreeSpace(bot, 1, 6); await placeBlock(bot, 'furnace', pos.x, pos.y, pos.z); furnaceBlock = world.getNearestBlock(bot, 'furnace', 6); placedFurnace = true; } } if (!furnaceBlock){ log(bot, `There is no furnace nearby and you have no furnace.`) return false; } await bot.lookAt(furnaceBlock.position); console.log('smelting...'); const furnace = await bot.openFurnace(furnaceBlock); // check if the furnace is already smelting something let input_item = furnace.inputItem(); if (input_item && input_item.type !== mc.getItemId(itemName) && input_item.count > 0) { // TODO: check if furnace is currently burning fuel. furnace.fuel is always null, I think there is a bug. // This only checks if the furnace has an input item, but it may not be smelting it and should be cleared. log(bot, `The furnace is currently smelting ${mc.getItemName(input_item.type)}.`); if (placedFurnace) await collectBlock(bot, 'furnace', 1); return false; } // check if the bot has enough items to smelt let inv_counts = world.getInventoryCounts(bot); if (!inv_counts[itemName] || inv_counts[itemName] < num) { log(bot, `You do not have enough ${itemName} to smelt.`); if (placedFurnace) await collectBlock(bot, 'furnace', 1); return false; } // fuel the furnace if (!furnace.fuelItem()) { let fuel = bot.inventory.items().find(item => item.name === 'coal' || item.name === 'charcoal'); let put_fuel = Math.ceil(num / 8); if (!fuel || fuel.count < put_fuel) { log(bot, `You do not have enough coal or charcoal to smelt ${num} ${itemName}, you need ${put_fuel} coal or charcoal`); if (placedFurnace) await collectBlock(bot, 'furnace', 1); return false; } await furnace.putFuel(fuel.type, null, put_fuel); log(bot, `Added ${put_fuel} ${mc.getItemName(fuel.type)} to furnace fuel.`); console.log(`Added ${put_fuel} ${mc.getItemName(fuel.type)} to furnace fuel.`) } // put the items in the furnace await furnace.putInput(mc.getItemId(itemName), null, num); // wait for the items to smelt let total = 0; let collected_last = true; let smelted_item = null; await new Promise(resolve => setTimeout(resolve, 200)); while (total < num) { await new Promise(resolve => setTimeout(resolve, 10000)); console.log('checking...'); let collected = false; if (furnace.outputItem()) { smelted_item = await furnace.takeOutput(); if (smelted_item) { total += smelted_item.count; collected = true; } } if (!collected && !collected_last) { break; // if nothing was collected this time or last time } collected_last = collected; if (bot.interrupt_code) { break; } } if (placedFurnace) { await collectBlock(bot, 'furnace', 1); } if (total === 0) { log(bot, `Failed to smelt ${itemName}.`); return false; } if (total < num) { log(bot, `Only smelted ${total} ${mc.getItemName(smelted_item.type)}.`); return false; } log(bot, `Successfully smelted ${itemName}, got ${total} ${mc.getItemName(smelted_item.type)}.`); return true; } export async function clearNearestFurnace(bot) { /** * Clears the nearest furnace of all items. * @param {MinecraftBot} bot, reference to the minecraft bot. * @returns {Promise} true if the furnace was cleared, false otherwise. * @example * await skills.clearNearestFurnace(bot); **/ let furnaceBlock = world.getNearestBlock(bot, 'furnace', 6); if (!furnaceBlock){ log(bot, `There is no furnace nearby.`) return false; } console.log('clearing furnace...'); const furnace = await bot.openFurnace(furnaceBlock); console.log('opened furnace...') // take the items out of the furnace let smelted_item, intput_item, fuel_item; if (furnace.outputItem()) smelted_item = await furnace.takeOutput(); if (furnace.inputItem()) intput_item = await furnace.takeInput(); if (furnace.fuelItem()) fuel_item = await furnace.takeFuel(); console.log(smelted_item, intput_item, fuel_item) let smelted_name = smelted_item ? `${smelted_item.count} ${smelted_item.name}` : `0 smelted items`; let input_name = intput_item ? `${intput_item.count} ${intput_item.name}` : `0 input items`; let fuel_name = fuel_item ? `${fuel_item.count} ${fuel_item.name}` : `0 fuel items`; log(bot, `Cleared furnace, recieved ${smelted_name}, ${input_name}, and ${fuel_name}.`); return true; } export async function attackNearest(bot, mobType, kill=true) { /** * Attack mob of the given type. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} mobType, the type of mob to attack. * @param {boolean} kill, whether or not to continue attacking until the mob is dead. Defaults to true. * @returns {Promise} true if the mob was attacked, false if the mob type was not found. * @example * await skills.attackNearest(bot, "zombie", true); **/ bot.modes.pause('cowardice'); const mob = world.getNearbyEntities(bot, 24).find(entity => entity.name === mobType); if (mob) { return await attackEntity(bot, mob, kill); } log(bot, 'Could not find any '+mobType+' to attack.'); return false; } export async function attackEntity(bot, entity, kill=true) { /** * Attack mob of the given type. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {Entity} entity, the entity to attack. * @returns {Promise} true if the entity was attacked, false if interrupted * @example * await skills.attackEntity(bot, entity); **/ let pos = entity.position; console.log(bot.entity.position.distanceTo(pos)) await equipHighestAttack(bot) if (!kill) { if (bot.entity.position.distanceTo(pos) > 5) { console.log('moving to mob...') await goToPosition(bot, pos.x, pos.y, pos.z); } console.log('attacking mob...') await bot.attack(entity); } else { bot.pvp.attack(entity); while (world.getNearbyEntities(bot, 24).includes(entity)) { await new Promise(resolve => setTimeout(resolve, 1000)); if (bot.interrupt_code) { bot.pvp.stop(); return false; } } log(bot, `Successfully killed ${entity.name}.`); await pickupNearbyItems(bot); return true; } } export async function defendSelf(bot, range=9) { /** * Defend yourself from all nearby hostile mobs until there are no more. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {number} range, the range to look for mobs. Defaults to 8. * @returns {Promise} true if the bot found any enemies and has killed them, false if no entities were found. * @example * await skills.defendSelf(bot); * **/ bot.modes.pause('self_defense'); bot.modes.pause('cowardice'); let attacked = false; let enemy = world.getNearestEntityWhere(bot, entity => mc.isHostile(entity), range); while (enemy) { await equipHighestAttack(bot); if (bot.entity.position.distanceTo(enemy.position) > 4 && enemy.name !== 'creeper' && enemy.name !== 'phantom') { try { bot.pathfinder.setMovements(new pf.Movements(bot)); await bot.pathfinder.goto(new pf.goals.GoalFollow(enemy, 2), true); } catch (err) {/* might error if entity dies, ignore */} } bot.pvp.attack(enemy); attacked = true; await new Promise(resolve => setTimeout(resolve, 500)); enemy = world.getNearestEntityWhere(bot, entity => mc.isHostile(entity), range); if (bot.interrupt_code) { bot.pvp.stop(); return false; } } bot.pvp.stop(); if (attacked) log(bot, `Successfully defended self.`); else log(bot, `No enemies nearby to defend self from.`); return attacked; } export async function collectBlock(bot, blockType, num=1, exclude=null) { /** * Collect one of the given block type. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} blockType, the type of block to collect. * @param {number} num, the number of blocks to collect. Defaults to 1. * @returns {Promise} true if the block was collected, false if the block type was not found. * @example * await skills.collectBlock(bot, "oak_log"); **/ if (num < 1) { log(bot, `Invalid number of blocks to collect: ${num}.`); return false; } let blocktypes = [blockType]; if (blockType.endsWith('ore')) blocktypes.push('deepslate_'+blockType); if (blockType === 'dirt') blocktypes.push('grass_block'); let collected = 0; for (let i=0; i block.position.x !== position.x || block.position.y !== position.y || block.position.z !== position.z ); } } if (blocks.length === 0) { if (collected === 0) log(bot, `No ${blockType} nearby to collect.`); else log(bot, `No more ${blockType} nearby to collect.`); break; } const block = blocks[0]; await bot.tool.equipForBlock(block); const itemId = bot.heldItem ? bot.heldItem.type : null if (!block.canHarvest(itemId)) { log(bot, `Don't have right tools to harvest ${blockType}.`); return false; } try { await bot.collectBlock.collect(block); collected++; await autoLight(bot); } catch (err) { if (err.name === 'NoChests') { log(bot, `Failed to collect ${blockType}: Inventory full, no place to deposit.`); break; } else { log(bot, `Failed to collect ${blockType}: ${err}.`); continue; } } if (bot.interrupt_code) break; } log(bot, `Collected ${collected} ${blockType}.`); return collected > 0; } export async function pickupNearbyItems(bot) { /** * Pick up all nearby items. * @param {MinecraftBot} bot, reference to the minecraft bot. * @returns {Promise} true if the items were picked up, false otherwise. * @example * await skills.pickupNearbyItems(bot); **/ const distance = 8; const getNearestItem = bot => bot.nearestEntity(entity => entity.name === 'item' && bot.entity.position.distanceTo(entity.position) < distance); let nearestItem = getNearestItem(bot); let pickedUp = 0; while (nearestItem) { bot.pathfinder.setMovements(new pf.Movements(bot)); await bot.pathfinder.goto(new pf.goals.GoalFollow(nearestItem, 0.8), true); await new Promise(resolve => setTimeout(resolve, 200)); let prev = nearestItem; nearestItem = getNearestItem(bot); if (prev === nearestItem) { break; } pickedUp++; } log(bot, `Picked up ${pickedUp} items.`); return true; } export async function breakBlockAt(bot, x, y, z) { /** * Break the block at the given position. Will use the bot's equipped item. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {number} x, the x coordinate of the block to break. * @param {number} y, the y coordinate of the block to break. * @param {number} z, the z coordinate of the block to break. * @returns {Promise} true if the block was broken, false otherwise. * @example * let position = world.getPosition(bot); * await skills.breakBlockAt(bot, position.x, position.y - 1, position.x); **/ if (x == null || y == null || z == null) throw new Error('Invalid position to break block at.'); let block = bot.blockAt(Vec3(x, y, z)); if (block.name !== 'air' && block.name !== 'water' && block.name !== 'lava') { if (bot.entity.position.distanceTo(block.position) > 4.5) { let pos = block.position; let movements = new pf.Movements(bot); movements.canPlaceOn = false; movements.allow1by1towers = false; bot.pathfinder.setMovements(movements); await bot.pathfinder.goto(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); } if (bot.game.gameMode !== 'creative') { await bot.tool.equipForBlock(block); const itemId = bot.heldItem ? bot.heldItem.type : null if (!block.canHarvest(itemId)) { log(bot, `Don't have right tools to break ${block.name}.`); return false; } } await bot.dig(block, true); log(bot, `Broke ${block.name} at x:${x.toFixed(1)}, y:${y.toFixed(1)}, z:${z.toFixed(1)}.`); } else { log(bot, `Skipping block at x:${x.toFixed(1)}, y:${y.toFixed(1)}, z:${z.toFixed(1)} because it is ${block.name}.`); return false; } return true; } export async function placeBlock(bot, blockType, x, y, z) { /** * Place the given block type at the given position. It will build off from any adjacent blocks. Will fail if there is a block in the way or nothing to build off of. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} blockType, the type of block to place. * @param {number} x, the x coordinate of the block to place. * @param {number} y, the y coordinate of the block to place. * @param {number} z, the z coordinate of the block to place. * @returns {Promise} true if the block was placed, false otherwise. * @example * let position = world.getPosition(bot); * await skills.placeBlock(bot, "oak_log", position.x + 1, position.y - 1, position.x); **/ console.log('placing block...') let block = bot.inventory.items().find(item => item.name === blockType); if (!block) { log(bot, `Don't have any ${blockType} to place.`); return false; } const target_dest = new Vec3(Math.floor(x), Math.floor(y), Math.floor(z)); const targetBlock = bot.blockAt(target_dest); if (targetBlock.name === blockType) { log(bot, `${blockType} already at ${targetBlock.position}.`); return false; } const empty_blocks = ['air', 'water', 'lava', 'grass', 'short_grass', 'tall_grass', 'snow', 'dead_bush', 'fern']; if (!empty_blocks.includes(targetBlock.name)) { log(bot, `${blockType} in the way at ${targetBlock.position}.`); const removed = await breakBlockAt(bot, x, y, z); if (!removed) { log(bot, `Cannot place ${blockType} at ${targetBlock.position}: block in the way.`); return false; } await new Promise(resolve => setTimeout(resolve, 200)); // wait for block to break } // get the buildoffblock and facevec based on whichever adjacent block is not empty let buildOffBlock = null; let faceVec = null; const dirs = [Vec3(0, -1, 0), Vec3(0, 1, 0), Vec3(1, 0, 0), Vec3(-1, 0, 0), Vec3(0, 0, 1), Vec3(0, 0, -1)]; for (let d of dirs) { const block = bot.blockAt(target_dest.plus(d)); if (!empty_blocks.includes(block.name)) { buildOffBlock = block; faceVec = new Vec3(-d.x, -d.y, -d.z); break; } } if (!buildOffBlock) { log(bot, `Cannot place ${blockType} at ${targetBlock.position}: nothing to place on.`); return false; } const pos = bot.entity.position; const pos_above = pos.plus(Vec3(0,1,0)); const dont_move_for = ['torch', 'redstone_torch', 'redstone', 'lever', 'button', 'rail', 'detector_rail', 'powered_rail', 'activator_rail', 'tripwire_hook', 'tripwire', 'water_bucket']; if (!dont_move_for.includes(blockType) && (pos.distanceTo(targetBlock.position) < 1 || pos_above.distanceTo(targetBlock.position) < 1)) { // too close let goal = new pf.goals.GoalNear(targetBlock.position.x, targetBlock.position.y, targetBlock.position.z, 2); let inverted_goal = new pf.goals.GoalInvert(goal); bot.pathfinder.setMovements(new pf.Movements(bot)); await bot.pathfinder.goto(inverted_goal); } if (bot.entity.position.distanceTo(targetBlock.position) > 4.5) { // too far let pos = targetBlock.position; let movements = new pf.Movements(bot); bot.pathfinder.setMovements(movements); await bot.pathfinder.goto(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); } await bot.equip(block, 'hand'); await bot.lookAt(buildOffBlock.position); // will throw error if an entity is in the way, and sometimes even if the block was placed try { await bot.placeBlock(buildOffBlock, faceVec); log(bot, `Successfully placed ${blockType} at ${target_dest}.`); await new Promise(resolve => setTimeout(resolve, 200)); return true; } catch (err) { log(bot, `Failed to place ${blockType} at ${target_dest}.`); return false; } } export async function equip(bot, itemName, bodyPart) { /** * Equip the given item to the given body part, like tools or armor. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} itemName, the item or block name to equip. * @param {string} bodyPart, the body part to equip the item to. * @returns {Promise} true if the item was equipped, false otherwise. * @example * await skills.equip(bot, "iron_pickaxe", "hand"); * await skills.equip(bot, "diamond_chestplate", "torso"); **/ let item = bot.inventory.items().find(item => item.name === itemName); if (!item) { log(bot, `You do not have any ${itemName} to equip.`); return false; } await bot.equip(item, bodyPart); return true; } export async function discard(bot, itemName, num=-1) { /** * Discard the given item. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} itemName, the item or block name to discard. * @param {number} num, the number of items to discard. Defaults to -1, which discards all items. * @returns {Promise} true if the item was discarded, false otherwise. * @example * await skills.discard(bot, "oak_log"); **/ let discarded = 0; while (true) { let item = bot.inventory.items().find(item => item.name === itemName); if (!item) { break; } let to_discard = num === -1 ? item.count : Math.min(num - discarded, item.count); await bot.toss(item.type, null, to_discard); discarded += to_discard; if (num !== -1 && discarded >= num) { break; } } if (discarded === 0) { log(bot, `You do not have any ${itemName} to discard.`); return false; } log(bot, `Successfully discarded ${discarded} ${itemName}.`); return true; } export async function eat(bot, foodName="") { /** * Eat the given item. If no item is given, it will eat the first food item in the bot's inventory. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} item, the item to eat. * @returns {Promise} true if the item was eaten, false otherwise. * @example * await skills.eat(bot, "apple"); **/ let item, name; if (foodName) { item = bot.inventory.items().find(item => item.name === foodName); name = foodName; } else { item = bot.inventory.items().find(item => item.foodRecovery > 0); name = "food"; } if (!item) { log(bot, `You do not have any ${name} to eat.`); return false; } await bot.equip(item, 'hand'); await bot.consume(); log(bot, `Successfully ate ${item.name}.`); return true; } export async function giveToPlayer(bot, itemType, username, num=1) { /** * Give one of the specified item to the specified player * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} itemType, the name of the item to give. * @param {string} username, the username of the player to give the item to. * @param {number} num, the number of items to give. Defaults to 1. * @returns {Promise} true if the item was given, false otherwise. * @example * await skills.giveToPlayer(bot, "oak_log", "player1"); **/ let player = bot.players[username].entity if (!player){ log(bot, `Could not find ${username}.`); return false; } await goToPlayer(bot, username); await bot.lookAt(player.position); discard(bot, itemType, num); return true; } export async function goToPosition(bot, x, y, z, min_distance=2) { /** * Navigate to the given position. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {number} x, the x coordinate to navigate to. If null, the bot's current x coordinate will be used. * @param {number} y, the y coordinate to navigate to. If null, the bot's current y coordinate will be used. * @param {number} z, the z coordinate to navigate to. If null, the bot's current z coordinate will be used. * @param {number} distance, the distance to keep from the position. Defaults to 2. * @returns {Promise} true if the position was reached, false otherwise. * @example * let position = world.world.getNearestBlock(bot, "oak_log", 64).position; * await skills.goToPosition(bot, position.x, position.y, position.x + 20); **/ if (x == null || y == null || z == null) { log(bot, `Missing coordinates, given x:${x} y:${y} z:${z}`); return false; } bot.pathfinder.setMovements(new pf.Movements(bot)); await bot.pathfinder.goto(new pf.goals.GoalNear(x, y, z, min_distance)); log(bot, `You have reached at ${x}, ${y}, ${z}.`); return true; } export async function goToPlayer(bot, username, distance=3) { /** * Navigate to the given player. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} username, the username of the player to navigate to. * @param {number} distance, the goal distance to the player. * @returns {Promise} true if the player was found, false otherwise. * @example * await skills.goToPlayer(bot, "player"); **/ bot.modes.pause('self_defense'); bot.modes.pause('cowardice'); let player = bot.players[username].entity if (!player) { log(bot, `Could not find ${username}.`); return false; } const move = new pf.Movements(bot); bot.pathfinder.setMovements(move); await bot.pathfinder.goto(new pf.goals.GoalFollow(player, distance), true); log(bot, `You have reached ${username}.`); } export async function followPlayer(bot, username, distance=4) { /** * Follow the given player endlessly. Will not return until the code is manually stopped. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} username, the username of the player to follow. * @returns {Promise} true if the player was found, false otherwise. * @example * await skills.followPlayer(bot, "player"); **/ let player = bot.players[username].entity if (!player) return false; const move = new pf.Movements(bot); bot.pathfinder.setMovements(move); bot.pathfinder.setGoal(new pf.goals.GoalFollow(player, distance), true); log(bot, `You are now actively following player ${username}.`); while (!bot.interrupt_code) { await new Promise(resolve => setTimeout(resolve, 500)); } return true; } export async function moveAway(bot, distance) { /** * Move away from current position in any direction. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {number} distance, the distance to move away. * @returns {Promise} true if the bot moved away, false otherwise. * @example * await skills.moveAway(bot, 8); **/ const pos = bot.entity.position; let goal = new pf.goals.GoalNear(pos.x, pos.y, pos.z, distance); let inverted_goal = new pf.goals.GoalInvert(goal); bot.pathfinder.setMovements(new pf.Movements(bot)); await bot.pathfinder.goto(inverted_goal); let new_pos = bot.entity.position; log(bot, `Moved away from nearest entity to ${new_pos}.`); return true; } export async function avoidEnemies(bot, distance=16) { /** * Move a given distance away from all nearby enemy mobs. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {number} distance, the distance to move away. * @returns {Promise} true if the bot moved away, false otherwise. * @example * await skills.avoidEnemies(bot, 8); **/ bot.modes.pause('self_preservation'); // prevents damage-on-low-health from interrupting the bot let enemy = world.getNearestEntityWhere(bot, entity => mc.isHostile(entity), distance); while (enemy) { const follow = new pf.goals.GoalFollow(enemy, distance+1); // move a little further away const inverted_goal = new pf.goals.GoalInvert(follow); bot.pathfinder.setMovements(new pf.Movements(bot)); bot.pathfinder.setGoal(inverted_goal, true); await new Promise(resolve => setTimeout(resolve, 500)); enemy = world.getNearestEntityWhere(bot, entity => mc.isHostile(entity), distance); if (bot.interrupt_code) { break; } } bot.pathfinder.stop(); log(bot, `Moved ${distance} away from enemies.`); return true; } export async function stay(bot) { /** * Stay in the current position until interrupted. Disables all modes. * @param {MinecraftBot} bot, reference to the minecraft bot. * @returns {Promise} true if the bot stayed, false otherwise. * @example * await skills.stay(bot); **/ bot.modes.pause('self_preservation'); bot.modes.pause('cowardice'); bot.modes.pause('self_defense'); bot.modes.pause('hunting'); bot.modes.pause('torch_placing'); bot.modes.pause('item_collecting'); while (!bot.interrupt_code) { await new Promise(resolve => setTimeout(resolve, 500)); } return true; } export async function useDoor(bot, door_pos=null) { /** * Use the door at the given position. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {Vec3} door_pos, the position of the door to use. If null, the nearest door will be used. * @returns {Promise} true if the door was used, false otherwise. * @example * let door = world.getNearestBlock(bot, "oak_door", 16).position; * await skills.useDoor(bot, door); **/ if (!door_pos) { for (let door_type of ['oak_door', 'spruce_door', 'birch_door', 'jungle_door', 'acacia_door', 'dark_oak_door', 'mangrove_door', 'cherry_door', 'bamboo_door', 'crimson_door', 'warped_door']) { door_pos = world.getNearestBlock(bot, door_type, 16).position; if (door_pos) break; } } else { door_pos = Vec3(door_pos.x, door_pos.y, door_pos.z); } if (!door_pos) { log(bot, `Could not find a door to use.`); return false; } bot.pathfinder.setGoal(new pf.goals.GoalNear(door_pos.x, door_pos.y, door_pos.z, 1)); await new Promise((resolve) => setTimeout(resolve, 1000)); while (bot.pathfinder.isMoving()) { await new Promise((resolve) => setTimeout(resolve, 100)); } let door_block = bot.blockAt(door_pos); await bot.lookAt(door_pos); if (!door_block._properties.open) await bot.activateBlock(door_block); bot.setControlState("forward", true); await new Promise((resolve) => setTimeout(resolve, 600)); bot.setControlState("forward", false); await bot.activateBlock(door_block); log(bot, `Used door at ${door_pos}.`); return true; } export async function goToBed(bot) { /** * Sleep in the nearest bed. * @param {MinecraftBot} bot, reference to the minecraft bot. * @returns {Promise} true if the bed was found, false otherwise. * @example * await skills.goToBed(bot); **/ const beds = bot.findBlocks({ matching: (block) => { return block.name.includes('bed'); }, maxDistance: 32, count: 1 }); if (beds.length === 0) { log(bot, `Could not find a bed to sleep in.`); return false; } let loc = beds[0]; await goToPosition(bot, loc.x, loc.y, loc.z); const bed = bot.blockAt(loc); await bot.sleep(bed); log(bot, `You are in bed.`); while (bot.isSleeping) { await new Promise(resolve => setTimeout(resolve, 500)); } log(bot, `You have woken up.`); return true; } export async function tillAndSow(bot, x, y, z, seedType=null) { /** * Till the ground at the given position and plant the given seed type. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {number} x, the x coordinate to till. * @param {number} y, the y coordinate to till. * @param {number} z, the z coordinate to till. * @param {string} plantType, the type of plant to plant. Defaults to none, which will only till the ground. * @returns {Promise} true if the ground was tilled, false otherwise. * @example * let position = world.getPosition(bot); * await skills.till(bot, position.x, position.y - 1, position.x); **/ console.log(x, y, z) x = Math.round(x); y = Math.round(y); z = Math.round(z); let block = bot.blockAt(new Vec3(x, y, z)); console.log(x, y, z) if (block.name !== 'grass_block' && block.name !== 'dirt' && block.name !== 'farmland') { log(bot, `Cannot till ${block.name}, must be grass_block or dirt.`); return false; } let above = bot.blockAt(new Vec3(x, y+1, z)); if (above.name !== 'air') { log(bot, `Cannot till, there is ${above.name} above the block.`); return false; } // if distance is too far, move to the block if (bot.entity.position.distanceTo(block.position) > 4.5) { let pos = block.position; bot.pathfinder.setMovements(new pf.Movements(bot)); await bot.pathfinder.goto(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); } if (block.name !== 'farmland') { let hoe = bot.inventory.items().find(item => item.name.includes('hoe')); if (!hoe) { log(bot, `Cannot till, no hoes.`); return false; } await bot.equip(hoe, 'hand'); await bot.activateBlock(block); log(bot, `Tilled block x:${x.toFixed(1)}, y:${y.toFixed(1)}, z:${z.toFixed(1)}.`); } if (seedType) { if (seedType.endsWith('seed') && !seedType.endsWith('seeds')) seedType += 's'; // fixes common mistake let seeds = bot.inventory.items().find(item => item.name === seedType); if (!seeds) { log(bot, `No ${seedType} to plant.`); return false; } await bot.equip(seeds, 'hand'); await bot.placeBlock(block, new Vec3(0, -1, 0)); log(bot, `Planted ${seedType} at x:${x.toFixed(1)}, y:${y.toFixed(1)}, z:${z.toFixed(1)}.`); } return true; } export async function activateNearestBlock(bot, type) { /** * Activate the nearest block of the given type. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {string} type, the type of block to activate. * @returns {Promise} true if the block was activated, false otherwise. * @example * await skills.activateNearestBlock(bot, "lever"); * **/ let block = world.getNearestBlock(bot, type, 16); if (!block) { log(bot, `Could not find any ${type} to activate.`); return false; } if (bot.entity.position.distanceTo(block.position) > 4.5) { let pos = block.position; bot.pathfinder.setMovements(new pf.Movements(bot)); await bot.pathfinder.goto(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); } await bot.activateBlock(block); log(bot, `Activated ${type} at x:${block.position.x.toFixed(1)}, y:${block.position.y.toFixed(1)}, z:${block.position.z.toFixed(1)}.`); return true; }