194 lines
6.3 KiB
JavaScript
194 lines
6.3 KiB
JavaScript
const { Vec3 } = require('vec3')
|
|
|
|
module.exports = inject
|
|
|
|
const CARDINAL_DIRECTIONS = ['south', 'west', 'north', 'east']
|
|
|
|
function inject (bot) {
|
|
bot.isSleeping = false
|
|
|
|
const beds = new Set(['white_bed', 'orange_bed', 'magenta_bed', 'light_blue_bed', 'yellow_bed', 'lime_bed', 'pink_bed', 'gray_bed',
|
|
'light_gray_bed', 'cyan_bed', 'purple_bed', 'blue_bed', 'brown_bed', 'green_bed', 'red_bed', 'black_bed', 'bed'])
|
|
|
|
function isABed (block) {
|
|
return beds.has(block.name)
|
|
}
|
|
|
|
function parseBedMetadata (bedBlock) {
|
|
const metadata = {
|
|
part: false, // true: head, false: foot
|
|
occupied: 0,
|
|
facing: 0, // 0: south, 1: west, 2: north, 3 east
|
|
headOffset: new Vec3(0, 0, 1)
|
|
}
|
|
|
|
if (bot.supportFeature('blockStateId')) {
|
|
const state = bedBlock.stateId - bot.registry.blocksByStateId[bedBlock.stateId].minStateId
|
|
const bitMetadata = state.toString(2).padStart(4, '0') // FACING (first 2 bits), PART (3rd bit), OCCUPIED (4th bit)
|
|
metadata.part = bitMetadata[3] === '0'
|
|
metadata.occupied = bitMetadata[2] === '0'
|
|
|
|
switch (bitMetadata.slice(0, 2)) {
|
|
case '00':
|
|
metadata.facing = 2
|
|
metadata.headOffset.set(0, 0, -1)
|
|
break
|
|
case '10':
|
|
metadata.facing = 1
|
|
metadata.headOffset.set(-1, 0, 0)
|
|
break
|
|
case '11':
|
|
metadata.facing = 3
|
|
metadata.headOffset.set(1, 0, 0)
|
|
}
|
|
} else if (bot.supportFeature('blockMetadata')) {
|
|
const bitMetadata = bedBlock.metadata.toString(2).padStart(4, '0') // PART (1st bit), OCCUPIED (2nd bit), FACING (last 2 bits)
|
|
metadata.part = bitMetadata[0] === '1'
|
|
metadata.occupied = bitMetadata[1] === '1'
|
|
|
|
switch (bitMetadata.slice(2, 4)) {
|
|
case '01':
|
|
metadata.facing = 1
|
|
metadata.headOffset.set(-1, 0, 0)
|
|
break
|
|
case '10':
|
|
metadata.facing = 2
|
|
metadata.headOffset.set(0, 0, -1)
|
|
break
|
|
case '11':
|
|
metadata.facing = 3
|
|
metadata.headOffset.set(1, 0, 0)
|
|
}
|
|
}
|
|
|
|
return metadata
|
|
}
|
|
|
|
async function wake () {
|
|
if (!bot.isSleeping) {
|
|
throw new Error('already awake')
|
|
} else {
|
|
bot._client.write('entity_action', {
|
|
entityId: bot.entity.id,
|
|
actionId: 2,
|
|
jumpBoost: 0
|
|
})
|
|
}
|
|
}
|
|
|
|
async function sleep (bedBlock) {
|
|
const thunderstorm = bot.isRaining && (bot.thunderState > 0)
|
|
if (!thunderstorm && !(bot.time.timeOfDay >= 12541 && bot.time.timeOfDay <= 23458)) {
|
|
throw new Error("it's not night and it's not a thunderstorm")
|
|
} else if (bot.isSleeping) {
|
|
throw new Error('already sleeping')
|
|
} else if (!isABed(bedBlock)) {
|
|
throw new Error('wrong block : not a bed block')
|
|
} else {
|
|
const botPos = bot.entity.position.floored()
|
|
const metadata = parseBedMetadata(bedBlock)
|
|
let headPoint = bedBlock.position
|
|
|
|
if (metadata.occupied) {
|
|
throw new Error('the bed is occupied')
|
|
}
|
|
|
|
if (!metadata.part) { // Is foot
|
|
const upperBlock = bot.blockAt(bedBlock.position.plus(metadata.headOffset))
|
|
|
|
if (isABed(upperBlock)) {
|
|
headPoint = upperBlock.position
|
|
} else {
|
|
const lowerBlock = bot.blockAt(bedBlock.position.plus(metadata.headOffset.scaled(-1)))
|
|
|
|
if (isABed(lowerBlock)) {
|
|
// If there are 2 foot parts, minecraft only lets you sleep if you click on the lower one
|
|
headPoint = bedBlock.position
|
|
bedBlock = lowerBlock
|
|
} else {
|
|
throw new Error("there's only half bed")
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bot.canDigBlock(bedBlock)) {
|
|
throw new Error('cant click the bed')
|
|
}
|
|
|
|
const clickRange = [2, -3, -3, 2] // [south, west, north, east]
|
|
const monsterRange = [7, -8, -8, 7]
|
|
const oppositeCardinal = (metadata.facing + 2) % CARDINAL_DIRECTIONS.length
|
|
|
|
if (clickRange[oppositeCardinal] < 0) {
|
|
clickRange[oppositeCardinal]--
|
|
} else {
|
|
clickRange[oppositeCardinal]++
|
|
}
|
|
|
|
const nwClickCorner = headPoint.offset(clickRange[1], -2, clickRange[2]) // North-West lower corner
|
|
const seClickCorner = headPoint.offset(clickRange[3], 2, clickRange[0]) // South-East upper corner
|
|
if (botPos.x > seClickCorner.x || botPos.x < nwClickCorner.x || botPos.y > seClickCorner.y || botPos.y < nwClickCorner.y || botPos.z > seClickCorner.z || botPos.z < nwClickCorner.z) {
|
|
throw new Error('the bed is too far')
|
|
}
|
|
|
|
if (bot.game.gameMode !== 'creative' || bot.supportFeature('creativeSleepNearMobs')) { // If in creative mode the bot should be able to sleep even if there are monster nearby (starting in 1.13)
|
|
const nwMonsterCorner = headPoint.offset(monsterRange[1], -6, monsterRange[2]) // North-West lower corner
|
|
const seMonsterCorner = headPoint.offset(monsterRange[3], 4, monsterRange[0]) // South-East upper corner
|
|
|
|
for (const key of Object.keys(bot.entities)) {
|
|
const entity = bot.entities[key]
|
|
if (entity.kind === 'Hostile mobs') {
|
|
const entityPos = entity.position.floored()
|
|
if (entityPos.x <= seMonsterCorner.x && entityPos.x >= nwMonsterCorner.x && entityPos.y <= seMonsterCorner.y && entityPos.y >= nwMonsterCorner.y && entityPos.z <= seMonsterCorner.z && entityPos.z >= nwMonsterCorner.z) {
|
|
throw new Error('there are monsters nearby')
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bot.activateBlock(bedBlock)
|
|
|
|
await waitUntilSleep()
|
|
}
|
|
}
|
|
|
|
async function waitUntilSleep () {
|
|
return new Promise((resolve, reject) => {
|
|
const timeoutForSleep = setTimeout(() => {
|
|
reject(new Error('bot is not sleeping'))
|
|
}, 3000)
|
|
|
|
bot.once('sleep', () => {
|
|
clearTimeout(timeoutForSleep)
|
|
resolve()
|
|
})
|
|
})
|
|
}
|
|
|
|
bot._client.on('game_state_change', (packet) => {
|
|
if (packet.reason === 0) {
|
|
// occurs when you can't spawn in your bed and your spawn point gets reset
|
|
bot.emit('spawnReset')
|
|
}
|
|
})
|
|
|
|
bot.on('entitySleep', (entity) => {
|
|
if (entity === bot.entity) {
|
|
bot.isSleeping = true
|
|
bot.emit('sleep')
|
|
}
|
|
})
|
|
|
|
bot.on('entityWake', (entity) => {
|
|
if (entity === bot.entity) {
|
|
bot.isSleeping = false
|
|
bot.emit('wake')
|
|
}
|
|
})
|
|
|
|
bot.parseBedMetadata = parseBedMetadata
|
|
bot.wake = wake
|
|
bot.sleep = sleep
|
|
bot.isABed = isABed
|
|
}
|