diff --git a/src/agent/library/skills.js b/src/agent/library/skills.js index adf5912..ac52ec1 100644 --- a/src/agent/library/skills.js +++ b/src/agent/library/skills.js @@ -764,6 +764,49 @@ export async function stay(bot) { return true; } +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} 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; +} export async function goToBed(bot) { /** diff --git a/src/agent/npc/build_goal.js b/src/agent/npc/build_goal.js index 27fc3c2..3af1e88 100644 --- a/src/agent/npc/build_goal.js +++ b/src/agent/npc/build_goal.js @@ -2,7 +2,7 @@ import { Vec3 } from 'vec3'; import * as skills from '../library/skills.js'; import * as world from '../library/world.js'; import * as mc from '../../utils/mcdata.js'; -import { blockSatisfied, getTypeOfGeneric } from './utils.js'; +import { blockSatisfied, getTypeOfGeneric, rotateXZ } from './utils.js'; export class BuildGoal { @@ -10,13 +10,6 @@ export class BuildGoal { this.agent = agent; } - rotateXZ(x, z, orientation, sizex, sizez) { - if (orientation === 0) return [x, z]; - if (orientation === 1) return [z, sizex-x-1]; - if (orientation === 2) return [sizex-x-1, sizez-z-1]; - if (orientation === 3) return [sizez-z-1, x]; - } - async wrapSkill(func) { if (!this.agent.isIdle()) return false; @@ -45,7 +38,7 @@ export class BuildGoal { for (let z = 0; z < sizez; z++) { for (let x = 0; x < sizex; x++) { - let [rx, rz] = this.rotateXZ(x, z, orientation, sizex, sizez); + let [rx, rz] = rotateXZ(x, z, orientation, sizex, sizez); let ry = y - goal.offset; let block_name = goal.blocks[ry][rz][rx]; if (block_name === null || block_name === '') continue; diff --git a/src/agent/npc/controller.js b/src/agent/npc/controller.js index 1ac20ba..5ae358f 100644 --- a/src/agent/npc/controller.js +++ b/src/agent/npc/controller.js @@ -2,7 +2,10 @@ import { readdirSync, readFileSync } from 'fs'; import { NPCData } from './data.js'; import { ItemGoal } from './item_goal.js'; import { BuildGoal } from './build_goal.js'; -import { itemSatisfied } from './utils.js'; +import { itemSatisfied, rotateXZ } from './utils.js'; +import * as skills from '../library/skills.js'; +import * as world from '../library/world.js'; +import * as mc from '../../utils/mcdata.js'; export class NPCContoller { @@ -53,14 +56,52 @@ export class NPCContoller { if (!this.agent.isIdle()) return; // Persue goal - if (!this.agent.coder.resume_func) + if (!this.agent.coder.resume_func) { this.executeNext(); + this.agent.history.save(); + } }); } async executeNext() { + if (!this.agent.isIdle()) return; + + if (this.agent.bot.time.timeOfDay < 12000) { + // Exit any buildings + let building = this.currentBuilding(); + if (building) { + let door_pos = this.getBuildingDoor(building); + if (door_pos) { + await this.agent.coder.execute(async () => { + await skills.useDoor(this.agent.bot, door_pos); + }); + } + } + + // Work towards goals + await this.executeGoal(); + + } else { + // Return to home + let building = this.currentBuilding(); + if (this.data.home !== null && (building === null || building != this.data.home)) { + let door_pos = this.getBuildingDoor(this.data.home); + await this.agent.coder.execute(async () => { + await skills.useDoor(this.agent.bot, door_pos); + }); + } + + // Go to bed + await this.agent.coder.execute(async () => { + await skills.goToBed(this.agent.bot); + }); + } + } + + async executeGoal() { // If we need more blocks to complete a building, get those first let goals = this.temp_goals.concat(this.data.goals); + this.temp_goals = []; for (let goal of goals) { @@ -89,7 +130,9 @@ export class NPCContoller { orientation: res.orientation }; } - this.temp_goals = []; + if (Object.keys(res.missing).length === 0) { + this.data.home = goal.name; + } for (let block_name in res.missing) { this.temp_goals.push({ name: block_name, @@ -103,4 +146,57 @@ export class NPCContoller { if (this.agent.isIdle()) this.agent.bot.emit('idle'); } + + currentBuilding() { + let bot_pos = this.agent.bot.entity.position; + for (let name in this.data.built) { + let pos = this.data.built[name].position; + let offset = this.constructions[name].offset; + let sizex = this.constructions[name].blocks[0][0].length; + let sizez = this.constructions[name].blocks[0].length; + let sizey = this.constructions[name].blocks.length; + if (this.data.built[name].orientation % 2 === 1) [sizex, sizez] = [sizez, sizex]; + if (bot_pos.x >= pos.x && bot_pos.x < pos.x + sizex && + bot_pos.y >= pos.y + offset && bot_pos.y < pos.y + sizey + offset && + bot_pos.z >= pos.z && bot_pos.z < pos.z + sizez) { + return name; + } + } + return null; + } + + getBuildingDoor(name) { + if (name === null || this.data.built[name] === undefined) return null; + let door_x = null; + let door_z = null; + let door_y = null; + for (let y = 0; y < this.constructions[name].blocks.length; y++) { + for (let z = 0; z < this.constructions[name].blocks[y].length; z++) { + for (let x = 0; x < this.constructions[name].blocks[y][z].length; x++) { + if (this.constructions[name].blocks[y][z][x] !== null && + this.constructions[name].blocks[y][z][x].includes('door')) { + door_x = x; + door_z = z; + door_y = y; + break; + } + } + if (door_x !== null) break; + } + if (door_x !== null) break; + } + if (door_x === null) return null; + + let sizex = this.constructions[name].blocks[0][0].length; + let sizez = this.constructions[name].blocks[0].length; + let orientation = this.data.built[name].orientation; + [door_x, door_z] = rotateXZ(door_x, door_z, orientation, sizex, sizez); + door_y += this.constructions[name].offset; + + return { + x: this.data.built[name].position.x + door_x, + y: this.data.built[name].position.y + door_y, + z: this.data.built[name].position.z + door_z + }; + } } \ No newline at end of file diff --git a/src/agent/npc/data.js b/src/agent/npc/data.js index b160c79..70b59a4 100644 --- a/src/agent/npc/data.js +++ b/src/agent/npc/data.js @@ -2,6 +2,7 @@ export class NPCData { constructor() { this.goals = []; this.built = {}; + this.home = null; } toObject() { @@ -10,6 +11,8 @@ export class NPCData { obj.goals = this.goals; if (Object.keys(this.built).length > 0) obj.built = this.built; + if (this.home) + obj.home = this.home; return obj; } @@ -27,6 +30,8 @@ export class NPCData { } if (obj.built) npc.built = obj.built; + if (obj.home) + npc.home = obj.home; return npc; } } \ No newline at end of file diff --git a/src/agent/npc/utils.js b/src/agent/npc/utils.js index a92a38a..d5c0528 100644 --- a/src/agent/npc/utils.js +++ b/src/agent/npc/utils.js @@ -114,3 +114,11 @@ export function itemSatisfied(bot, item, quantity=1) { } return false; } + + +export function rotateXZ(x, z, orientation, sizex, sizez) { + if (orientation === 0) return [x, z]; + if (orientation === 1) return [z, sizex-x-1]; + if (orientation === 2) return [sizex-x-1, sizez-z-1]; + if (orientation === 3) return [sizez-z-1, x]; +}