Compare commits
No commits in common. "ef4124c2df9ef043e4f31e7dd027aa9366ed5dde" and "126f2d6bfca60cfeb1c09eabb74ed2c4a1989eaa" have entirely different histories.
ef4124c2df
...
126f2d6bfc
65
behavior-tree/index.mjs
Normal file
65
behavior-tree/index.mjs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
export class BehaviorTree {
|
||||||
|
constructor(root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Node {
|
||||||
|
constructor() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ExecutionNode extends Node {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
isLeaf() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ControlNode extends Node {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.children = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
isLeaf() { return false; }
|
||||||
|
appendChild(child) {
|
||||||
|
this.children.push(child);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export class SequenceNode extends ControlNode {
|
||||||
|
constructor() { super(); }
|
||||||
|
|
||||||
|
async tick(blackboard) {
|
||||||
|
for (let child of this.children) {
|
||||||
|
await child.tick(blackboard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export class FallbackNode extends ControlNode {
|
||||||
|
constructor() { super(); }
|
||||||
|
|
||||||
|
async tick(blackboard) {
|
||||||
|
for (let i = 0; i < this.children.length; i += 1) {
|
||||||
|
let child = this.children[i];
|
||||||
|
try {
|
||||||
|
await child.tick(blackboard);
|
||||||
|
break;
|
||||||
|
} catch(err) {
|
||||||
|
if (i == this.children.length - 1) { throw err; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ParallelNode extends ControlNode {
|
||||||
|
constructor() { super(); }
|
||||||
|
|
||||||
|
tick(blackboard) {
|
||||||
|
return Promise.all(this.children.map(child => child(blackboard)));
|
||||||
|
}
|
||||||
|
}
|
10
behavior-tree/package.json
Normal file
10
behavior-tree/package.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "compass-behavior-tree",
|
||||||
|
"description": "Behavior Tree Library",
|
||||||
|
"type": "module",
|
||||||
|
"main": "index.mjs",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"compass-utils": "file:../utils"
|
||||||
|
}
|
||||||
|
}
|
31
enhanced-vec3/index.d.ts
vendored
31
enhanced-vec3/index.d.ts
vendored
@ -1,31 +0,0 @@
|
|||||||
import { Vec3 } from 'vec3';
|
|
||||||
|
|
||||||
declare module 'vec3' {
|
|
||||||
export interface Vec3 {
|
|
||||||
setXZ(x: number, z: number): Vec3;
|
|
||||||
setXY(x: number, y: number): Vec3;
|
|
||||||
setYZ(y: number, z: number): Vec3;
|
|
||||||
updateXZ(other: Vec3): Vec3;
|
|
||||||
xzNorm(): number;
|
|
||||||
dotXZ(other: Vec3): number;
|
|
||||||
crossXZ(other: Vec3): number;
|
|
||||||
offsetXZ(x: number, z: number): Vec3;
|
|
||||||
centralizeXZ(): Vec3;
|
|
||||||
updateXY(other: Vec3): Vec3;
|
|
||||||
updateYZ(other: Vec3): Vec3;
|
|
||||||
xyNorm(): number;
|
|
||||||
yzNorm(): number;
|
|
||||||
dotXY(other: Vec3): number;
|
|
||||||
dotYZ(other: Vec3): number;
|
|
||||||
crossXY(other: Vec3): number;
|
|
||||||
crossYZ(other: Vec3): number;
|
|
||||||
offsetXY(x: number, y: number): Vec3;
|
|
||||||
offsetYZ(y: number, z: number): Vec3;
|
|
||||||
centralizeXY(): Vec3;
|
|
||||||
centralizeYZ(): Vec3;
|
|
||||||
centralize(): Vec3;
|
|
||||||
xzNormSquared(): number;
|
|
||||||
xyNormSquared(): number;
|
|
||||||
yzNormSquared(): number;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
import { Vec3 } from 'vec3';
|
|
||||||
|
|
||||||
Vec3.prototype.setXZ = function setXZ(x, z) {
|
|
||||||
this.x = x;
|
|
||||||
this.z = z;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.setXY = function setXY(x, y) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.setYZ = function setYZ(y, z) {
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.updateXZ = function updateXZ(other) {
|
|
||||||
this.x = other.x;
|
|
||||||
this.z = other.z;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.xzNorm = function xzNorm() {
|
|
||||||
return Math.sqrt((this.x * this.x) + (this.z * this.z));
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.dotXZ = function dotXZ(other) {
|
|
||||||
return (this.x * other.x) + (this.y * other.y);
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.crossXZ = function crossXZ(other) {
|
|
||||||
return (this.x * other.z) - (this.z * other.x);
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.offsetXZ = function offsetXZ(x, z) {
|
|
||||||
this.x += x;
|
|
||||||
this.z += z;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.centralizeXZ = function centralizeXZ() {
|
|
||||||
this.x = Math.floor(this.x) + .5;
|
|
||||||
this.z = Math.floor(this.z) + .5;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.updateXY = function updateXY(other) {
|
|
||||||
this.x = other.x;
|
|
||||||
this.y = other.y;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.updateYZ = function updateYZ(other) {
|
|
||||||
this.y = other.y;
|
|
||||||
this.z = other.z;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.xyNorm = function xyNorm() {
|
|
||||||
return Math.sqrt((this.x * this.x) + (this.y * this.y));
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.yzNorm = function yzNorm() {
|
|
||||||
return Math.sqrt((this.y * this.y) + (this.z * this.z));
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.dotXY = function dotXY(other) {
|
|
||||||
return (this.x * other.x) + (this.y * other.y);
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.dotYZ = function dotYZ(other) {
|
|
||||||
return (this.y * other.y) + (this.z * other.z);
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.crossXY = function crossXY(other) {
|
|
||||||
return (this.x * other.y) - (this.y * other.x);
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.crossYZ = function crossYZ(other) {
|
|
||||||
return (this.y * other.z) - (this.z * other.y);
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.offsetXY = function offsetXY(x, y) {
|
|
||||||
this.x += x;
|
|
||||||
this.y += y;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.offsetYZ = function offsetYZ(y, z) {
|
|
||||||
this.y += y;
|
|
||||||
this.z += z;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.centralizeXY = function centralizeXY() {
|
|
||||||
this.x = Math.floor(this.x) + .5;
|
|
||||||
this.y = Math.floor(this.y) + .5;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.centralizeYZ = function centralizeYZ() {
|
|
||||||
this.y = Math.floor(this.y) + .5;
|
|
||||||
this.z = Math.floor(this.z) + .5;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.centralize = function centralize() {
|
|
||||||
this.x = Math.floor(this.x) + .5;
|
|
||||||
this.y = Math.floor(this.y) + .5;
|
|
||||||
this.z = Math.floor(this.z) + .5;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.xzNormSquared = function xzNormSquared() {
|
|
||||||
return this.x * this.x + this.z * this.z;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.xyNormSquared = function xyNormSquared() {
|
|
||||||
return this.x * this.x + this.y * this.y;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec3.prototype.yzNormSquared = function yzNormSquared() {
|
|
||||||
return this.y * this.y + this.z * this.z;
|
|
||||||
};
|
|
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "enhanced-vec3",
|
|
||||||
"description": "More methods for vec3",
|
|
||||||
"type": "module",
|
|
||||||
"main": "index.mjs",
|
|
||||||
"types": "index.d.ts",
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "^4.3.4",
|
|
||||||
"vec3": "^0.1.8",
|
|
||||||
"compass-utils": "file:../../utils"
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ import mineflayer from 'mineflayer';
|
|||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
import { asyncSleep, parseLogin, waitEvent } from 'compass-utils';
|
import { asyncSleep, parseLogin, waitEvent } from 'compass-utils';
|
||||||
import repl from 'node:repl';
|
import repl from 'node:repl';
|
||||||
import 'enhanced-vec3';
|
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
"mineflayer-control": "file:plugin/control",
|
"mineflayer-control": "file:plugin/control",
|
||||||
"mineflayer-event-promise": "file:plugin/event-promise",
|
"mineflayer-event-promise": "file:plugin/event-promise",
|
||||||
"mineflayer-fly-control": "file:plugin/fly-control",
|
"mineflayer-fly-control": "file:plugin/fly-control",
|
||||||
"enhanced-vec3": "file:enhanced-vec3",
|
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import { Queue, Task, isIterable } from 'compass-utils';
|
import { Queue, Task, isIterable } from 'compass-utils';
|
||||||
import { Vec3 } from 'vec3';
|
import { Vec3 } from 'vec3';
|
||||||
import 'enhanced-vec3';
|
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
const logger = debug('mineflayer-control');
|
const logger = debug('mineflayer-control');
|
||||||
|
|
||||||
@ -81,6 +80,15 @@ export class ControlState {
|
|||||||
|
|
||||||
ControlState.CONTROLS = ['forward', 'back', 'left', 'right', 'jump', 'sprint', 'sneak'];
|
ControlState.CONTROLS = ['forward', 'back', 'left', 'right', 'jump', 'sprint', 'sneak'];
|
||||||
|
|
||||||
|
function adjust05(x) {
|
||||||
|
return Math.floor(x) + .5;
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustXZ(vec) {
|
||||||
|
vec.x = adjust05(vec.x);
|
||||||
|
vec.z = adjust05(vec.z);
|
||||||
|
}
|
||||||
|
|
||||||
export class MoveInterferedError extends Error {
|
export class MoveInterferedError extends Error {
|
||||||
constructor() { super('Move task has been interfered by an external force.'); }
|
constructor() { super('Move task has been interfered by an external force.'); }
|
||||||
};
|
};
|
||||||
@ -101,10 +109,10 @@ async function moveAxisTask(bot, task, axis_raw, target_raw, level) {
|
|||||||
assert.ok(target_raw instanceof Vec3, 'target');
|
assert.ok(target_raw instanceof Vec3, 'target');
|
||||||
const stable_axis = "xz"[axis % 2];
|
const stable_axis = "xz"[axis % 2];
|
||||||
const target = target_raw.clone();
|
const target = target_raw.clone();
|
||||||
target.centralizeXZ();
|
adjustXZ(target);
|
||||||
|
|
||||||
bot.clearControlStates();
|
bot.clearControlStates();
|
||||||
bot.control.centralizeXZ();
|
bot.control.adjustXZ();
|
||||||
|
|
||||||
let pos = bot.entity.position;
|
let pos = bot.entity.position;
|
||||||
const delta = target.minus(pos);
|
const delta = target.minus(pos);
|
||||||
@ -179,8 +187,10 @@ async function moveAxisTask(bot, task, axis_raw, target_raw, level) {
|
|||||||
remaining_dis = delta.dot(AXIS_UNIT[axis]);
|
remaining_dis = delta.dot(AXIS_UNIT[axis]);
|
||||||
if (Math.abs(remaining_dis) <= 0.5) {
|
if (Math.abs(remaining_dis) <= 0.5) {
|
||||||
logger(`moveAxisTask() very close! remain: ${remaining_dis}.`);
|
logger(`moveAxisTask() very close! remain: ${remaining_dis}.`);
|
||||||
pos.updateXZ(target);
|
pos.x = target.x;
|
||||||
bot.entity.velocity.setXZ(0, 0);
|
pos.z = target.z;
|
||||||
|
bot.entity.velocity.x = 0;
|
||||||
|
bot.entity.velocity.z = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +205,7 @@ async function moveAxisTask(bot, task, axis_raw, target_raw, level) {
|
|||||||
|
|
||||||
async function ladderAscendTask(bot, task, target_y) {
|
async function ladderAscendTask(bot, task, target_y) {
|
||||||
assert.equal(typeof target_y, 'number', 'target_y');
|
assert.equal(typeof target_y, 'number', 'target_y');
|
||||||
bot.control.centralizeXZ();
|
bot.control.adjustXZ();
|
||||||
const start_pos = bot.entity.position.clone();
|
const start_pos = bot.entity.position.clone();
|
||||||
logger(`ladderAscendTask() initial position: ${start_pos}.`);
|
logger(`ladderAscendTask() initial position: ${start_pos}.`);
|
||||||
logger(`ladderAscendTask() target y: ${target_y}.`);
|
logger(`ladderAscendTask() target y: ${target_y}.`);
|
||||||
@ -217,7 +227,7 @@ async function ladderAscendTask(bot, task, target_y) {
|
|||||||
|
|
||||||
const pos = bot.entity.position;
|
const pos = bot.entity.position;
|
||||||
if (pos.xzDistanceTo(start_pos) > 1) { throw new MoveInterferedError(); }
|
if (pos.xzDistanceTo(start_pos) > 1) { throw new MoveInterferedError(); }
|
||||||
bot.control.centralizeXZ();
|
bot.control.adjustXZ();
|
||||||
|
|
||||||
if (Math.abs(pos.y - target_y) < 0.2) {
|
if (Math.abs(pos.y - target_y) < 0.2) {
|
||||||
logger('ladderAscendTask() reached.');
|
logger('ladderAscendTask() reached.');
|
||||||
@ -248,7 +258,7 @@ async function ladderAscendTask(bot, task, target_y) {
|
|||||||
export default function inject(bot) {
|
export default function inject(bot) {
|
||||||
bot.control = {};
|
bot.control = {};
|
||||||
bot.control.getState = () => { return ControlState.from(bot.controlState); };
|
bot.control.getState = () => { return ControlState.from(bot.controlState); };
|
||||||
bot.control.centralizeXZ = () => { bot.entity.position.centralizeXZ(); };
|
bot.control.adjustXZ = () => { adjustXZ(bot.entity.position); };
|
||||||
|
|
||||||
bot.control.moveAxis = (axis, target, level = MOVE_LEVEL.SPRINT) => {
|
bot.control.moveAxis = (axis, target, level = MOVE_LEVEL.SPRINT) => {
|
||||||
let task = new Task();
|
let task = new Task();
|
||||||
@ -272,7 +282,7 @@ export default function inject(bot) {
|
|||||||
if (!bot.entity.onGround) {
|
if (!bot.entity.onGround) {
|
||||||
throw new NotOnGroundError();
|
throw new NotOnGroundError();
|
||||||
}
|
}
|
||||||
bot.control.centralizeXZ();
|
bot.control.adjustXZ();
|
||||||
await bot.look(axis * Math.PI / 2, 0, true);
|
await bot.look(axis * Math.PI / 2, 0, true);
|
||||||
let controls = new ControlState('forward', 'jump');
|
let controls = new ControlState('forward', 'jump');
|
||||||
let pos = bot.entity.position;
|
let pos = bot.entity.position;
|
||||||
@ -280,7 +290,8 @@ export default function inject(bot) {
|
|||||||
await bot.waitForTicks(time);
|
await bot.waitForTicks(time);
|
||||||
bot.clearControlStates();
|
bot.clearControlStates();
|
||||||
bot.entity.position.update(pos.plus(AXIS_UNIT[axis]).offset(0, 1, 0));
|
bot.entity.position.update(pos.plus(AXIS_UNIT[axis]).offset(0, 1, 0));
|
||||||
bot.entity.velocity.setXZ(0, 0);
|
bot.entity.velocity.x = 0;
|
||||||
|
bot.entity.velocity.z = 0;
|
||||||
await bot.waitForTicks(1);
|
await bot.waitForTicks(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -298,7 +309,7 @@ export default function inject(bot) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const axis = AXIS[axis_raw];
|
const axis = AXIS[axis_raw];
|
||||||
bot.control.centralizeXZ();
|
bot.control.adjustXZ();
|
||||||
let target = bot.entity.position.plus(AXIS_UNIT[axis].scaled(dis));
|
let target = bot.entity.position.plus(AXIS_UNIT[axis].scaled(dis));
|
||||||
logger(`jumpForward() axis: ${"zx"[axis % 2]}`);
|
logger(`jumpForward() axis: ${"zx"[axis % 2]}`);
|
||||||
logger(`jumpForward() target: ${target}`);
|
logger(`jumpForward() target: ${target}`);
|
||||||
@ -323,8 +334,10 @@ export default function inject(bot) {
|
|||||||
if (pos.distanceTo(target) > 1) {
|
if (pos.distanceTo(target) > 1) {
|
||||||
throw new MoveInterferedError();
|
throw new MoveInterferedError();
|
||||||
}
|
}
|
||||||
pos.updateXZ(target);
|
pos.x = target.x;
|
||||||
bot.entity.velocity.setXZ(0, 0);
|
pos.z = target.z;
|
||||||
|
bot.entity.velocity.x = 0;
|
||||||
|
bot.entity.velocity.z = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bot.control.jump = async () => {
|
bot.control.jump = async () => {
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"vec3": "^0.1.8",
|
"vec3": "^0.1.8",
|
||||||
"compass-utils": "file:../../utils",
|
"compass-utils": "file:../../utils"
|
||||||
"enhanced-vec3": "file:../../enhanced-vec3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { Task } from 'compass-utils';
|
|
||||||
import { Vec3 } from 'vec3';
|
|
||||||
import 'enhanced-vec3';
|
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
|
import { Task } from 'compass-utils';
|
||||||
const logger = debug('mineflayer-fly-control');
|
const logger = debug('mineflayer-fly-control');
|
||||||
|
|
||||||
export class ElytraNotEquippedError extends Error {
|
export class ElytraNotEquippedError extends Error {
|
||||||
@ -21,10 +19,6 @@ export class AlreadyElytraFlyingError extends Error {
|
|||||||
constructor() { super('Already elytra flying!'); }
|
constructor() { super('Already elytra flying!'); }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FlyInterferedError extends Error {
|
|
||||||
constructor() { super('Fly task has been interfered by an external force.'); }
|
|
||||||
};
|
|
||||||
|
|
||||||
export function fireworkFlight(firework_item) {
|
export function fireworkFlight(firework_item) {
|
||||||
return firework_item?.nbt?.value?.Fireworks?.value?.Flight?.value ?? 1;
|
return firework_item?.nbt?.value?.Fireworks?.value?.Flight?.value ?? 1;
|
||||||
}
|
}
|
||||||
@ -40,146 +34,9 @@ async function takeOffTask(bot, task) {
|
|||||||
task._interuptableHere();
|
task._interuptableHere();
|
||||||
}
|
}
|
||||||
|
|
||||||
function potentialHeightRequired(xz_distance) {
|
async function ascendTask(bot, task, flight, gracefulMode) {
|
||||||
return xz_distance / 9.1 + 16;
|
assert.ok(typeof flight == 'number');
|
||||||
}
|
bot.control.adjustXZ();
|
||||||
|
|
||||||
function yawOfXZ(pos, target) {
|
|
||||||
const delta = target.minus(pos);
|
|
||||||
return Math.atan2(-delta.x, -delta.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function cruiseTask(bot, task, target, tlimit) {
|
|
||||||
bot.control.centralizeXZ();
|
|
||||||
let start_pos = bot.entity.position.clone();
|
|
||||||
let distance_left = target.xzDistanceTo(start_pos);
|
|
||||||
let dy_requirement = potentialHeightRequired(distance_left);
|
|
||||||
let delta = target.minus(start_pos);
|
|
||||||
logger(`cruiseTask() start position=${start_pos}`);
|
|
||||||
logger(`cruiseTask() target position=${target} delta=${delta}`);
|
|
||||||
logger(`cruiseTask() dy_requirement=${dy_requirement}`);
|
|
||||||
if (-delta.y < dy_requirement) {
|
|
||||||
logger(`cruiseTask() height is insufficient: another ${dy_requirement + delta.y} meters required`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let delta_norm_squared;
|
|
||||||
async function updateRoute() {
|
|
||||||
start_pos.update(bot.entity.position);
|
|
||||||
delta = target.minus(start_pos);
|
|
||||||
delta_norm_squared = delta.xzNormSquared();
|
|
||||||
await bot.look(yawOfXZ(start_pos, target), 0, true);
|
|
||||||
task._interuptableHere();
|
|
||||||
}
|
|
||||||
await updateRoute();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
await bot.waitForTicks(1);
|
|
||||||
task._interuptableHere();
|
|
||||||
|
|
||||||
if (bot.entity.onGround || !bot.entity.elytraFlying) {
|
|
||||||
logger('cruiseTask() no longer flying!');
|
|
||||||
throw new FlyInterferedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
const pos = bot.entity.position.clone();
|
|
||||||
const dpos = pos.minus(start_pos);
|
|
||||||
const progress = dpos.dotXZ(delta) / delta_norm_squared;
|
|
||||||
const intended_pos = start_pos.plus(delta.scaled(progress));
|
|
||||||
const error_offset = intended_pos.xzDistanceTo(pos);
|
|
||||||
if (progress < 1 && error_offset > 6) {
|
|
||||||
logger(`cruiseTask() departured from expected route, error offset=${error_offset}`);
|
|
||||||
logger(`cruiseTask() progress=${progress} expected position=${intended_pos}`);
|
|
||||||
logger(`cruiseTask() current position=${pos} to start delta=${dpos}`);
|
|
||||||
await updateRoute();
|
|
||||||
logger(`cruiseTask() corrocted fly route.`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progress >= 1) {
|
|
||||||
const error_dis = pos.xzDistanceTo(target);
|
|
||||||
const velocity = bot.entity.velocity, hspeed = velocity.xzNorm();
|
|
||||||
if (error_dis < tlimit && hspeed < .5) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
logger(`cruiseTask() goal reached, adjusting. position=${pos}`);
|
|
||||||
logger(`cruiseTask() to target delta=${target.minus(pos)} distance=${error_dis}`);
|
|
||||||
logger(`cruiseTask() velocity=${velocity} hspeed=${hspeed}`);
|
|
||||||
await updateRoute();
|
|
||||||
logger(`cruiseTask() replanned. facing: ${bot.entity.yaw}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error_offset < tlimit) {
|
|
||||||
pos.updateXZ(intended_pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const final_pos = bot.entity.position;
|
|
||||||
await bot.look(yawOfXZ(final_pos, target), 0, true);
|
|
||||||
final_pos.updateXZ(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function gracefulLandTask(bot, task, target_y, fall_height) {
|
|
||||||
assert.ok(typeof target_y == 'number');
|
|
||||||
assert.ok(typeof fall_height == 'number');
|
|
||||||
bot.control.centralizeXZ();
|
|
||||||
const start_pos = bot.entity.position.clone();
|
|
||||||
logger(`gracefulLandTask() starting position=${start_pos}`);
|
|
||||||
await bot.look(0, Math.PI / 6);
|
|
||||||
task._interuptableHere();
|
|
||||||
bot.entity.velocity.setXZ(0, 0);
|
|
||||||
|
|
||||||
logger(`gracefulLandTask() secondary position=${start_pos}`);
|
|
||||||
bot.entity.position.updateXZ(start_pos);
|
|
||||||
|
|
||||||
logger(`gracefulLandTask() secondary adjusted position=${start_pos}`);
|
|
||||||
logger(`gracefulLandTask() secondary velocity=${bot.entity.velocity}`);
|
|
||||||
const fall_y = fall_height + target_y;
|
|
||||||
for (let phase = 1; !bot.entity.onGround && bot.entity.position.y > fall_y; phase = (phase + 1) % 3) {
|
|
||||||
await bot.look(phase * Math.PI / 1.5, Math.PI / 6);
|
|
||||||
task._interuptableHere();
|
|
||||||
const pos = bot.entity.position;
|
|
||||||
if (pos.xzDistanceTo(start_pos) > 3) {
|
|
||||||
throw new FlyInterferedError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ff_pos = bot.entity.position;
|
|
||||||
logger(`gracefulLandTask() fall stage initial position=${ff_pos}`);
|
|
||||||
let look_promise = bot.look(yawOfXZ(ff_pos, start_pos), Math.PI / 6, true);
|
|
||||||
await bot.unequip('torso');
|
|
||||||
task._interuptableHere();
|
|
||||||
|
|
||||||
bot.entity.velocity.setXZ(0, 0);
|
|
||||||
ff_pos.updateXZ(start_pos);
|
|
||||||
logger(`gracefulLandTask() fall stage adjusted position=${ff_pos}`);
|
|
||||||
|
|
||||||
while (!bot.entity.onGround && bot.entity.position.y > target_y) {
|
|
||||||
if (look_promise == null) {
|
|
||||||
await bot.waitForTicks(1);
|
|
||||||
} else {
|
|
||||||
await look_promise;
|
|
||||||
look_promise = null;
|
|
||||||
}
|
|
||||||
task._interuptableHere();
|
|
||||||
|
|
||||||
const pos = bot.entity.position;
|
|
||||||
const t = new Vec3();
|
|
||||||
if (pos.xzDistanceTo(start_pos) > 1) {
|
|
||||||
throw new FlyInterferedError();
|
|
||||||
}
|
|
||||||
bot.control.centralizeXZ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bot.entity.velocity.setXZ(0, 0);
|
|
||||||
logger(`gracefulLandTask() finish position=${bot.entity.position}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ascendTask(bot, task, target_y, gracefulMode) {
|
|
||||||
assert.ok(typeof target_y == 'number');
|
|
||||||
if (target_y <= bot.entity.position.y) { return 0; }
|
|
||||||
|
|
||||||
bot.control.centralizeXZ();
|
|
||||||
await bot.look(0, Math.PI / 2, true);
|
await bot.look(0, Math.PI / 2, true);
|
||||||
task._interuptableHere();
|
task._interuptableHere();
|
||||||
|
|
||||||
@ -192,30 +49,17 @@ async function ascendTask(bot, task, target_y, gracefulMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fastModePredicate() {
|
function fastModePredicate() {
|
||||||
return bot.fireworkRocketDuration > 0 || bot.entity.velocity.y > 27;
|
return bot.fireworkRocketDuration > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const predicate = gracefulMode ? gracefulModePredicate : fastModePredicate;
|
const predicate = gracefulMode ? gracefulModePredicate : fastModePredicate;
|
||||||
|
|
||||||
const firework_id = bot.registry.itemsByName.firework_rocket.id;
|
let flight_pre_rocket = fireworkFlight(bot.heldItem);
|
||||||
while (bot.entity.position.y < target_y) {
|
for (let i = 0; i < flight; i += flight_pre_rocket) {
|
||||||
if (bot.heldItem?.type != firework_id) {
|
|
||||||
try {
|
|
||||||
await bot.equip(firework_id);
|
|
||||||
} catch (err) {
|
|
||||||
logger(`ascendTask(): equip rocket error: ${err}.`);
|
|
||||||
throw new InsufficientRocketError();
|
|
||||||
}
|
|
||||||
task._interuptableHere();
|
|
||||||
}
|
|
||||||
bot.activateItem();
|
bot.activateItem();
|
||||||
do {
|
do {
|
||||||
await bot.waitForTicks(1);
|
await bot.waitForTicks(Math.max(1, bot.fireworkRocketDuration));
|
||||||
task._interuptableHere();
|
task._interuptableHere();
|
||||||
|
|
||||||
if (bot.entity.onGround || !bot.entity.elytraFlying) {
|
|
||||||
throw new FlyInterferedError();
|
|
||||||
}
|
|
||||||
} while (predicate());
|
} while (predicate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,20 +71,38 @@ export default function inject(bot) {
|
|||||||
|
|
||||||
bot.flyctl = {};
|
bot.flyctl = {};
|
||||||
bot.flyctl.skipValidation = false;
|
bot.flyctl.skipValidation = false;
|
||||||
function beforeFlightValidation() {
|
function beforeFlyValidation(flight_requirement = 0) {
|
||||||
|
assert.ok(typeof flight_requirement == 'number');
|
||||||
|
assert.ok(flight_requirement >= 0);
|
||||||
|
|
||||||
if (bot.flyctl.skipValidation) {
|
if (bot.flyctl.skipValidation) {
|
||||||
logger('beforeFlightValidation() skipped.');
|
logger('beforeFlyValidation() skipped.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger(`beforeFlyValidation() elytra slot: ${elytra_slot}`);
|
||||||
let elytra_slot_item = bot.inventory.slots[elytra_slot]?.type;
|
let elytra_slot_item = bot.inventory.slots[elytra_slot]?.type;
|
||||||
if (elytra_slot_item != elytra_id) {
|
if (elytra_slot_item != elytra_id) {
|
||||||
logger(`beforeFlightValidation() failed: elytra slot found ${elytra_slot_item}.`);
|
logger(`beforeFlyValidation() failed: elytra slot found ${elytra_slot_item}.`);
|
||||||
logger(`beforeFlightValidation() expected ${elytra_id}.`);
|
logger(`beforeFlyValidation() expected ${elytra_id}.`);
|
||||||
throw new ElytraNotEquippedError();
|
throw new ElytraNotEquippedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
logger('beforeFlightValidation() passed.');
|
if (flight_requirement > 0) {
|
||||||
|
let rocket_item = bot.heldItem;
|
||||||
|
if (rocket_item?.type != firework_id) {
|
||||||
|
logger('beforeFlyValidation() failed: holding is not rocket.');
|
||||||
|
logger(`beforeFlyValidation() found ${rocket_item?.type} expected ${firework_id} .`);
|
||||||
|
throw new InsufficientRocketError(flight_requirement, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let flight_sum = rocket_item.count * fireworkFlight(rocket_item);
|
||||||
|
if (flight_sum < flight_requirement) {
|
||||||
|
throw new InsufficientRocketError(flight_requirement, flight_sum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger('beforeFlyValidation() passed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
bot.flyctl.prepare = async () => {
|
bot.flyctl.prepare = async () => {
|
||||||
@ -248,13 +110,12 @@ export default function inject(bot) {
|
|||||||
await bot.equip(firework_id);
|
await bot.equip(firework_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
bot.flyctl.ascend = (target_y, gracefulMode = true) => {
|
bot.flyctl.ascend = async (flight = 1, gracefulMode = true) => {
|
||||||
let task = new Task();
|
let task = new Task();
|
||||||
queueMicrotask(async () => {
|
queueMicrotask(async () => {
|
||||||
try {
|
try {
|
||||||
task._start();
|
beforeFlyValidation(flight);
|
||||||
beforeFlightValidation();
|
task._ready(await ascendTask(bot, task, flight, gracefulMode));
|
||||||
task._ready(await ascendTask(bot, task, target_y, gracefulMode));
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
task._fail(err);
|
task._fail(err);
|
||||||
}
|
}
|
||||||
@ -262,83 +123,19 @@ export default function inject(bot) {
|
|||||||
return task;
|
return task;
|
||||||
};
|
};
|
||||||
|
|
||||||
bot.flyctl.gracefulAscend = (target_y) => {
|
bot.flyctl.gracefulAscend = (flight = 1) => {
|
||||||
return bot.flyctl.ascend(target_y, true);
|
return bot.flyctl.ascend(flight, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
bot.flyctl.fastAscend = (target_y) => {
|
bot.flyctl.fastAscend = (flight = 1) => {
|
||||||
return bot.flyctl.ascend(target_y, false);
|
return bot.flyctl.ascend(flight, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
bot.flyctl.gracefulLand = (target_y, fall_height=4) => {
|
|
||||||
let task = new Task();
|
|
||||||
queueMicrotask(async () => {
|
|
||||||
try {
|
|
||||||
task._start();
|
|
||||||
await gracefulLandTask(bot, task, target_y, fall_height);
|
|
||||||
await bot.equip(elytra_id, 'torso');
|
|
||||||
task._ready();
|
|
||||||
} catch(err) {
|
|
||||||
await bot.equip(elytra_id, 'torso');
|
|
||||||
task._fail(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return task;
|
|
||||||
};
|
|
||||||
|
|
||||||
bot.flyctl.cruise = (target_raw, teleport_limit=2.5) => {
|
|
||||||
let task = new Task();
|
|
||||||
queueMicrotask(async () => {
|
|
||||||
try {
|
|
||||||
task._start();
|
|
||||||
assert.ok(target_raw instanceof Vec3);
|
|
||||||
const target = target_raw.clone();
|
|
||||||
target.centralizeXZ();
|
|
||||||
task._ready(await cruiseTask(bot, task, target, teleport_limit));
|
|
||||||
} catch(err) {
|
|
||||||
task._fail(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return task;
|
|
||||||
};
|
|
||||||
|
|
||||||
bot.flyctl.autoFlyTo = (target_raw, tactic) => {
|
|
||||||
let task = new Task();
|
|
||||||
queueMicrotask(async () => {
|
|
||||||
tactic = tactic ?? {};
|
|
||||||
tactic.ascend_mode = tactic.ascend_mode ?? 'graceful';
|
|
||||||
tactic.teleport_limit = tactic.teleport_limit ?? 2.5;
|
|
||||||
tactic.fall_height = tactic.teleport_limit ?? 4;
|
|
||||||
|
|
||||||
try {
|
|
||||||
task._start();
|
|
||||||
assert.ok(target_raw instanceof Vec3);
|
|
||||||
const target = target_raw.clone();
|
|
||||||
target.centralizeXZ();
|
|
||||||
beforeFlightValidation();
|
|
||||||
const start_pos = bot.entity.position;
|
|
||||||
|
|
||||||
let dy = potentialHeightRequired(start_pos.xzDistanceTo(target));
|
|
||||||
const graceful_ascend = tactic.ascend_mode === 'graceful';
|
|
||||||
const take_off_y = Math.max(target.y + dy, start_pos.y + 2);
|
|
||||||
await ascendTask(bot, task, take_off_y, graceful_ascend);
|
|
||||||
|
|
||||||
await cruiseTask(bot, task, target, tactic.teleport_limit);
|
|
||||||
await gracefulLandTask(bot, task, target.y, tactic.fall_height);
|
|
||||||
task._ready();
|
|
||||||
} catch(err) {
|
|
||||||
task._fail(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
bot.flyctl.takeOff = () => {
|
bot.flyctl.takeOff = () => {
|
||||||
let task = new Task();
|
let task = new Task();
|
||||||
queueMicrotask(async () => {
|
queueMicrotask(async () => {
|
||||||
try {
|
try {
|
||||||
task._start();
|
beforeFlyValidation(flight);
|
||||||
beforeFlightValidation();
|
|
||||||
task._ready(await takeOffTask(bot, task));
|
task._ready(await takeOffTask(bot, task));
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
task._fail(err);
|
task._fail(err);
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"vec3": "^0.1.8",
|
"vec3": "^0.1.8",
|
||||||
"compass-utils": "file:../../utils",
|
"compass-utils": "file:../../utils"
|
||||||
"enhanced-vec3": "file:../../enhanced-vec3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user