diff --git a/src/agent/agent.js b/src/agent/agent.js index 801e243..ef21f34 100644 --- a/src/agent/agent.js +++ b/src/agent/agent.js @@ -128,6 +128,16 @@ export class Agent { const checkInterrupt = () => this.self_prompter.shouldInterrupt(self_prompt) || this.shut_up; + let behavior_log = this.bot.modes.flushBehaviorLog(); + if (behavior_log !== '') { + const MAX_LOG = 500; + if (behavior_log.length > MAX_LOG) { + behavior_log = behavior_log.substring(behavior_log.length - MAX_LOG) + '...'; + } + behavior_log = 'Recent behaviors log: \n' + behavior_log.substring(behavior_log.indexOf('\n')); + await this.history.add('system', behavior_log); + } + await this.history.add(source, message); this.history.save(); @@ -239,6 +249,10 @@ export class Agent { this.bot.on('idle', () => { this.bot.clearControlStates(); this.bot.pathfinder.stop(); // clear any lingering pathfinder + if (this.bot.currentWindow) { + this.bot.chat('Closing window...'); + this.bot.closeWindow(this.bot.currentWindow); + } this.bot.modes.unPauseAll(); this.coder.executeResume(); }); diff --git a/src/agent/commands/actions.js b/src/agent/commands/actions.js index 26dc0e5..efd9609 100644 --- a/src/agent/commands/actions.js +++ b/src/agent/commands/actions.js @@ -152,6 +152,36 @@ export const actionsList = [ await skills.equip(agent.bot, item_name); }) }, + { + name: '!putInChest', + description: 'Put the given item in the nearest chest.', + params: { + 'item_name': '(string) The name of the item to put in the chest.', + 'num': '(number) The number of items to put in the chest.' + }, + perform: wrapExecution(async (agent, item_name, num) => { + await skills.putInChest(agent.bot, item_name, num); + }) + }, + { + name: '!takeFromChest', + description: 'Take the given items from the nearest chest.', + params: { + 'item_name': '(string) The name of the item to take.', + 'num': '(number) The number of items to take.' + }, + perform: wrapExecution(async (agent, item_name, num) => { + await skills.takeFromChest(agent.bot, item_name, num); + }) + }, + { + name: '!viewChest', + description: 'View the items/counts of the nearest chest.', + params: { }, + perform: wrapExecution(async (agent) => { + await skills.viewChest(agent.bot); + }) + }, { name: '!discard', description: 'Discard the given item from the inventory.', diff --git a/src/agent/library/skills.js b/src/agent/library/skills.js index 77c6c87..7ef87b2 100644 --- a/src/agent/library/skills.js +++ b/src/agent/library/skills.js @@ -201,6 +201,7 @@ export async function smeltItem(bot, itemName, num=1) { break; } } + await bot.closeWindow(furnace); if (placedFurnace) { await collectBlock(bot, 'furnace', 1); @@ -382,6 +383,12 @@ export async function collectBlock(bot, blockType, num=1, exclude=null) { ); } } + const movements = new pf.Movements(bot); + movements.dontMineUnderFallingBlock = false; + blocks = blocks.filter( + block => movements.safeToBreak(block) + ); + if (blocks.length === 0) { if (collected === 0) log(bot, `No ${blockType} nearby to collect.`); @@ -711,6 +718,94 @@ export async function discard(bot, itemName, num=-1) { return true; } +export async function putInChest(bot, itemName, num=-1) { + /** + * Put the given item in the nearest chest. + * @param {MinecraftBot} bot, reference to the minecraft bot. + * @param {string} itemName, the item or block name to put in the chest. + * @param {number} num, the number of items to put in the chest. Defaults to -1, which puts all items. + * @returns {Promise} true if the item was put in the chest, false otherwise. + * @example + * await skills.putInChest(bot, "oak_log"); + **/ + let chest = world.getNearestBlock(bot, 'chest', 32); + if (!chest) { + log(bot, `Could not find a chest nearby.`); + return false; + } + let item = bot.inventory.items().find(item => item.name === itemName); + if (!item) { + log(bot, `You do not have any ${itemName} to put in the chest.`); + return false; + } + let to_put = num === -1 ? item.count : Math.min(num, item.count); + await goToPosition(bot, chest.position.x, chest.position.y, chest.position.z, 2); + const chestContainer = await bot.openContainer(chest); + await chestContainer.deposit(item.type, null, to_put); + await chestContainer.close(); + log(bot, `Successfully put ${to_put} ${itemName} in the chest.`); + return true; +} + +export async function takeFromChest(bot, itemName, num=-1) { + /** + * Take the given item from the nearest chest. + * @param {MinecraftBot} bot, reference to the minecraft bot. + * @param {string} itemName, the item or block name to take from the chest. + * @param {number} num, the number of items to take from the chest. Defaults to -1, which takes all items. + * @returns {Promise} true if the item was taken from the chest, false otherwise. + * @example + * await skills.takeFromChest(bot, "oak_log"); + * **/ + let chest = world.getNearestBlock(bot, 'chest', 32); + if (!chest) { + log(bot, `Could not find a chest nearby.`); + return false; + } + await goToPosition(bot, chest.position.x, chest.position.y, chest.position.z, 2); + const chestContainer = await bot.openContainer(chest); + let item = chestContainer.containerItems().find(item => item.name === itemName); + if (!item) { + log(bot, `Could not find any ${itemName} in the chest.`); + await chestContainer.close(); + return false; + } + let to_take = num === -1 ? item.count : Math.min(num, item.count); + await chestContainer.withdraw(item.type, null, to_take); + await chestContainer.close(); + log(bot, `Successfully took ${to_take} ${itemName} from the chest.`); + return true; +} + +export async function viewChest(bot) { + /** + * View the contents of the nearest chest. + * @param {MinecraftBot} bot, reference to the minecraft bot. + * @returns {Promise} true if the chest was viewed, false otherwise. + * @example + * await skills.viewChest(bot); + * **/ + let chest = world.getNearestBlock(bot, 'chest', 32); + if (!chest) { + log(bot, `Could not find a chest nearby.`); + return false; + } + await goToPosition(bot, chest.position.x, chest.position.y, chest.position.z, 2); + const chestContainer = await bot.openContainer(chest); + let items = chestContainer.containerItems(); + if (items.length === 0) { + log(bot, `The chest is empty.`); + } + else { + log(bot, `The chest contains:`); + for (let item of items) { + log(bot, `${item.count} ${item.name}`); + } + } + await chestContainer.close(); + 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. diff --git a/src/agent/modes.js b/src/agent/modes.js index 35c0c79..653598f 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -4,6 +4,7 @@ import * as mc from '../utils/mcdata.js'; import settings from '../../settings.js' function say(agent, message) { + agent.bot.modes.behavior_log += message + '\n'; if (agent.shut_up || !settings.narrate_behavior) return; agent.bot.chat(message); } @@ -261,6 +262,7 @@ class ModeController { this.agent = agent; this.modes_list = modes; this.modes_map = {}; + this.behavior_log = ''; for (let mode of this.modes_list) { this.modes_map[mode.name] = mode; } @@ -320,6 +322,12 @@ class ModeController { } } + flushBehaviorLog() { + const log = this.behavior_log; + this.behavior_log = ''; + return log; + } + getJson() { let res = {}; for (let mode of this.modes_list) {