From ff9c24187648a4bdadf6102426a5140a84eb99e3 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Sat, 13 Jan 2024 12:11:04 -0600 Subject: [PATCH] added defend, improved gotoplayer --- src/agent/commands/actions.js | 14 ++++-- src/agent/skills.js | 80 ++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/agent/commands/actions.js b/src/agent/commands/actions.js index e13c06a..6f97838 100644 --- a/src/agent/commands/actions.js +++ b/src/agent/commands/actions.js @@ -3,6 +3,7 @@ import * as world from '../world.js'; function wrapExecution(func) { return async function (agent, ...args) { + await agent.coder.stop(); agent.bot.output = ''; agent.coder.executing = true; let res = await func(agent, ...args); @@ -13,7 +14,6 @@ function wrapExecution(func) { } } -// const actionsList = [ export const actionsList = [ { name: '!newAction', @@ -34,7 +34,7 @@ export const actionsList = [ }, { name: '!goToPlayer', - description: 'Go to the nearest player. Ex: !goToPlayer("steve")', + description: 'Go to the given player. Ex: !goToPlayer("steve")', params: {'player_name': '(string) The name of the player to go to.'}, perform: wrapExecution(async (agent, player_name) => { return await skills.goToPlayer(agent.bot, player_name); @@ -42,7 +42,7 @@ export const actionsList = [ }, { name: '!followPlayer', - description: 'Endlessly follow the nearest player. Ex: !followPlayer("stevie")', + description: 'Endlessly follow the given player. Ex: !followPlayer("stevie")', params: {'player_name': '(string) The name of the player to follow.'}, perform: wrapExecution(async (agent, player_name) => { await skills.followPlayer(agent.bot, player_name); @@ -66,5 +66,13 @@ export const actionsList = [ perform: wrapExecution(async (agent, type) => { await skills.attackMob(agent.bot, type, true); }) + }, + { + name: '!defend', + description: 'Follow the given player and attack any nearby monsters.', + params: {'player_name': '(string) The name of the player to defend.'}, + perform: wrapExecution(async (agent, player_name) => { + await skills.defendPlayer(agent.bot, player_name); + }) } ]; diff --git a/src/agent/skills.js b/src/agent/skills.js index 2073eed..ae3e052 100644 --- a/src/agent/skills.js +++ b/src/agent/skills.js @@ -44,13 +44,15 @@ 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. Must contain "raw" + * @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"); **/ - if (!itemName.includes('raw')) { + 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. @@ -166,6 +168,13 @@ export async function clearNearestFurnace(bot) { } +function equipHighestAttack(bot) { + let weapons = bot.inventory.items().filter(item => item.name.includes('sword') || item.name.includes('axe') || item.name.includes('pickaxe') || item.name.includes('shovel')); + let weapon = weapons.sort((a, b) => b.attackDamage - a.attackDamage)[0]; + if (weapon) + bot.equip(weapon, 'hand'); +} + export async function attackMob(bot, mobType, kill=true) { /** * Attack mob of the given type. @@ -177,16 +186,11 @@ export async function attackMob(bot, mobType, kill=true) { * await skills.attackMob(bot, "zombie", true); **/ const mob = bot.nearestEntity(entity => entity.name && entity.name.toLowerCase() === mobType.toLowerCase()); - const attackable = ['animal', 'monster', 'mob']; - if (mob && attackable.includes(mob.type)) { + if (mob) { let pos = mob.position; console.log(bot.entity.position.distanceTo(pos)) - // equip highest damage weapon - let weapons = bot.inventory.items().filter(item => item.name.includes('sword') || item.name.includes('axe') || item.name.includes('pickaxe') || item.name.includes('shovel')); - let weapon = weapons.sort((a, b) => b.attackDamage - a.attackDamage)[0]; - if (weapon) - await bot.equip(weapon, 'hand'); + equipHighestAttack(bot) if (!kill) { if (bot.entity.position.distanceTo(pos) > 5) { @@ -542,13 +546,10 @@ export async function goToPlayer(bot, username) { return false; } - let arrived = await goToPosition(bot, player.position.x, player.position.y, player.position.z); - if (!arrived) { - log(bot, `Failed to reach ${username}.`); - return false; - } - log(bot, `You have reached the player at position ${player.position}.`); - return true; + bot.pathfinder.setMovements(new pf.Movements(bot)); + await bot.pathfinder.goto(new pf.goals.GoalFollow(player, 2), true); + + log(bot, `You have reached ${username}.`); } @@ -573,5 +574,52 @@ export async function followPlayer(bot, username) { await new Promise(resolve => setTimeout(resolve, 1000)); } + return true; +} + +export async function defendPlayer(bot, username) { + /** + * Defend the given player endlessly, attacking any nearby monsters. 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 defend. + * @returns {Promise} true if the player was found, false otherwise. + * @example + * await skills.defendPlayer(bot, "bob"); + **/ + let player = bot.players[username].entity + if (!player) + return false; + + bot.pathfinder.setMovements(new pf.Movements(bot)); + bot.pathfinder.setGoal(new pf.goals.GoalFollow(player, 5), true); + log(bot, `Actively defending player ${username}.`); + + while (!bot.interrupt_code) { + if (bot.entity.position.distanceTo(player.position) < 10) { + const mobs = getNearbyMobs(bot, 8).filter(mob => mob.type === 'mob' || mob.type === 'hostile'); + const mob = mobs.sort((a, b) => a.position.distanceTo(player.position) - b.position.distanceTo(player.position))[0]; // get closest to player + if (mob) { + bot.pathfinder.stop(); + log(bot, `Found ${mob.name}, attacking!`); + bot.chat(`Found ${mob.name}, attacking!`); + equipHighestAttack(bot); + bot.pvp.attack(mob); + while (getNearbyMobs(bot, 8).includes(mob)) { + await new Promise(resolve => setTimeout(resolve, 500)); + console.log('attacking...') + if (bot.interrupt_code || bot.entity.position.distanceTo(player.position) > 16) { + console.log('stopping pvp...'); + bot.pvp.stop(); + break; + } + } + console.log('resuming pathfinder...') + bot.pathfinder.setMovements(new pf.Movements(bot)); + bot.pathfinder.setGoal(new pf.goals.GoalFollow(player, 5), true); + } + } + await new Promise(resolve => setTimeout(resolve, 500)); + } + return true; } \ No newline at end of file