"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CollectBlock = void 0; const mineflayer_pathfinder_1 = require("mineflayer-pathfinder"); const TemporarySubscriber_1 = require("./TemporarySubscriber"); const Util_1 = require("./Util"); const Inventory_1 = require("./Inventory"); const BlockVeins_1 = require("./BlockVeins"); const Targets_1 = require("./Targets"); const minecraft_data_1 = __importDefault(require("minecraft-data")); const events_1 = require("events"); const util_1 = require("util"); function collectAll(bot, options) { return __awaiter(this, void 0, void 0, function* () { while (!options.targets.empty) { yield (0, Inventory_1.emptyInventoryIfFull)(bot, options.chestLocations, options.itemFilter); const closest = options.targets.getClosest(); if (closest == null) break; switch (closest.constructor.name) { case 'Block': { const goal = new mineflayer_pathfinder_1.goals.GoalLookAtBlock(closest.position, bot.world); yield bot.pathfinder.goto(goal); yield mineBlock(bot, closest, options); // TODO: options.ignoreNoPath break; } case 'Entity': { // Don't collect any entities that are marked as 'invalid' if (!closest.isValid) break; const tempEvents = new TemporarySubscriber_1.TemporarySubscriber(bot); const waitForPickup = new Promise(resolve => { tempEvents.subscribeTo('entityGone', (entity) => { if (entity === closest) { tempEvents.cleanup(); resolve(); } }); }); yield bot.pathfinder.goto(new mineflayer_pathfinder_1.goals.GoalFollow(closest, 0)); yield waitForPickup; break; } default: { throw (0, Util_1.error)('UnknownType', `Target ${closest.constructor.name} is not a Block or Entity!`); } } options.targets.removeTarget(closest); } }); } const equipToolOptions = { requireHarvest: true, getFromChest: true, maxTools: 2 }; function mineBlock(bot, block, options) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { // @ts-expect-error if (((_a = bot.blockAt(block.position)) === null || _a === void 0 ? void 0 : _a.type) !== block.type || ((_b = bot.blockAt(block.position)) === null || _b === void 0 ? void 0 : _b.type) === 0 || !bot.pathfinder.movements.safeToBreak(block)) { options.targets.removeTarget(block); return; } yield bot.tool.equipForBlock(block, equipToolOptions); // @ts-expect-error if (!block.canHarvest(bot.heldItem)) { options.targets.removeTarget(block); return; } const tempEvents = new TemporarySubscriber_1.TemporarySubscriber(bot); tempEvents.subscribeTo('itemDrop', (entity) => { if (entity.position.distanceTo(block.position.offset(0.5, 0.5, 0.5)) <= 0.5) { options.targets.appendTarget(entity); } }); try { yield bot.dig(block); // Waiting for items to drop yield new Promise(resolve => { let remainingTicks = 10; tempEvents.subscribeTo('physicTick', () => { remainingTicks--; if (remainingTicks <= 0) { tempEvents.cleanup(); resolve(); } }); }); } finally { tempEvents.cleanup(); } }); } /** * The collect block plugin. */ class CollectBlock { /** * Creates a new instance of the create block plugin. * * @param bot - The bot this plugin is acting on. */ constructor(bot) { /** * A list of chest locations which the bot is allowed to empty their inventory into * if it becomes full while the bot is collecting resources. */ this.chestLocations = []; /** * When collecting items, this filter is used to determine what items should be placed * into a chest if the bot's inventory becomes full. By default, returns true for all * items except for tools, weapons, and armor. * * @param item - The item stack in the bot's inventory to check. * * @returns True if the item should be moved into the chest. False otherwise. */ this.itemFilter = (item) => { if (item.name.includes('helmet')) return false; if (item.name.includes('chestplate')) return false; if (item.name.includes('leggings')) return false; if (item.name.includes('boots')) return false; if (item.name.includes('shield')) return false; if (item.name.includes('sword')) return false; if (item.name.includes('pickaxe')) return false; if (item.name.includes('axe')) return false; if (item.name.includes('shovel')) return false; if (item.name.includes('hoe')) return false; return true; }; this.bot = bot; this.targets = new Targets_1.Targets(bot); this.movements = new mineflayer_pathfinder_1.Movements(bot, (0, minecraft_data_1.default)(bot.version)); } /** * If target is a block: * Causes the bot to break and collect the target block. * * If target is an item drop: * Causes the bot to collect the item drop. * * If target is an array containing items or blocks, preforms the correct action for * all targets in that array sorting dynamically by distance. * * @param target - The block(s) or item(s) to collect. * @param options - The set of options to use when handling these targets * @param cb - The callback that is called finished. */ collect(target, options = {}, cb) { var _a, _b, _c, _d; return __awaiter(this, void 0, void 0, function* () { if (typeof options === 'function') { cb = options; options = {}; } // @ts-expect-error if (cb != null) return (0, util_1.callbackify)(this.collect)(target, options, cb); const optionsFull = { append: (_a = options.append) !== null && _a !== void 0 ? _a : false, ignoreNoPath: (_b = options.ignoreNoPath) !== null && _b !== void 0 ? _b : false, chestLocations: (_c = options.chestLocations) !== null && _c !== void 0 ? _c : this.chestLocations, itemFilter: (_d = options.itemFilter) !== null && _d !== void 0 ? _d : this.itemFilter, targets: this.targets }; if (this.bot.pathfinder == null) { throw (0, Util_1.error)('UnresolvedDependency', 'The mineflayer-collectblock plugin relies on the mineflayer-pathfinder plugin to run!'); } if (this.bot.tool == null) { throw (0, Util_1.error)('UnresolvedDependency', 'The mineflayer-collectblock plugin relies on the mineflayer-tool plugin to run!'); } if (this.movements != null) { this.bot.pathfinder.setMovements(this.movements); } if (!optionsFull.append) yield this.cancelTask(); if (Array.isArray(target)) { this.targets.appendTargets(target); } else { this.targets.appendTarget(target); } try { yield collectAll(this.bot, optionsFull); } catch (err) { this.targets.clear(); // Ignore path stopped error for cancelTask to work properly (imo we shouldn't throw any pathing errors) // @ts-expect-error if (err.name !== 'PathStopped') throw err; } finally { // @ts-expect-error this.bot.emit('collectBlock_finished'); } }); } /** * Loads all touching blocks of the same type to the given block and returns them as an array. * This effectively acts as a flood fill algorithm to retrieve blocks in the same ore vein and similar. * * @param block - The starting block. * @param maxBlocks - The maximum number of blocks to look for before stopping. * @param maxDistance - The max distance from the starting block to look. * @param floodRadius - The max distance distance from block A to block B to be considered "touching" */ findFromVein(block, maxBlocks = 100, maxDistance = 16, floodRadius = 1) { return (0, BlockVeins_1.findFromVein)(this.bot, block, maxBlocks, maxDistance, floodRadius); } /** * Cancels the current collection task, if still active. * * @param cb - The callback to use when the task is stopped. */ cancelTask(cb) { return __awaiter(this, void 0, void 0, function* () { if (this.targets.empty) { if (cb != null) cb(); return yield Promise.resolve(); } this.bot.pathfinder.stop(); if (cb != null) { // @ts-expect-error this.bot.once('collectBlock_finished', cb); } yield (0, events_1.once)(this.bot, 'collectBlock_finished'); }); } } exports.CollectBlock = CollectBlock;