2024-01-25 13:25:36 -08:00
import * as mc from "../../utils/mcdata.js" ;
import * as world from "./world.js" ;
2023-08-15 23:39:02 -07:00
import pf from 'mineflayer-pathfinder' ;
2023-11-08 19:24:24 -06:00
import Vec3 from 'vec3' ;
2023-08-15 23:39:02 -07:00
2024-01-25 13:25:36 -08:00
2024-01-26 15:05:36 -06:00
export function log ( bot , message , chat = false ) {
2023-11-12 13:57:22 -06:00
bot . output += message + '\n' ;
2024-01-23 18:01:38 -06:00
if ( chat )
bot . chat ( message ) ;
2023-11-12 13:57:22 -06:00
}
2024-01-25 13:25:36 -08:00
async function autoLight ( bot ) {
2024-06-03 19:09:22 -05:00
if ( world . shouldPlaceTorch ( bot ) ) {
2024-06-03 18:23:01 -05:00
try {
const pos = world . getPosition ( bot ) ;
2024-06-22 15:19:10 -05:00
return await placeBlock ( bot , 'torch' , pos . x , pos . y , pos . z , 'bottom' , true ) ;
2024-06-03 18:23:01 -05:00
} catch ( err ) { return false ; }
2024-01-25 13:25:36 -08:00
}
return false ;
}
2024-02-05 19:08:08 -06:00
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 ] ;
2024-01-25 13:25:36 -08:00
if ( weapon )
2024-02-05 19:08:08 -06:00
await bot . equip ( weapon , 'hand' ) ;
2024-01-25 13:25:36 -08:00
}
2024-09-26 16:29:39 +10:00
/ * *
* Returns the number of ingredients required to use the recipe once .
*
* @ param { Recipe } recipe
* @ returns { Object < mc . ItemName , number > } an object describing the number of each ingredient .
* /
function ingredientsFromPrismarineRecipe ( recipe ) {
let requiredIngedients = { } ;
if ( recipe . inShape )
for ( const ingredient of recipe . inShape . flat ( ) ) {
if ( ingredient . id < 0 ) continue ; //prismarine-recipe uses id -1 as an empty crafting slot
const ingredientName = mc . getItemName ( ingredient . id ) ;
requiredIngedients [ ingredientName ] ? ? = 0 ;
requiredIngedients [ ingredientName ] += ingredient . count ;
}
if ( recipe . ingredients )
for ( const ingredient of recipe . ingredients ) {
if ( ingredient . id < 0 ) continue ;
const ingredientName = mc . getItemName ( ingredient . id ) ;
requiredIngedients [ ingredientName ] ? ? = 0 ;
requiredIngedients [ ingredientName ] -= ingredient . count ;
//Yes, the `-=` is intended.
//prismarine-recipe uses positive numbers for the shaped ingredients but negative for unshaped.
//Why this is the case is beyond my understanding.
}
return requiredIngedients ;
}
/ * *
* Calculates the number of times an action , such as a crafing recipe , can be completed before running out of resources .
* @ template T - doesn ' t have to be an item . This could be any resource .
* @ param { Object . < T , number > } availableItems - The resources available ; e . g , ` {'cobble_stone': 7, 'stick': 10} `
* @ param { Object . < T , number > } requiredItems - The resources required to complete the action once ; e . g , ` {'cobble_stone': 3, 'stick': 2} `
* @ param { boolean } discrete - Is the action discrete ?
* @ returns { { num : number , limitingResource : ( T | null ) } } the number of times the action can be completed and the limmiting resource ; e . g ` {num: 2, limitingResource: 'cobble_stone'} `
* /
function calculateLimitingResource ( availableItems , requiredItems , discrete = true ) {
let limitingResource = null ;
let num = Infinity ;
for ( const itemType in requiredItems ) {
if ( availableItems [ itemType ] < requiredItems [ itemType ] * num ) {
limitingResource = itemType ;
num = availableItems [ itemType ] / requiredItems [ itemType ] ;
}
}
if ( discrete ) num = Math . floor ( num ) ;
return { num , limitingResource }
}
2023-08-15 23:39:02 -07:00
2024-03-05 12:50:13 -08:00
export async function craftRecipe ( bot , itemName , num = 1 ) {
2023-11-07 09:44:56 -06:00
/ * *
2023-11-27 21:07:52 -06:00
* Attempt to craft the given item name from a recipe . May craft many items .
2023-11-07 09:44:56 -06:00
* @ param { MinecraftBot } bot , reference to the minecraft bot .
2023-11-27 21:07:52 -06:00
* @ param { string } itemName , the item name to craft .
2024-01-16 15:53:27 -06:00
* @ returns { Promise < boolean > } true if the recipe was crafted , false otherwise .
2023-11-07 09:44:56 -06:00
* @ example
2023-11-27 21:07:52 -06:00
* await skills . craftRecipe ( bot , "stick" ) ;
2023-11-07 09:44:56 -06:00
* * /
2024-01-25 14:16:25 -08:00
let placedTable = false ;
// get recipes that don't require a crafting table
let recipes = bot . recipesFor ( mc . getItemId ( itemName ) , null , 1 , null ) ;
2023-12-04 21:33:40 -06:00
let craftingTable = null ;
2024-08-22 15:57:20 -05:00
const craftingTableRange = 32 ;
2024-09-26 16:29:39 +10:00
placeTable : if ( ! recipes || recipes . length === 0 ) {
recipes = bot . recipesFor ( mc . getItemId ( itemName ) , null , 1 , true ) ;
if ( ! recipes || recipes . length === 0 ) break placeTable ; //Don't bother going to the table if we don't have the required resources.
2024-01-25 14:16:25 -08:00
// Look for crafting table
2024-08-22 15:57:20 -05:00
craftingTable = world . getNearestBlock ( bot , 'crafting_table' , craftingTableRange ) ;
2023-11-15 17:01:23 -06:00
if ( craftingTable === null ) {
2024-01-25 14:16:25 -08:00
// 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 ) ;
2024-08-22 15:57:20 -05:00
craftingTable = world . getNearestBlock ( bot , 'crafting_table' , craftingTableRange ) ;
2024-01-25 14:16:25 -08:00
if ( craftingTable ) {
recipes = bot . recipesFor ( mc . getItemId ( itemName ) , null , 1 , craftingTable ) ;
placedTable = true ;
}
}
else {
2024-09-26 16:29:39 +10:00
log ( bot , ` Crafting ${ itemName } requires a crafting table. ` )
2024-01-25 14:16:25 -08:00
return false ;
}
}
else {
recipes = bot . recipesFor ( mc . getItemId ( itemName ) , null , 1 , craftingTable ) ;
2023-11-15 17:01:23 -06:00
}
}
if ( ! recipes || recipes . length === 0 ) {
2023-11-27 21:07:52 -06:00
log ( bot , ` You do not have the resources to craft a ${ itemName } . ` ) ;
2024-01-25 14:16:25 -08:00
if ( placedTable ) {
await collectBlock ( bot , 'crafting_table' , 1 ) ;
}
2023-11-15 17:01:23 -06:00
return false ;
}
2024-08-22 15:57:20 -05:00
if ( craftingTable && bot . entity . position . distanceTo ( craftingTable . position ) > 4 ) {
await goToNearestBlock ( bot , 'crafting_table' , 4 , craftingTableRange ) ;
}
2023-11-15 17:01:23 -06:00
2024-01-25 14:16:25 -08:00
const recipe = recipes [ 0 ] ;
2023-11-15 17:01:23 -06:00
console . log ( 'crafting...' ) ;
2024-09-25 11:43:31 +10:00
//Check that the agent has sufficient items to use the recipe `num` times.
2024-09-26 16:29:39 +10:00
const inventory = world . getInventoryCounts ( bot ) ; //Items in the agents inventory
const requiredIngredients = ingredientsFromPrismarineRecipe ( recipe ) ; //Items required to use the recipe once.
const craftLimit = calculateLimitingResource ( inventory , requiredIngredients ) ;
await bot . craft ( recipe , Math . min ( craftLimit . num , num ) , craftingTable ) ;
if ( craftLimit . num < num ) log ( bot , ` Not enough ${ craftLimit . limitingResource } to craft ${ num } , crafted ${ craftLimit . num } . You now have ${ world . getInventoryCounts ( bot ) [ itemName ] } ${ itemName } . ` ) ;
2024-09-25 11:43:31 +10:00
else log ( bot , ` Successfully crafted ${ itemName } , you now have ${ world . getInventoryCounts ( bot ) [ itemName ] } ${ itemName } . ` ) ;
2024-01-25 14:16:25 -08:00
if ( placedTable ) {
await collectBlock ( bot , 'crafting_table' , 1 ) ;
}
2024-09-25 11:44:21 +10:00
//Equip any armor the bot may have crafted.
//There is probablly a more efficient method than checking the entire inventory but this is all mineflayer-armor-manager provides. :P
bot . armorManager . equipAll ( ) ;
2023-08-15 23:39:02 -07:00
return true ;
}
2023-12-04 21:33:40 -06:00
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 .
2024-01-13 12:11:04 -06:00
* @ param { string } itemName , the item name to smelt . Ores must contain "raw" like raw _iron .
2023-12-04 21:33:40 -06:00
* @ param { number } num , the number of items to smelt . Defaults to 1.
* @ returns { Promise < boolean > } true if the item was smelted , false otherwise . Fail
* @ example
* await skills . smeltItem ( bot , "raw_iron" ) ;
2024-01-13 12:11:04 -06:00
* await skills . smeltItem ( bot , "beef" ) ;
2023-12-04 21:33:40 -06:00
* * /
2024-01-13 12:11:04 -06:00
const foods = [ 'beef' , 'chicken' , 'cod' , 'mutton' , 'porkchop' , 'rabbit' , 'salmon' , 'tropical_fish' ] ;
if ( ! itemName . includes ( 'raw' ) && ! foods . includes ( itemName ) ) {
2023-12-04 22:33:30 -06:00
log ( bot , ` Cannot smelt ${ itemName } , must be a "raw" item, like "raw_iron". ` ) ;
return false ;
} // TODO: allow cobblestone, sand, clay, etc.
2024-03-05 12:50:13 -08:00
let placedFurnace = false ;
2023-12-04 21:33:40 -06:00
let furnaceBlock = undefined ;
2024-08-22 15:57:20 -05:00
const furnaceRange = 32 ;
furnaceBlock = world . getNearestBlock ( bot , 'furnace' , furnaceRange ) ;
2023-12-10 20:19:07 -06:00
if ( ! furnaceBlock ) {
2024-03-05 12:50:13 -08:00
// Try to place furnace
let hasFurnace = world . getInventoryCounts ( bot ) [ 'furnace' ] > 0 ;
if ( hasFurnace ) {
2024-08-22 15:57:20 -05:00
let pos = world . getNearestFreeSpace ( bot , 1 , furnaceRange ) ;
2024-03-05 12:50:13 -08:00
await placeBlock ( bot , 'furnace' , pos . x , pos . y , pos . z ) ;
2024-08-22 15:57:20 -05:00
furnaceBlock = world . getNearestBlock ( bot , 'furnace' , furnaceRange ) ;
2024-03-05 12:50:13 -08:00
placedFurnace = true ;
}
}
if ( ! furnaceBlock ) {
log ( bot , ` There is no furnace nearby and you have no furnace. ` )
2023-12-04 21:33:40 -06:00
return false ;
}
2024-08-22 15:57:20 -05:00
if ( bot . entity . position . distanceTo ( furnaceBlock . position ) > 4 ) {
await goToNearestBlock ( bot , 'furnace' , 4 , furnaceRange ) ;
}
2023-12-04 21:33:40 -06:00
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 ( ) ;
2024-01-25 13:25:36 -08:00
if ( input _item && input _item . type !== mc . getItemId ( itemName ) && input _item . count > 0 ) {
2023-12-04 21:33:40 -06:00
// 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.
2024-01-25 13:25:36 -08:00
log ( bot , ` The furnace is currently smelting ${ mc . getItemName ( input _item . type ) } . ` ) ;
2024-03-05 12:50:13 -08:00
if ( placedFurnace )
await collectBlock ( bot , 'furnace' , 1 ) ;
2023-12-04 21:33:40 -06:00
return false ;
}
// check if the bot has enough items to smelt
2024-01-25 13:25:36 -08:00
let inv _counts = world . getInventoryCounts ( bot ) ;
2023-12-04 21:33:40 -06:00
if ( ! inv _counts [ itemName ] || inv _counts [ itemName ] < num ) {
log ( bot , ` You do not have enough ${ itemName } to smelt. ` ) ;
2024-03-05 12:50:13 -08:00
if ( placedFurnace )
await collectBlock ( bot , 'furnace' , 1 ) ;
2023-12-04 21:33:40 -06:00
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 ` ) ;
2024-03-05 12:50:13 -08:00
if ( placedFurnace )
await collectBlock ( bot , 'furnace' , 1 ) ;
2023-12-04 21:33:40 -06:00
return false ;
}
await furnace . putFuel ( fuel . type , null , put _fuel ) ;
2024-01-25 13:25:36 -08:00
log ( bot , ` Added ${ put _fuel } ${ mc . getItemName ( fuel . type ) } to furnace fuel. ` ) ;
console . log ( ` Added ${ put _fuel } ${ mc . getItemName ( fuel . type ) } to furnace fuel. ` )
2023-12-04 21:33:40 -06:00
}
// put the items in the furnace
2024-01-25 13:25:36 -08:00
await furnace . putInput ( mc . getItemId ( itemName ) , null , num ) ;
2023-12-04 21:33:40 -06:00
// wait for the items to smelt
let total = 0 ;
let collected _last = true ;
let smelted _item = null ;
2023-12-04 22:33:30 -06:00
await new Promise ( resolve => setTimeout ( resolve , 200 ) ) ;
2023-12-04 21:33:40 -06:00
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 ;
}
}
2024-03-05 12:50:13 -08:00
if ( placedFurnace ) {
await collectBlock ( bot , 'furnace' , 1 ) ;
}
2023-12-04 21:33:40 -06:00
if ( total === 0 ) {
log ( bot , ` Failed to smelt ${ itemName } . ` ) ;
return false ;
}
if ( total < num ) {
2024-01-25 13:25:36 -08:00
log ( bot , ` Only smelted ${ total } ${ mc . getItemName ( smelted _item . type ) } . ` ) ;
2023-12-04 21:33:40 -06:00
return false ;
}
2024-01-25 13:25:36 -08:00
log ( bot , ` Successfully smelted ${ itemName } , got ${ total } ${ mc . getItemName ( smelted _item . type ) } . ` ) ;
2023-12-04 21:33:40 -06:00
return true ;
}
export async function clearNearestFurnace ( bot ) {
/ * *
* Clears the nearest furnace of all items .
* @ param { MinecraftBot } bot , reference to the minecraft bot .
* @ returns { Promise < boolean > } true if the furnace was cleared , false otherwise .
* @ example
* await skills . clearNearestFurnace ( bot ) ;
* * /
2024-01-25 13:25:36 -08:00
let furnaceBlock = world . getNearestBlock ( bot , 'furnace' , 6 ) ;
2023-12-04 21:33:40 -06:00
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 ;
}
2024-01-23 18:01:38 -06:00
export async function attackNearest ( bot , mobType , kill = true ) {
2023-11-07 09:44:56 -06:00
/ * *
* Attack mob of the given type .
* @ param { MinecraftBot } bot , reference to the minecraft bot .
* @ param { string } mobType , the type of mob to attack .
2023-11-19 17:11:46 -06:00
* @ param { boolean } kill , whether or not to continue attacking until the mob is dead . Defaults to true .
2023-11-07 09:44:56 -06:00
* @ returns { Promise < boolean > } true if the mob was attacked , false if the mob type was not found .
* @ example
2024-01-24 17:24:52 -06:00
* await skills . attackNearest ( bot , "zombie" , true ) ;
2023-11-07 09:44:56 -06:00
* * /
2024-04-13 22:56:18 -05:00
bot . modes . pause ( 'cowardice' ) ;
2024-04-20 22:22:26 -05:00
const mob = world . getNearbyEntities ( bot , 24 ) . find ( entity => entity . name === mobType ) ;
2024-01-13 12:11:04 -06:00
if ( mob ) {
2024-01-23 18:01:38 -06:00
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 < boolean > } true if the entity was attacked , false if interrupted
* @ example
* await skills . attackEntity ( bot , entity ) ;
* * /
2023-11-19 17:11:46 -06:00
2024-01-23 18:01:38 -06:00
let pos = entity . position ;
console . log ( bot . entity . position . distanceTo ( pos ) )
2023-12-04 21:33:40 -06:00
2024-02-05 19:08:08 -06:00
await equipHighestAttack ( bot )
2024-01-23 18:01:38 -06:00
if ( ! kill ) {
if ( bot . entity . position . distanceTo ( pos ) > 5 ) {
console . log ( 'moving to mob...' )
await goToPosition ( bot , pos . x , pos . y , pos . z ) ;
2023-11-19 17:11:46 -06:00
}
2024-01-23 18:01:38 -06:00
console . log ( 'attacking mob...' )
await bot . attack ( entity ) ;
}
else {
bot . pvp . attack ( entity ) ;
2024-04-20 22:22:26 -05:00
while ( world . getNearbyEntities ( bot , 24 ) . includes ( entity ) ) {
2024-01-23 18:01:38 -06:00
await new Promise ( resolve => setTimeout ( resolve , 1000 ) ) ;
if ( bot . interrupt _code ) {
bot . pvp . stop ( ) ;
return false ;
2023-11-19 17:11:46 -06:00
}
2023-09-29 15:53:16 -07:00
}
2024-01-23 18:01:38 -06:00
log ( bot , ` Successfully killed ${ entity . name } . ` ) ;
2024-02-02 15:47:17 -06:00
await pickupNearbyItems ( bot ) ;
2024-01-23 18:01:38 -06:00
return true ;
}
}
2023-11-19 17:11:46 -06:00
2024-02-01 16:16:30 -06:00
export async function defendSelf ( bot , range = 9 ) {
2024-01-23 18:01:38 -06:00
/ * *
* 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 < boolean > } 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' ) ;
2024-04-13 22:56:18 -05:00
bot . modes . pause ( 'cowardice' ) ;
2024-01-23 18:01:38 -06:00
let attacked = false ;
2024-01-25 13:25:36 -08:00
let enemy = world . getNearestEntityWhere ( bot , entity => mc . isHostile ( entity ) , range ) ;
2024-01-23 18:01:38 -06:00
while ( enemy ) {
2024-02-05 19:08:08 -06:00
await equipHighestAttack ( bot ) ;
2024-01-24 17:24:52 -06:00
if ( bot . entity . position . distanceTo ( enemy . position ) > 4 && enemy . name !== 'creeper' && enemy . name !== 'phantom' ) {
2024-01-23 18:01:38 -06:00
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 ) ) ;
2024-01-25 13:25:36 -08:00
enemy = world . getNearestEntityWhere ( bot , entity => mc . isHostile ( entity ) , range ) ;
2024-01-23 18:01:38 -06:00
if ( bot . interrupt _code ) {
bot . pvp . stop ( ) ;
return false ;
}
2023-09-29 15:53:16 -07:00
}
2024-01-24 17:24:52 -06:00
bot . pvp . stop ( ) ;
2024-01-23 18:01:38 -06:00
if ( attacked )
log ( bot , ` Successfully defended self. ` ) ;
else
log ( bot , ` No enemies nearby to defend self from. ` ) ;
return attacked ;
2023-09-29 15:53:16 -07:00
}
2024-01-23 18:01:38 -06:00
2024-03-06 13:18:23 -08:00
export async function collectBlock ( bot , blockType , num = 1 , exclude = null ) {
2023-11-07 09:44:56 -06:00
/ * *
* Collect one of the given block type .
* @ param { MinecraftBot } bot , reference to the minecraft bot .
* @ param { string } blockType , the type of block to collect .
2023-12-04 21:33:40 -06:00
* @ param { number } num , the number of blocks to collect . Defaults to 1.
2023-11-07 09:44:56 -06:00
* @ returns { Promise < boolean > } true if the block was collected , false if the block type was not found .
* @ example
* await skills . collectBlock ( bot , "oak_log" ) ;
* * /
2023-12-06 22:15:09 -06:00
if ( num < 1 ) {
log ( bot , ` Invalid number of blocks to collect: ${ num } . ` ) ;
return false ;
}
2024-01-27 19:17:36 -06:00
let blocktypes = [ blockType ] ;
2024-04-30 23:07:07 -05:00
if ( blockType === 'coal' || blockType === 'diamond' || blockType === 'emerald' || blockType === 'iron' || blockType === 'gold' || blockType === 'lapis_lazuli' || blockType === 'redstone' )
blocktypes . push ( blockType + '_ore' ) ;
2024-01-27 19:17:36 -06:00
if ( blockType . endsWith ( 'ore' ) )
blocktypes . push ( 'deepslate_' + blockType ) ;
2024-03-06 14:32:54 -08:00
if ( blockType === 'dirt' )
blocktypes . push ( 'grass_block' ) ;
2024-01-27 19:17:36 -06:00
2023-12-04 21:33:40 -06:00
let collected = 0 ;
2024-02-01 16:16:30 -06:00
for ( let i = 0 ; i < num ; i ++ ) {
2024-03-06 14:32:54 -08:00
let blocks = world . getNearestBlocks ( bot , blocktypes , 64 ) ;
2024-03-06 13:18:23 -08:00
if ( exclude ) {
for ( let position of exclude ) {
blocks = blocks . filter (
block => block . position . x !== position . x || block . position . y !== position . y || block . position . z !== position . z
) ;
}
}
2024-02-01 16:16:30 -06:00
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 ;
}
2023-12-04 21:33:40 -06:00
try {
await bot . collectBlock . collect ( block ) ;
collected ++ ;
2024-02-01 16:16:30 -06:00
await autoLight ( bot ) ;
2023-11-27 21:07:52 -06:00
}
2023-12-04 21:33:40 -06:00
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 ;
}
2023-12-08 16:18:20 -06:00
}
2024-01-23 18:01:38 -06:00
2023-12-08 16:18:20 -06:00
if ( bot . interrupt _code )
break ;
2023-09-29 15:53:16 -07:00
}
2023-12-04 21:33:40 -06:00
log ( bot , ` Collected ${ collected } ${ blockType } . ` ) ;
2024-02-05 13:21:32 -06:00
return collected > 0 ;
2023-12-04 21:33:40 -06:00
}
2024-02-02 15:34:17 -06:00
export async function pickupNearbyItems ( bot ) {
2023-12-04 21:33:40 -06:00
/ * *
* Pick up all nearby items .
* @ param { MinecraftBot } bot , reference to the minecraft bot .
* @ returns { Promise < boolean > } true if the items were picked up , false otherwise .
* @ example
2024-02-02 15:47:17 -06:00
* await skills . pickupNearbyItems ( bot ) ;
2023-12-04 21:33:40 -06:00
* * /
2024-02-02 15:34:17 -06:00
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 ++ ;
2023-12-04 21:33:40 -06:00
}
2024-02-02 15:34:17 -06:00
log ( bot , ` Picked up ${ pickedUp } items. ` ) ;
2023-12-04 21:33:40 -06:00
return true ;
2023-09-29 15:53:16 -07:00
}
export async function breakBlockAt ( bot , x , y , z ) {
2023-11-07 09:44:56 -06:00
/ * *
* 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 < boolean > } true if the block was broken , false otherwise .
* @ example
2023-11-07 20:21:19 -08:00
* let position = world . getPosition ( bot ) ;
2023-11-07 09:44:56 -06:00
* await skills . breakBlockAt ( bot , position . x , position . y - 1 , position . x ) ;
* * /
2024-02-16 11:57:48 -06:00
if ( x == null || y == null || z == null ) throw new Error ( 'Invalid position to break block at.' ) ;
2024-02-03 12:00:33 -06:00
let block = bot . blockAt ( Vec3 ( x , y , z ) ) ;
if ( block . name !== 'air' && block . name !== 'water' && block . name !== 'lava' ) {
2024-06-03 18:23:01 -05:00
if ( bot . modes . isOn ( 'cheat' ) ) {
let msg = '/setblock ' + Math . floor ( x ) + ' ' + Math . floor ( y ) + ' ' + Math . floor ( z ) + ' air' ;
bot . chat ( msg ) ;
log ( bot , ` Used /setblock to break block at ${ x } , ${ y } , ${ z } . ` ) ;
return true ;
}
2024-02-03 12:00:33 -06:00
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 ;
2024-02-05 19:08:08 -06:00
bot . pathfinder . setMovements ( movements ) ;
2024-02-03 12:00:33 -06:00
await bot . pathfinder . goto ( new pf . goals . GoalNear ( pos . x , pos . y , pos . z , 4 ) ) ;
}
2024-05-18 12:06:50 -05:00
if ( bot . game . gameMode !== 'creative' ) {
2024-05-14 21:24:41 -05:00
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 ;
}
2024-02-16 11:57:48 -06:00
}
2024-02-03 12:00:33 -06:00
await bot . dig ( block , true ) ;
2024-02-16 11:57:48 -06:00
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 ;
2024-02-03 12:00:33 -06:00
}
2023-09-29 15:53:16 -07:00
return true ;
}
2024-06-22 15:19:10 -05:00
export async function placeBlock ( bot , blockType , x , y , z , placeOn = 'bottom' , dontCheat = false ) {
2023-11-07 09:44:56 -06:00
/ * *
2023-11-12 23:19:58 -06:00
* 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 .
2023-11-07 09:44:56 -06:00
* @ param { MinecraftBot } bot , reference to the minecraft bot .
* @ param { string } blockType , the type of block to place .
2023-11-08 19:24:24 -06:00
* @ 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 .
2024-04-21 12:50:36 -05:00
* @ param { string } placeOn , the preferred side of the block to place on . Can be 'top' , 'bottom' , 'north' , 'south' , 'east' , 'west' , or 'side' . Defaults to bottom . Will place on first available side if not possible .
2024-06-22 15:19:10 -05:00
* @ param { boolean } dontCheat , overrides cheat mode to place the block normally . Defaults to false .
2023-11-07 09:44:56 -06:00
* @ returns { Promise < boolean > } true if the block was placed , false otherwise .
* @ example
2024-04-21 12:50:36 -05:00
* let p = world . getPosition ( bot ) ;
* await skills . placeBlock ( bot , "oak_log" , p . x + 2 , p . y , p . x ) ;
* await skills . placeBlock ( bot , "torch" , p . x + 1 , p . y , p . x , 'side' ) ;
2023-11-07 09:44:56 -06:00
* * /
2024-06-03 18:23:01 -05:00
if ( ! mc . getBlockId ( blockType ) ) {
log ( bot , ` Invalid block type: ${ blockType } . ` ) ;
return false ;
}
2024-06-22 15:19:10 -05:00
const target _dest = new Vec3 ( Math . floor ( x ) , Math . floor ( y ) , Math . floor ( z ) ) ;
if ( bot . modes . isOn ( 'cheat' ) && ! dontCheat ) {
// invert the facing direction
let face = placeOn === 'north' ? 'south' : placeOn === 'south' ? 'north' : placeOn === 'east' ? 'west' : 'east' ;
if ( blockType . includes ( 'torch' ) && placeOn !== 'bottom' ) {
// insert wall_ before torch
blockType = blockType . replace ( 'torch' , 'wall_torch' ) ;
if ( placeOn !== 'side' && placeOn !== 'top' ) {
blockType += ` [facing= ${ face } ] ` ;
}
}
if ( blockType . includes ( 'button' ) || blockType === 'lever' ) {
if ( placeOn === 'top' ) {
blockType += ` [face=ceiling] ` ;
}
else if ( placeOn === 'bottom' ) {
blockType += ` [face=floor] ` ;
}
else {
blockType += ` [facing= ${ face } ] ` ;
}
}
2024-06-22 15:35:18 -05:00
if ( blockType === 'ladder' || blockType === 'repeater' || blockType === 'comparator' ) {
2024-06-22 15:19:10 -05:00
blockType += ` [facing= ${ face } ] ` ;
}
2024-06-03 18:23:01 -05:00
let msg = '/setblock ' + Math . floor ( x ) + ' ' + Math . floor ( y ) + ' ' + Math . floor ( z ) + ' ' + blockType ;
bot . chat ( msg ) ;
2024-06-22 15:19:10 -05:00
if ( blockType . includes ( 'door' ) )
2024-06-22 16:04:49 -05:00
bot . chat ( '/setblock ' + Math . floor ( x ) + ' ' + Math . floor ( y + 1 ) + ' ' + Math . floor ( z ) + ' ' + blockType + '[half=upper]' ) ;
2024-06-22 15:19:10 -05:00
if ( blockType . includes ( 'bed' ) )
bot . chat ( '/setblock ' + Math . floor ( x ) + ' ' + Math . floor ( y ) + ' ' + Math . floor ( z - 1 ) + ' ' + blockType + '[part=head]' ) ;
log ( bot , ` Used /setblock to place ${ blockType } at ${ target _dest } . ` ) ;
2024-06-03 18:23:01 -05:00
return true ;
}
2024-04-02 19:18:13 -05:00
let block = bot . inventory . items ( ) . find ( item => item . name === blockType ) ;
2024-06-22 15:35:18 -05:00
if ( ! block && bot . game . gameMode === 'creative' ) {
await bot . creative . setInventorySlot ( 36 , mc . makeItem ( blockType , 1 ) ) ; // 36 is first hotbar slot
block = bot . inventory . items ( ) . find ( item => item . name === blockType ) ;
}
2024-04-02 19:18:13 -05:00
if ( ! block ) {
log ( bot , ` Don't have any ${ blockType } to place. ` ) ;
return false ;
}
2023-12-04 21:33:40 -06:00
const targetBlock = bot . blockAt ( target _dest ) ;
2024-04-02 19:18:13 -05:00
if ( targetBlock . name === blockType ) {
log ( bot , ` ${ blockType } already at ${ targetBlock . position } . ` ) ;
2023-11-12 23:19:58 -06:00
return false ;
}
2024-04-05 16:58:08 -05:00
const empty _blocks = [ 'air' , 'water' , 'lava' , 'grass' , 'short_grass' , 'tall_grass' , 'snow' , 'dead_bush' , 'fern' ] ;
2024-04-02 19:18:13 -05:00
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
}
2023-11-12 23:19:58 -06:00
// get the buildoffblock and facevec based on whichever adjacent block is not empty
let buildOffBlock = null ;
let faceVec = null ;
2024-04-21 12:50:36 -05:00
const dir _map = {
'top' : Vec3 ( 0 , 1 , 0 ) ,
'bottom' : Vec3 ( 0 , - 1 , 0 ) ,
'north' : Vec3 ( 0 , 0 , - 1 ) ,
'south' : Vec3 ( 0 , 0 , 1 ) ,
'east' : Vec3 ( 1 , 0 , 0 ) ,
'west' : Vec3 ( - 1 , 0 , 0 ) ,
}
let dirs = [ ] ;
if ( placeOn === 'side' ) {
dirs . push ( dir _map [ 'north' ] , dir _map [ 'south' ] , dir _map [ 'east' ] , dir _map [ 'west' ] ) ;
}
else if ( dir _map [ placeOn ] !== undefined ) {
dirs . push ( dir _map [ placeOn ] ) ;
}
else {
dirs . push ( dir _map [ 'bottom' ] ) ;
log ( bot , ` Unknown placeOn value " ${ placeOn } ". Defaulting to bottom. ` ) ;
}
dirs . push ( ... Object . values ( dir _map ) . filter ( d => ! dirs . includes ( d ) ) ) ;
2023-11-12 23:19:58 -06:00
for ( let d of dirs ) {
2023-12-04 21:33:40 -06:00
const block = bot . blockAt ( target _dest . plus ( d ) ) ;
2023-11-12 23:19:58 -06:00
if ( ! empty _blocks . includes ( block . name ) ) {
buildOffBlock = block ;
2024-04-21 12:50:36 -05:00
faceVec = new Vec3 ( - d . x , - d . y , - d . z ) ; // invert
2023-11-12 23:19:58 -06:00
break ;
}
}
if ( ! buildOffBlock ) {
2023-12-04 21:33:40 -06:00
log ( bot , ` Cannot place ${ blockType } at ${ targetBlock . position } : nothing to place on. ` ) ;
2023-11-12 23:19:58 -06:00
return false ;
}
2024-01-23 18:01:38 -06:00
const pos = bot . entity . position ;
const pos _above = pos . plus ( Vec3 ( 0 , 1 , 0 ) ) ;
2024-03-23 11:15:53 -05:00
const dont _move _for = [ 'torch' , 'redstone_torch' , 'redstone' , 'lever' , 'button' , 'rail' , 'detector_rail' , 'powered_rail' , 'activator_rail' , 'tripwire_hook' , 'tripwire' , 'water_bucket' ] ;
2024-01-23 18:01:38 -06:00
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 ) ;
2023-11-19 17:11:46 -06:00
}
if ( bot . entity . position . distanceTo ( targetBlock . position ) > 4.5 ) {
2024-01-23 18:01:38 -06:00
// too far
2023-11-19 17:11:46 -06:00
let pos = targetBlock . position ;
2024-03-23 11:15:53 -05:00
let movements = new pf . Movements ( bot ) ;
bot . pathfinder . setMovements ( movements ) ;
2023-11-19 17:11:46 -06:00
await bot . pathfinder . goto ( new pf . goals . GoalNear ( pos . x , pos . y , pos . z , 4 ) ) ;
}
2023-12-04 21:33:40 -06:00
await bot . equip ( block , 'hand' ) ;
await bot . lookAt ( buildOffBlock . position ) ;
2023-11-13 00:57:20 -06:00
2023-12-04 21:33:40 -06:00
// will throw error if an entity is in the way, and sometimes even if the block was placed
try {
await bot . placeBlock ( buildOffBlock , faceVec ) ;
2024-08-22 15:57:20 -05:00
log ( bot , ` Placed ${ blockType } at ${ target _dest } . ` ) ;
2023-12-04 21:33:40 -06:00
await new Promise ( resolve => setTimeout ( resolve , 200 ) ) ;
return true ;
} catch ( err ) {
log ( bot , ` Failed to place ${ blockType } at ${ target _dest } . ` ) ;
2023-11-12 23:19:58 -06:00
return false ;
}
2023-09-29 15:53:16 -07:00
}
2023-12-04 21:33:40 -06:00
export async function equip ( bot , itemName , bodyPart ) {
2023-11-07 09:44:56 -06:00
/ * *
2023-12-04 21:33:40 -06:00
* Equip the given item to the given body part , like tools or armor .
2023-11-07 09:44:56 -06:00
* @ param { MinecraftBot } bot , reference to the minecraft bot .
* @ param { string } itemName , the item or block name to equip .
2023-12-04 21:33:40 -06:00
* @ param { string } bodyPart , the body part to equip the item to .
2023-11-07 09:44:56 -06:00
* @ returns { Promise < boolean > } true if the item was equipped , false otherwise .
* @ example
2023-12-04 21:33:40 -06:00
* 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 < boolean > } true if the item was discarded , false otherwise .
* @ example
* await skills . discard ( bot , "oak_log" ) ;
2023-11-07 09:44:56 -06:00
* * /
2023-12-04 21:33:40 -06:00
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 ) {
2023-09-29 15:53:16 -07:00
break ;
}
}
2023-12-04 21:33:40 -06:00
if ( discarded === 0 ) {
log ( bot , ` You do not have any ${ itemName } to discard. ` ) ;
2023-09-29 15:53:16 -07:00
return false ;
2023-12-04 21:33:40 -06:00
}
log ( bot , ` Successfully discarded ${ discarded } ${ itemName } . ` ) ;
2023-09-29 15:53:16 -07:00
return true ;
}
2023-12-04 21:33:40 -06:00
export async function eat ( bot , foodName = "" ) {
2023-11-07 09:44:56 -06:00
/ * *
2023-12-04 21:33:40 -06:00
* Eat the given item . If no item is given , it will eat the first food item in the bot ' s inventory .
2023-11-07 09:44:56 -06:00
* @ param { MinecraftBot } bot , reference to the minecraft bot .
2023-12-04 21:33:40 -06:00
* @ param { string } item , the item to eat .
* @ returns { Promise < boolean > } true if the item was eaten , false otherwise .
2023-11-07 09:44:56 -06:00
* @ example
2023-12-04 21:33:40 -06:00
* await skills . eat ( bot , "apple" ) ;
2023-11-07 09:44:56 -06:00
* * /
2023-12-04 21:33:40 -06:00
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 } . ` ) ;
2023-09-29 15:53:16 -07:00
return true ;
}
2023-12-04 21:33:40 -06:00
export async function giveToPlayer ( bot , itemType , username , num = 1 ) {
2023-11-07 09:44:56 -06:00
/ * *
* 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 .
2023-12-04 21:33:40 -06:00
* @ param { number } num , the number of items to give . Defaults to 1.
2023-11-07 09:44:56 -06:00
* @ returns { Promise < boolean > } true if the item was given , false otherwise .
* @ example
* await skills . giveToPlayer ( bot , "oak_log" , "player1" ) ;
* * /
2023-09-29 15:53:16 -07:00
let player = bot . players [ username ] . entity
2023-11-27 21:33:44 -06:00
if ( ! player ) {
log ( bot , ` Could not find ${ username } . ` ) ;
2023-09-29 15:53:16 -07:00
return false ;
2023-11-27 21:33:44 -06:00
}
2023-12-04 21:33:40 -06:00
await goToPlayer ( bot , username ) ;
await bot . lookAt ( player . position ) ;
discard ( bot , itemType , num ) ;
return true ;
}
2024-01-23 18:01:38 -06:00
2023-12-04 21:33:40 -06:00
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 < boolean > } true if the position was reached , false otherwise .
* @ example
2024-01-25 13:25:36 -08:00
* let position = world . world . getNearestBlock ( bot , "oak_log" , 64 ) . position ;
2023-12-04 21:33:40 -06:00
* 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 } ` ) ;
2023-09-29 15:53:16 -07:00
return false ;
2023-11-27 21:33:44 -06:00
}
2024-06-03 18:33:00 -05:00
if ( bot . modes . isOn ( 'cheat' ) ) {
bot . chat ( '/tp @s ' + x + ' ' + y + ' ' + z ) ;
log ( bot , ` Teleported to ${ x } , ${ y } , ${ z } . ` ) ;
return true ;
}
2023-12-04 21:33:40 -06:00
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 } . ` ) ;
2023-09-29 15:53:16 -07:00
return true ;
}
2024-08-22 15:57:20 -05:00
export async function goToNearestBlock ( bot , blockType , min _distance = 2 , range = 64 ) {
/ * *
* Navigate to the nearest block of the given type .
* @ param { MinecraftBot } bot , reference to the minecraft bot .
* @ param { string } blockType , the type of block to navigate to .
* @ param { number } min _distance , the distance to keep from the block . Defaults to 2.
* @ param { number } range , the range to look for the block . Defaults to 64.
* @ returns { Promise < boolean > } true if the block was reached , false otherwise .
* @ example
* await skills . goToNearestBlock ( bot , "oak_log" , 64 , 2 ) ;
* * * /
const MAX _RANGE = 512 ;
if ( range > MAX _RANGE ) {
log ( bot , ` Maximum search range capped at ${ MAX _RANGE } . ` ) ;
range = MAX _RANGE ;
}
let block = world . getNearestBlock ( bot , blockType , range ) ;
if ( ! block ) {
log ( bot , ` Could not find any ${ blockType } in ${ range } blocks. ` ) ;
return false ;
}
log ( bot , ` Found ${ blockType } at ${ block . position } . ` ) ;
await goToPosition ( bot , block . position . x , block . position . y , block . position . z , min _distance ) ;
return true ;
}
2023-09-29 15:53:16 -07:00
2024-02-05 19:08:08 -06:00
export async function goToPlayer ( bot , username , distance = 3 ) {
2023-11-07 09:44:56 -06:00
/ * *
* Navigate to the given player .
* @ param { MinecraftBot } bot , reference to the minecraft bot .
* @ param { string } username , the username of the player to navigate to .
2024-02-05 19:08:08 -06:00
* @ param { number } distance , the goal distance to the player .
2023-11-07 09:44:56 -06:00
* @ returns { Promise < boolean > } true if the player was found , false otherwise .
* @ example
* await skills . goToPlayer ( bot , "player" ) ;
* * /
2024-06-03 18:23:01 -05:00
if ( bot . modes . isOn ( 'cheat' ) ) {
bot . chat ( '/tp @s ' + username ) ;
log ( bot , ` Teleported to ${ username } . ` ) ;
return true ;
}
2024-01-23 18:01:38 -06:00
bot . modes . pause ( 'self_defense' ) ;
2024-04-20 22:22:26 -05:00
bot . modes . pause ( 'cowardice' ) ;
2023-08-15 23:39:02 -07:00
let player = bot . players [ username ] . entity
2023-12-04 21:33:40 -06:00
if ( ! player ) {
log ( bot , ` Could not find ${ username } . ` ) ;
2023-08-15 23:39:02 -07:00
return false ;
2023-12-04 21:33:40 -06:00
}
2024-03-23 11:15:53 -05:00
const move = new pf . Movements ( bot ) ;
bot . pathfinder . setMovements ( move ) ;
2024-02-05 19:08:08 -06:00
await bot . pathfinder . goto ( new pf . goals . GoalFollow ( player , distance ) , true ) ;
2024-01-13 12:11:04 -06:00
log ( bot , ` You have reached ${ username } . ` ) ;
2023-08-15 23:39:02 -07:00
}
2023-11-07 09:44:56 -06:00
2024-02-05 19:08:08 -06:00
export async function followPlayer ( bot , username , distance = 4 ) {
2023-11-07 09:44:56 -06:00
/ * *
2023-11-12 17:41:01 -06:00
* Follow the given player endlessly . Will not return until the code is manually stopped .
2023-11-07 09:44:56 -06:00
* @ param { MinecraftBot } bot , reference to the minecraft bot .
* @ param { string } username , the username of the player to follow .
* @ returns { Promise < boolean > } true if the player was found , false otherwise .
* @ example
* await skills . followPlayer ( bot , "player" ) ;
* * /
2024-01-13 12:11:04 -06:00
let player = bot . players [ username ] . entity
if ( ! player )
return false ;
2024-03-23 11:15:53 -05:00
const move = new pf . Movements ( bot ) ;
bot . pathfinder . setMovements ( move ) ;
2024-02-05 19:08:08 -06:00
bot . pathfinder . setGoal ( new pf . goals . GoalFollow ( player , distance ) , true ) ;
2024-01-23 18:01:38 -06:00
log ( bot , ` You are now actively following player ${ username } . ` ) ;
2024-01-13 12:11:04 -06:00
while ( ! bot . interrupt _code ) {
await new Promise ( resolve => setTimeout ( resolve , 500 ) ) ;
2024-08-22 15:57:20 -05:00
// in cheat mode, if the distance is too far, teleport to the player
if ( bot . modes . isOn ( 'cheat' ) && bot . entity . position . distanceTo ( player . position ) > 100 && player . isOnGround ) {
await goToPlayer ( bot , username ) ;
}
2024-01-13 12:11:04 -06:00
}
2023-11-07 09:44:56 -06:00
return true ;
2024-01-16 15:53:27 -06:00
}
2024-01-23 18:01:38 -06:00
2024-02-02 11:54:17 -06:00
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 < boolean > } 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 ) ) ;
2024-06-03 18:23:01 -05:00
if ( bot . modes . isOn ( 'cheat' ) ) {
2024-08-22 15:57:20 -05:00
const move = new pf . Movements ( bot ) ;
2024-06-03 18:23:01 -05:00
const path = await bot . pathfinder . getPathTo ( move , inverted _goal , 10000 ) ;
let last _move = path . path [ path . path . length - 1 ] ;
console . log ( last _move ) ;
if ( last _move ) {
let x = Math . floor ( last _move . x ) ;
let y = Math . floor ( last _move . y ) ;
let z = Math . floor ( last _move . z ) ;
bot . chat ( '/tp @s ' + x + ' ' + y + ' ' + z ) ;
return true ;
}
}
2024-02-02 11:54:17 -06:00
await bot . pathfinder . goto ( inverted _goal ) ;
let new _pos = bot . entity . position ;
log ( bot , ` Moved away from nearest entity to ${ new _pos } . ` ) ;
return true ;
}
2024-04-13 22:56:18 -05:00
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 < boolean > } true if the bot moved away , false otherwise .
* @ example
* await skills . avoidEnemies ( bot , 8 ) ;
* * /
2024-05-04 16:17:41 -05:00
bot . modes . pause ( 'self_preservation' ) ; // prevents damage-on-low-health from interrupting the bot
2024-04-13 22:56:18 -05:00
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 ) {
2024-05-04 16:17:41 -05:00
break ;
2024-04-13 22:56:18 -05:00
}
}
2024-05-04 16:17:41 -05:00
bot . pathfinder . stop ( ) ;
2024-04-13 22:56:18 -05:00
log ( bot , ` Moved ${ distance } away from enemies. ` ) ;
return true ;
}
2024-02-02 11:54:17 -06:00
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 < boolean > } true if the bot stayed , false otherwise .
* @ example
* await skills . stay ( bot ) ;
* * /
2024-04-20 22:22:26 -05:00
bot . modes . pause ( 'self_preservation' ) ;
2024-04-13 22:56:18 -05:00
bot . modes . pause ( 'cowardice' ) ;
2024-02-02 11:54:17 -06:00
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 ) ) ;
2024-01-13 12:11:04 -06:00
}
2023-11-07 09:44:56 -06:00
return true ;
2024-01-16 15:53:27 -06:00
}
2024-03-20 16:03:16 -07:00
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 < boolean > } 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 ;
}
2024-01-23 18:01:38 -06:00
2024-01-16 15:53:27 -06:00
export async function goToBed ( bot ) {
/ * *
* Sleep in the nearest bed .
* @ param { MinecraftBot } bot , reference to the minecraft bot .
* @ returns { Promise < boolean > } 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 ;
}
2024-03-23 11:15:53 -05:00
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 < boolean > } 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 ;
}
2024-04-19 14:02:30 -05:00
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 < boolean > } 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 ;
}