const { Movements } = require ( 'mineflayer-pathfinder' )
const v = require ( 'vec3' )
let cfg = { }
let bot = { }
// let moving
let pathfinder
let mcData
let movements = [ ]
function initMoves ( bot = bot , mcData = bot . mcData ) {
if ( movements . length > 0 ) {
bot . pathfinder . setMovements ( movements . defaultMove )
return console . warn ( "go init: movements already initialized!" , movements )
}
const normalMove = new Movements ( bot , mcData )
normalMove . canDig = false
normalMove . scafoldingBlocks . push ( mcData . blocksByName . slime _block . id )
normalMove . blocksCantBreak . add ( mcData . blocksByName . glass . id )
normalMove . blocksToAvoid . add ( mcData . blocksByName . magma _block . id )
movements . push ( normalMove )
movements . defaultMove = movements [ 0 ]
bot . pathfinder . setMovements ( normalMove )
}
function moveNear ( pos , distance = 3 ) {
const { GoalNear } = require ( 'mineflayer-pathfinder' ) . goals
pos = v ( pos )
cfg . quiet || bot . chat ( ` moving to ${ pos . floored ( ) } ` )
bot . pathfinder . setMovements ( movements . defaultMove )
bot . pathfinder . setGoal ( new GoalNear ( pos . x , pos . y , pos . z , distance ) )
}
function moveXZ ( pos ) {
const { GoalXZ } = require ( 'mineflayer-pathfinder' ) . goals
if ( Array . isArray ( pos ) && pos . length == 2 ) {
pos = v ( pos [ 0 ] , 0 , pos [ 1 ] )
}
pos = v ( pos )
console . log ( pos )
cfg . quiet || bot . chat ( ` moving to ${ pos . floored ( ) } ` )
bot . pathfinder . setMovements ( movements . defaultMove )
bot . pathfinder . setGoal ( new GoalXZ ( pos . x , pos . z ) )
}
function moveY ( pos ) {
const { GoalY } = require ( 'mineflayer-pathfinder' ) . goals
if ( Array . isArray ( pos ) && pos . length == 1 ) {
pos = v ( null , pos [ 0 ] , null )
}
pos = v ( pos )
console . log ( pos )
cfg . quiet || bot . chat ( ` moving to ${ pos . floored ( ) } ` )
bot . pathfinder . setMovements ( movements . defaultMove )
bot . pathfinder . setGoal ( new GoalY ( pos . y ) )
}
function follow ( entity , dynamic = true , distance = 3 ) {
console . assert ( entity )
const { GoalFollow } = require ( 'mineflayer-pathfinder' ) . goals
// console.log(entity)
cfg . quiet || bot . chat (
` following ${ entity . type
} : $ { entity . username || entity . displayName
} $ { dynamic ? "" : " once" } `
)
entity = entity . entity ? entity . entity : entity
// console.log(entity)
bot . pathfinder . setMovements ( movements . defaultMove )
bot . pathfinder . setGoal ( new GoalFollow ( entity , distance ) , dynamic )
}
function away ( entity = bot . nearestEntity ( ) , invertInvert = true , dynamic = true , distance = 10 ) {
const currentGoal = bot . pathfinder . goal
console . assert ( currentGoal || entity )
const { GoalInvert } = require ( 'mineflayer-pathfinder' ) . goals
bot . pathfinder . setMovements ( movements . defaultMove )
if ( ! currentGoal ) {
const { GoalFollow } = require ( 'mineflayer-pathfinder' ) . goals
if ( entity . entity ) {
console . log ( "go away entity:" , entity , entity . entity )
entity = entity . entity
}
cfg . quiet || bot . chat (
` going away from ${ entity ? . type
} : $ { entity ? . username || entity ? . displayName
} $ { dynamic ? "" : " once" } `
)
// alternative implementation
// follow(entity, dynamic, distance)
// bot.pathfinder.setGoal(new GoalInvert(bot.pathfinder.goal), dynamic)
return bot . pathfinder . setGoal ( new GoalInvert (
new GoalFollow ( entity , distance )
) , dynamic )
}
if ( currentGoal instanceof GoalInvert ) {
const currEntity = currentGoal . goal . entity
console . log ( "go away inverse goal:" , currentGoal . goal )
if ( invertInvert ) {
cfg . quiet || bot . chat (
` switching towards ${ currentGoal . goal ? . constructor . name
} : $ { currEntity ? . type
} : $ { currEntity ? . username || currEntity ? . displayName
} $ { dynamic ? "" : " once" } `
)
bot . pathfinder . setGoal ( currentGoal . goal , dynamic )
} else {
cfg . quiet || bot . chat (
` already going away from ${ currentGoal . goal ? . constructor . name
} ; not switching `
)
}
} else {
const currEntity = currentGoal . entity
console . log ( "go away goal:" , currentGoal )
cfg . quiet || bot . chat (
` going away from ${ currentGoal ? . constructor . name
} : $ { currEntity ? . type
} : $ { currEntity ? . username || currEntity ? . displayName
} $ { dynamic ? "" : " once" } `
)
bot . pathfinder . setGoal ( new GoalInvert ( currentGoal ) , dynamic )
}
}
function ride ( entity ) {
entity = entity ? . entity || entity
const ridableMobs = [ "Horse" , "Donkey" , "Pig" , "Strider" , "Mule" ]
const vehicle = entity && typeof entity !== "string" ? entity : bot . nearestEntity ( e => {
if ( typeof entity === "string" ) return e . name === entity
const maybeRidableMob = e . mobType ? . split ( " " )
return e . kind == "Vehicles"
|| ridableMobs . includes ( e . mobType )
|| maybeRidableMob && ridableMobs . includes ( maybeRidableMob [ maybeRidableMob . length - 1 ] )
} )
if ( ! vehicle ) {
return cfg . quiet || bot . chat ( ` nothing to ride! ` )
} else if ( ( dist = bot . entity . position . distanceSquared ( vehicle . position ) ) > 36 ) {
bot . lookAt ( vehicle . position )
follow ( vehicle , false )
bot . once ( 'goal_reached' , ride )
return cfg . quiet || bot . chat ( ` ${ vehicle . name } bit far ` )
}
console . log ( "vehicle:" , vehicle )
bot . mount ( vehicle )
}
function moveOrRide ( turn = false , reverse = - 1 , directionLabel , message _parts2 ) {
// bot.once("attach", state = "vehiccel")
if ( bot . vehicle ) {
// FIXME moveVehicle should be +-1 or 0?
const amount = parseInt ( message _parts2 [ 0 ] ) * - reverse
bot . moveVehicle ( turn && Math . sign ( amount ) || 0 , ! turn && amount || 0 )
} else {
command ( [ directionLabel ] . concat ( message _parts2 ) )
}
}
function hit ( blockOrEntity ) {
bot . chat ( ` hitting ${ entity . name || entity . type } ` )
}
function goalReached ( goal ) {
console . log ( goal )
const entity = goal ? . entity
let entityInfo = ""
if ( entity ) {
entityInfo += entity . type + ": "
switch ( entity . type ) {
case "player" :
entityInfo += entity . username
break ;
default :
break ;
}
}
cfg . quiet || bot . chat ( ` goal reached: ${ entityInfo } ; pos: [x: ${ goal ? . x } , y: ${ goal ? . y } , z: ${ goal ? . z } ] ` )
}
function stop ( ) {
bot . pathfinder . setGoal ( null )
bot . stopDigging ( )
}
function command ( message _parts , player ) {
const message _parts2 = message _parts . slice ( 1 )
switch ( message _parts [ 0 ] ) {
case "init" :
initMoves ( )
break
case "near" :
switch ( message _parts2 . length ) {
case 0 :
moveNear ( bot . nearestEntity ( ) . position )
break
case 1 :
switch ( message _parts2 [ 0 ] ) {
case "me" :
if ( player ) {
moveNear ( player . position )
} else {
cfg . quiet || bot . chat ( "can't see you" )
}
break ;
default :
const aPlayer = bot . players [ message _parts2 [ 0 ] ] ? bot . players [ message _parts2 [ 0 ] ] . entity : null
if ( aPlayer ) {
moveNear ( aPlayer . position )
} else {
cfg . quiet || bot . chat ( ` can't see ${ message _parts2 [ 0 ] } ` )
}
break ;
}
break
case 2 :
//TODO this isn't near
moveXZ ( message _parts2 )
break
case 3 :
//TODO more checks
moveNear ( message _parts2 )
break
default :
break
}
break
case "follow" :
// message_parts2 = message_parts.slice(2)
switch ( message _parts2 . length ) {
case 0 :
follow ( bot . nearestEntity ( ) )
break
case 1 :
let dist = 3
switch ( message _parts2 [ 0 ] ) {
case "close" :
dist = 1
case "me" :
case "once" :
if ( player ) {
follow ( player , message _parts2 [ 0 ] === "me" , dist )
} else {
cfg . quiet || bot . chat ( "can't see you" )
}
break ;
default :
const aPlayer = bot . players [ message _parts2 [ 0 ] ] ? bot . players [ message _parts2 [ 0 ] ] . entity : null
if ( aPlayer ) {
follow ( aPlayer )
} else {
cfg . quiet || bot . chat ( ` can't see ${ message _parts2 [ 0 ] } ` )
}
break ;
}
break
// case 2:
// bot.lookAt({}) goalxz?
// break
// case 3:
//TODO more checks
// moveNear(message_parts2)
// break
default :
cfg . quiet || bot . chat ( "unknown or bad command" )
break
}
break
case "ride" :
case "mount" :
ride ( message _parts2 [ 0 ] )
break
case "away" :
case "run" :
case "runaway" :
away ( )
break
case "w" :
case "f" :
moveOrRide ( 0 , - 1 , "forward" , message _parts2 )
break
case "s" :
case "b" :
moveOrRide ( 0 , 1 , "back" , message _parts2 )
break
case "a" :
case "l" :
moveOrRide ( 1 , - 1 , "right" , message _parts2 )
break
case "d" :
case "r" :
moveOrRide ( 1 , 1 , "left" , message _parts2 )
break
case "up" :
case "u" :
case "j" :
moveOrRide ( 1 , 1 , "left" , message _parts2 )
break
case "back" :
case "forward" :
case "jump" :
case "left" :
case "right" :
case "sneak" :
case "sprint" :
console . info ( bot . controlState [ message _parts [ 0 ] ] , bot . entity . position . floored ( ) )
bot . setControlState ( message _parts [ 0 ] , true )
console . info ( bot . controlState [ message _parts [ 0 ] ] )
setTimeout ( bot . setControlState , 100 * ( message _parts [ 1 ] || 2 ) , message _parts [ 0 ] , false )
setTimeout ( console . info , 5000 , bot . controlState [ message _parts [ 0 ] ] , bot . entity . position . floored ( ) )
break
case "stop" :
stop ( )
break
default :
return cfg . quiet || bot . chat ( ` unknown command ${ message _parts [ 0 ] } ` )
}
}
const load = ( config ) => {
cfg = config
bot = cfg . bot
cfg . move = {
// auto: true,
canDig : false ,
// list: ["hello", "wassup"],
quiet : ! ! cfg . quiet ,
movements : [ ]
}
mcData = bot . mcData || ( bot . mcData = require ( 'minecraft-data' ) ( bot . version ) )
pathfinder = bot . pathfinder || bot . loadPlugin ( require ( 'mineflayer-pathfinder' ) . pathfinder )
// initMoves(bot, mcData)
setTimeout ( initMoves , 500 , bot , mcData )
bot . on ( 'goal_reached' , goalReached )
}
const unload = ( ) => {
stop ( )
bot . off ( 'goal_reached' , goalReached )
}
module . exports = {
load , unload , command ,
stop , initMoves ,
moveNear , moveXZ , moveY , follow ,
ride
}