mioku-plugin-mc 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/config.md +157 -3
  2. package/index.ts +38 -2
  3. package/package.json +26 -3
  4. package/play/actions/registry.ts +196 -0
  5. package/play/ai/context-builder.ts +29 -0
  6. package/play/ai/debug-log.ts +135 -0
  7. package/play/ai/main-loop.ts +306 -0
  8. package/play/ai/main-tools.ts +230 -0
  9. package/play/ai/prompt.ts +207 -0
  10. package/play/ai/work-subroutine.ts +487 -0
  11. package/play/behavior/base-behavior.ts +82 -0
  12. package/play/behavior/catalog/approach-player.ts +65 -0
  13. package/play/behavior/catalog/defend.ts +68 -0
  14. package/play/behavior/catalog/explore.ts +72 -0
  15. package/play/behavior/catalog/factory.ts +26 -0
  16. package/play/behavior/catalog/farm-mobs.ts +43 -0
  17. package/play/behavior/catalog/follow.ts +80 -0
  18. package/play/behavior/catalog/gather.ts +135 -0
  19. package/play/behavior/catalog/idle.ts +130 -0
  20. package/play/behavior/catalog/seek-shelter.ts +85 -0
  21. package/play/behavior/engine.ts +243 -0
  22. package/play/behavior/survival/auto-eat.ts +46 -0
  23. package/play/behavior/survival/escape-lava.ts +44 -0
  24. package/play/behavior/survival/escape-water.ts +35 -0
  25. package/play/behavior/survival/flee-creeper.ts +76 -0
  26. package/play/behavior/survival/mlg-fall.ts +56 -0
  27. package/play/bot/bot-controller.ts +276 -0
  28. package/play/bot/play-bus.ts +48 -0
  29. package/play/combat/combat.ts +225 -0
  30. package/play/combat/index.ts +1 -0
  31. package/play/config.ts +39 -0
  32. package/play/context.ts +26 -0
  33. package/play/debug/README.md +74 -0
  34. package/play/debug/commands.ts +289 -0
  35. package/play/index.ts +247 -0
  36. package/play/mineflayer-shims.d.ts +22 -0
  37. package/play/missions/bundles/approach-player.ts +26 -0
  38. package/play/missions/bundles/explore.ts +24 -0
  39. package/play/missions/bundles/farm-mobs.ts +24 -0
  40. package/play/missions/bundles/follow-player.ts +40 -0
  41. package/play/missions/bundles/gather-resource.ts +37 -0
  42. package/play/missions/bundles/idle-wander.ts +24 -0
  43. package/play/missions/bundles/seek-shelter.ts +18 -0
  44. package/play/missions/mission-controller.ts +296 -0
  45. package/play/missions/registry.ts +107 -0
  46. package/play/path-engine/astar.ts +514 -0
  47. package/play/path-engine/goals.ts +84 -0
  48. package/play/path-engine/index.ts +4 -0
  49. package/play/path-engine/movements.ts +67 -0
  50. package/play/path-engine/path-engine.ts +540 -0
  51. package/play/runtime.ts +14 -0
  52. package/play/session.ts +486 -0
  53. package/play/state/cooldowns.ts +40 -0
  54. package/play/state/event-journal.ts +72 -0
  55. package/play/state/memory-bus.ts +115 -0
  56. package/play/state/mode.ts +57 -0
  57. package/play/state/sensors/entity-scanner.ts +275 -0
  58. package/play/state/snapshot.ts +360 -0
  59. package/play/types.ts +284 -0
  60. package/play/util/async.ts +11 -0
  61. package/play/util/endpoint.ts +38 -0
  62. package/play/util/entities.ts +135 -0
  63. package/play/util/inventory.ts +75 -0
  64. package/skills.ts +63 -0
@@ -0,0 +1,514 @@
1
+ import type { Bot } from "mineflayer";
2
+ import { Vec3 } from "vec3";
3
+ import { getBlock, isAir, isSolid, type MovementsConfig } from "./movements";
4
+ import type { PathGoal } from "./goals";
5
+
6
+ export interface AStarNode {
7
+ x: number;
8
+ y: number;
9
+ z: number;
10
+ g: number;
11
+ f: number;
12
+ estimatedCostToGoal: number;
13
+ previous: AStarNode | null;
14
+ jump: boolean;
15
+ toBreak: { x: number; y: number; z: number; stringId?: string }[];
16
+ toPlace: { x: number; y: number; z: number; dx: number; dy: number; dz: number; sneak?: boolean }[];
17
+ ascend?: boolean;
18
+ descend?: boolean;
19
+ parkour?: boolean;
20
+ pillar?: boolean;
21
+ diagonal?: boolean;
22
+ digTime?: number;
23
+ }
24
+
25
+ export type AStarResult =
26
+ | { status: "success"; nodes: AStarNode[] }
27
+ | { status: "noPath"; nodes: AStarNode[] }
28
+ | { status: "partial"; nodes: AStarNode[] }
29
+ | { status: "timeout"; nodes: AStarNode[] };
30
+
31
+ const COST_INF = 1_000_000;
32
+ const MAX_NODES = 1200;
33
+ const PARTIAL_MIN_DIST = 4;
34
+
35
+ const WALK_ONE_BLOCK_COST = 20;
36
+ const SPRINT_ONE_BLOCK_COST = 10;
37
+ const SNEAK_ONE_BLOCK_COST = 40;
38
+ const WALK_OFF_BLOCK_COST = 4;
39
+ const CENTER_AFTER_FALL_COST = 5;
40
+ const SOUL_SAND_WALK_RATIO = 0.4;
41
+ const PLACE_BLOCK_COST = 5;
42
+ const JUMP_PENALTY = 10;
43
+ const PLACE_BUCKET_COST = 100;
44
+ const PARKOUR_JUMP_PENALTY = 15;
45
+ const SPRINT_MULTIPLIER = 0.5;
46
+ const PARKOUR_MAX_DIST_SPRINT = 4;
47
+ const PARKOUR_MAX_DIST_WALK = 3;
48
+ const MAX_FALL_NO_WATER = 3;
49
+ const MAX_FALL_BUCKET = 11;
50
+ const LADDER_DOWN_COST = 5;
51
+ const MAX_DROP = 4;
52
+
53
+ function key(x: number, y: number, z: number): string {
54
+ return `${x},${y},${z}`;
55
+ }
56
+
57
+ function blockAt(bot: Bot, x: number, y: number, z: number): any {
58
+ try {
59
+ return bot.blockAt(new Vec3(x, y, z), false) as any;
60
+ } catch {
61
+ return null;
62
+ }
63
+ }
64
+
65
+ function isPassable(b: any): boolean {
66
+ if (!b) return true;
67
+ return b.boundingBox === "empty" && !b.fluid;
68
+ }
69
+
70
+ function isWalkable(b: any): boolean {
71
+ if (!b) return false;
72
+ if (b.boundingBox !== "block") return false;
73
+ if (b.diggable && b.material === "plant" && b.boundingBox === "empty") return true;
74
+ return true;
75
+ }
76
+
77
+ function isClimbable(b: any): boolean {
78
+ if (!b) return false;
79
+ return b.name === "ladder" || b.name === "vine";
80
+ }
81
+
82
+ function isWater(b: any): boolean {
83
+ return !!b && (b.name === "water" || b.name === "flowing_water");
84
+ }
85
+
86
+ function isLava(b: any): boolean {
87
+ return !!b && (b.name === "lava" || b.name === "flowing_lava");
88
+ }
89
+
90
+ function isLiquid(b: any): boolean {
91
+ return isWater(b) || isLava(b);
92
+ }
93
+
94
+ function isFalling(b: any): boolean {
95
+ if (!b) return false;
96
+ return b.name === "sand" || b.name === "gravel" || b.name === "red_sand";
97
+ }
98
+
99
+ function canPlaceAgainst(bot: Bot, x: number, y: number, z: number): boolean {
100
+ const b = blockAt(bot, x, y, z);
101
+ if (!b) return false;
102
+ if (b.boundingBox === "block" || isClimbable(b)) return true;
103
+ return false;
104
+ }
105
+
106
+ function isReplaceable(b: any): boolean {
107
+ if (!b) return true;
108
+ return b.boundingBox === "empty";
109
+ }
110
+
111
+ function estimateDigSeconds(bot: Bot, x: number, y: number, z: number): number {
112
+ const b = blockAt(bot, x, y, z);
113
+ if (!b) return 0;
114
+ if (isPassable(b)) return 0;
115
+ if (!b.diggable) return COST_INF;
116
+ try {
117
+ const held = (bot.heldItem as any) || null;
118
+ const itemType = held?.type ?? 0;
119
+ const ms = b.digTime(itemType, false, false, false);
120
+ const ticks = Math.max(1, ms / 50);
121
+ return ticks;
122
+ } catch {
123
+ return 20;
124
+ }
125
+ }
126
+
127
+ function chooseBestTool(bot: Bot, block: any): any {
128
+ try {
129
+ const items = bot.inventory?.items?.() ?? [];
130
+ if (items.length === 0) return null;
131
+ let best: any = null;
132
+ let bestSpeed = -1;
133
+ for (const it of items) {
134
+ if (!block.diggable) return null;
135
+ try {
136
+ const ms = block.digTime(it.type, false, false, false);
137
+ if (ms <= 0) continue;
138
+ const speed = 1000 / ms;
139
+ if (speed > bestSpeed) {
140
+ bestSpeed = speed;
141
+ best = it;
142
+ }
143
+ } catch {
144
+ // ignore
145
+ }
146
+ }
147
+ if (best) void bot.equip(best, "hand");
148
+ return best;
149
+ } catch {
150
+ return null;
151
+ }
152
+ }
153
+
154
+ function isAdjacentClear(bot: Bot, x: number, y: number, z: number): boolean {
155
+ return isPassable(blockAt(bot, x, y, z));
156
+ }
157
+
158
+ function safeOvershoot(bot: Bot, x: number, y: number, z: number): boolean {
159
+ const feet = blockAt(bot, x, y, z);
160
+ const head = blockAt(bot, x, y + 1, z);
161
+ const top = blockAt(bot, x, y + 2, z);
162
+ return isPassable(feet) && isPassable(head) && isPassable(top);
163
+ }
164
+
165
+ function tryMoveAscend(
166
+ bot: Bot,
167
+ node: AStarNode,
168
+ dir: { x: number; z: number },
169
+ _m: MovementsConfig,
170
+ ): AStarNode | null {
171
+ const x = node.x + dir.x;
172
+ const z = node.z + dir.z;
173
+ const y = node.y + 1;
174
+ const dest = blockAt(bot, x, y, z);
175
+ const destUp = blockAt(bot, x, y + 1, z);
176
+ const curHead = blockAt(bot, node.x, node.y + 1, node.z);
177
+ const curUp = blockAt(bot, node.x, node.y + 2, node.z);
178
+ if (!isWalkable(blockAt(bot, x, y - 1, z))) return null;
179
+ if (!isPassable(dest) || !isPassable(destUp)) return null;
180
+ if (!isPassable(curHead) || !isPassable(curUp)) return null;
181
+ const d1 = estimateDigSeconds(bot, x, y + 1, z);
182
+ const d2 = estimateDigSeconds(bot, x, y + 2, z);
183
+ if (d1 >= COST_INF || d2 >= COST_INF) return null;
184
+ return {
185
+ x, y, z,
186
+ g: 0, f: 0, estimatedCostToGoal: 0,
187
+ previous: node,
188
+ jump: true,
189
+ toBreak: [],
190
+ toPlace: [],
191
+ ascend: true,
192
+ digTime: d1 + d2,
193
+ };
194
+ }
195
+
196
+ function tryMoveTraverse(
197
+ bot: Bot,
198
+ node: AStarNode,
199
+ dir: { x: number; z: number },
200
+ _m: MovementsConfig,
201
+ ): AStarNode | null {
202
+ const x = node.x + dir.x;
203
+ const z = node.z + dir.z;
204
+ const y = node.y;
205
+ const feet = blockAt(bot, x, y, z);
206
+ const head = blockAt(bot, x, y + 1, z);
207
+ const under = blockAt(bot, x, y - 1, z);
208
+ if (!isPassable(feet) || !isPassable(head)) return null;
209
+ if (!isWalkable(under)) {
210
+ return tryBridge(bot, node, dir, under, _m);
211
+ }
212
+ const d1 = estimateDigSeconds(bot, x, y, z);
213
+ const d2 = estimateDigSeconds(bot, x, y + 1, z);
214
+ if (d1 >= COST_INF || d2 >= COST_INF) return null;
215
+ return {
216
+ x, y, z,
217
+ g: 0, f: 0, estimatedCostToGoal: 0,
218
+ previous: node,
219
+ jump: false,
220
+ toBreak: [],
221
+ toPlace: [],
222
+ digTime: d1 + d2,
223
+ };
224
+ }
225
+
226
+ function tryBridge(
227
+ bot: Bot,
228
+ node: AStarNode,
229
+ dir: { x: number; z: number },
230
+ under: any,
231
+ m: MovementsConfig,
232
+ ): AStarNode | null {
233
+ const x = node.x + dir.x;
234
+ const z = node.z + dir.z;
235
+ const y = node.y;
236
+ if (!isReplaceable(under)) return null;
237
+ if (isClimbable(under)) return null;
238
+ if (!canPlaceAgainst(bot, x, y - 1, z - dir.z) && !canPlaceAgainst(bot, x, y - 1, z + dir.x) && !canPlaceAgainst(bot, x - dir.x, y - 1, z) && !canPlaceAgainst(bot, x + dir.x, y - 1, z) && !canPlaceAgainst(bot, x, y - 2, z)) return null;
239
+ return {
240
+ x, y, z,
241
+ g: 0, f: 0, estimatedCostToGoal: 0,
242
+ previous: node,
243
+ jump: false,
244
+ toBreak: [],
245
+ toPlace: [{ x, y: y - 1, z, dx: 0, dy: 1, dz: 0, sneak: true }],
246
+ digTime: 0,
247
+ };
248
+ }
249
+
250
+ function tryMoveDescend(
251
+ bot: Bot,
252
+ node: AStarNode,
253
+ dir: { x: number; z: number },
254
+ _m: MovementsConfig,
255
+ ): AStarNode | null {
256
+ const x = node.x + dir.x;
257
+ const z = node.z + dir.z;
258
+ const y = node.y - 1;
259
+ const feet = blockAt(bot, x, y, z);
260
+ const head = blockAt(bot, x, y + 1, z);
261
+ if (!isPassable(feet) || !isPassable(head)) return null;
262
+ if (!isWalkable(blockAt(bot, x, y - 1, z))) {
263
+ for (let h = 2; h <= MAX_DROP; h++) {
264
+ const below = blockAt(bot, x, y - h, z);
265
+ if (isWalkable(below)) {
266
+ if (h > MAX_FALL_NO_WATER) return null;
267
+ return {
268
+ x, y: y - h + 1, z,
269
+ g: 0, f: 0, estimatedCostToGoal: 0,
270
+ previous: node,
271
+ jump: false,
272
+ toBreak: [],
273
+ toPlace: [],
274
+ descend: true,
275
+ digTime: 0,
276
+ };
277
+ }
278
+ }
279
+ return null;
280
+ }
281
+ return {
282
+ x, y, z,
283
+ g: 0, f: 0, estimatedCostToGoal: 0,
284
+ previous: node,
285
+ jump: false,
286
+ toBreak: [],
287
+ toPlace: [],
288
+ descend: true,
289
+ digTime: 0,
290
+ };
291
+ }
292
+
293
+ function tryMoveParkour(
294
+ bot: Bot,
295
+ node: AStarNode,
296
+ dir: { x: number; z: number },
297
+ m: MovementsConfig,
298
+ ): AStarNode | null {
299
+ if (!m.allowParkour) return null;
300
+ const x = node.x + dir.x;
301
+ const z = node.z + dir.z;
302
+ const y = node.y;
303
+ if (!isAdjacentClear(bot, x, y, z)) return null;
304
+ const adj = blockAt(bot, x, y - 1, z);
305
+ if (isWalkable(adj)) return null;
306
+ if (!isAdjacentClear(bot, x, y + 1, z) || !isAdjacentClear(bot, x, y + 2, z)) return null;
307
+ if (!isAdjacentClear(bot, node.x, node.y + 2, node.z)) return null;
308
+ const standing = blockAt(bot, node.x, node.y - 1, node.z);
309
+ if (isClimbable(standing)) return null;
310
+ const maxJump = PARKOUR_MAX_DIST_SPRINT;
311
+ for (let i = 2; i <= maxJump; i++) {
312
+ const destX = node.x + dir.x * i;
313
+ const destZ = node.z + dir.z * i;
314
+ if (!isAdjacentClear(bot, destX, y + 1, destZ)) break;
315
+ if (!isAdjacentClear(bot, destX, y + 2, destZ)) break;
316
+ const destInto = blockAt(bot, destX, y, destZ);
317
+ if (!isPassable(destInto)) break;
318
+ const landing = blockAt(bot, destX, y - 1, destZ);
319
+ if (isWalkable(landing) && landing.name !== "farmland" && safeOvershoot(bot, destX + dir.x, y, destZ + dir.z)) {
320
+ return {
321
+ x: destX, y, z: destZ,
322
+ g: 0, f: 0, estimatedCostToGoal: 0,
323
+ previous: node,
324
+ jump: true,
325
+ toBreak: [],
326
+ toPlace: [],
327
+ parkour: true,
328
+ digTime: 0,
329
+ };
330
+ }
331
+ }
332
+ return null;
333
+ }
334
+
335
+ function tryMovePillar(
336
+ bot: Bot,
337
+ node: AStarNode,
338
+ _dir: { x: number; z: number },
339
+ _m: MovementsConfig,
340
+ ): AStarNode | null {
341
+ const x = node.x;
342
+ const z = node.z;
343
+ const y = node.y + 1;
344
+ const head = blockAt(bot, x, y + 1, z);
345
+ if (!isPassable(head)) return null;
346
+ if (isClimbable(blockAt(bot, x, y - 1, z))) return null;
347
+ return {
348
+ x, y, z,
349
+ g: 0, f: 0, estimatedCostToGoal: 0,
350
+ previous: node,
351
+ jump: true,
352
+ toBreak: [],
353
+ toPlace: [{ x: node.x, y: node.y - 1, z: node.z, dx: 0, dy: 1, dz: 0, sneak: true }],
354
+ pillar: true,
355
+ digTime: 0,
356
+ };
357
+ }
358
+
359
+ function tryMoveDiagonal(
360
+ bot: Bot,
361
+ node: AStarNode,
362
+ dir: { x: number; z: number },
363
+ m: MovementsConfig,
364
+ ): AStarNode | null {
365
+ if (!m.allowParkour) return null;
366
+ const x = node.x + dir.x;
367
+ const z = node.z + dir.z;
368
+ const y = node.y;
369
+ if (!isAdjacentClear(bot, x, y, z) || !isAdjacentClear(bot, x, y + 1, z)) return null;
370
+ if (!isWalkable(blockAt(bot, x, y - 1, z))) return null;
371
+ return {
372
+ x, y, z,
373
+ g: 0, f: 0, estimatedCostToGoal: 0,
374
+ previous: node,
375
+ jump: false,
376
+ toBreak: [],
377
+ toPlace: [],
378
+ diagonal: true,
379
+ digTime: 0,
380
+ };
381
+ }
382
+
383
+ function reconstruct(node: AStarNode): AStarNode[] {
384
+ const path: AStarNode[] = [];
385
+ let n: AStarNode | null = node;
386
+ while (n) {
387
+ path.unshift(n);
388
+ n = n.previous;
389
+ }
390
+ return path;
391
+ }
392
+
393
+ export function astar(
394
+ bot: Bot,
395
+ start: { x: number; y: number; z: number },
396
+ goal: PathGoal,
397
+ movements: MovementsConfig,
398
+ maxNodes = MAX_NODES,
399
+ ): AStarResult {
400
+ const sx = Math.floor(start.x);
401
+ const sy = Math.floor(start.y);
402
+ const sz = Math.floor(start.z);
403
+
404
+ if (goal.isEnd({ x: sx + 0.5, y: sy, z: sz + 0.5 })) {
405
+ return {
406
+ status: "success",
407
+ nodes: [{
408
+ x: sx, y: sy, z: sz,
409
+ g: 0, f: 0, estimatedCostToGoal: 0,
410
+ previous: null, jump: false, toBreak: [], toPlace: [],
411
+ }],
412
+ };
413
+ }
414
+
415
+ const open = new Map<string, AStarNode>();
416
+ const closed = new Set<string>();
417
+
418
+ const startNode: AStarNode = {
419
+ x: sx, y: sy, z: sz,
420
+ g: 0,
421
+ f: goal.heuristic({ x: sx + 0.5, y: sy, z: sz + 0.5 }),
422
+ estimatedCostToGoal: goal.heuristic({ x: sx + 0.5, y: sy, z: sz + 0.5 }),
423
+ previous: null, jump: false, toBreak: [], toPlace: [],
424
+ };
425
+ open.set(key(sx, sy, sz), startNode);
426
+
427
+ let bestPartial: AStarNode | null = startNode;
428
+ let bestPartialDist = 0;
429
+ let nodes = 0;
430
+
431
+ while (open.size > 0 && nodes < maxNodes) {
432
+ let current: AStarNode | null = null;
433
+ let currentKey = "";
434
+ for (const [k, n] of open) {
435
+ if (!current || n.f < current.f) {
436
+ current = n;
437
+ currentKey = k;
438
+ }
439
+ }
440
+ if (!current) break;
441
+
442
+ if (goal.isEnd({ x: current.x + 0.5, y: current.y, z: current.z + 0.5 })) {
443
+ return { status: "success", nodes: reconstruct(current) };
444
+ }
445
+
446
+ open.delete(currentKey);
447
+ closed.add(currentKey);
448
+ nodes++;
449
+
450
+ const distSq =
451
+ (current.x - sx) ** 2 + (current.z - sz) ** 2;
452
+ if (distSq > bestPartialDist) {
453
+ bestPartialDist = distSq;
454
+ bestPartial = current;
455
+ }
456
+
457
+ const neighbors: AStarNode[] = [];
458
+ const tryAdd = (n: AStarNode | null) => {
459
+ if (!n) return;
460
+ const k = key(n.x, n.y, n.z);
461
+ if (closed.has(k)) return;
462
+ const existing = open.get(k);
463
+ const baseCost = current.g + (
464
+ n.jump ? SPRINT_ONE_BLOCK_COST + JUMP_PENALTY
465
+ : n.ascend ? SPRINT_ONE_BLOCK_COST + JUMP_PENALTY
466
+ : n.parkour ? PARKOUR_JUMP_PENALTY + (n.digTime ?? 0)
467
+ : n.pillar ? SPRINT_ONE_BLOCK_COST + JUMP_PENALTY + PLACE_BLOCK_COST
468
+ : n.descend ? WALK_OFF_BLOCK_COST + CENTER_AFTER_FALL_COST
469
+ : n.toPlace.length > 0 ? WALK_ONE_BLOCK_COST + PLACE_BLOCK_COST
470
+ : WALK_ONE_BLOCK_COST
471
+ ) + (n.digTime ?? 0);
472
+ const tentative = baseCost;
473
+ if (existing && tentative >= existing.g) return;
474
+ n.g = tentative;
475
+ n.f = tentative + n.estimatedCostToGoal;
476
+ n.previous = current;
477
+ open.set(k, n);
478
+ neighbors.push(n);
479
+ };
480
+
481
+ tryAdd(tryMoveTraverse(bot, current, { x: 1, z: 0 }, movements));
482
+ tryAdd(tryMoveTraverse(bot, current, { x: -1, z: 0 }, movements));
483
+ tryAdd(tryMoveTraverse(bot, current, { x: 0, z: 1 }, movements));
484
+ tryAdd(tryMoveTraverse(bot, current, { x: 0, z: -1 }, movements));
485
+ tryAdd(tryMoveAscend(bot, current, { x: 1, z: 0 }, movements));
486
+ tryAdd(tryMoveAscend(bot, current, { x: -1, z: 0 }, movements));
487
+ tryAdd(tryMoveAscend(bot, current, { x: 0, z: 1 }, movements));
488
+ tryAdd(tryMoveAscend(bot, current, { x: 0, z: -1 }, movements));
489
+ tryAdd(tryMoveDescend(bot, current, { x: 1, z: 0 }, movements));
490
+ tryAdd(tryMoveDescend(bot, current, { x: -1, z: 0 }, movements));
491
+ tryAdd(tryMoveDescend(bot, current, { x: 0, z: 1 }, movements));
492
+ tryAdd(tryMoveDescend(bot, current, { x: 0, z: -1 }, movements));
493
+ if (movements.allowParkour) {
494
+ tryAdd(tryMoveParkour(bot, current, { x: 1, z: 0 }, movements));
495
+ tryAdd(tryMoveParkour(bot, current, { x: -1, z: 0 }, movements));
496
+ tryAdd(tryMoveParkour(bot, current, { x: 0, z: 1 }, movements));
497
+ tryAdd(tryMoveParkour(bot, current, { x: 0, z: -1 }, movements));
498
+ tryAdd(tryMoveDiagonal(bot, current, { x: 1, z: 1 }, movements));
499
+ tryAdd(tryMoveDiagonal(bot, current, { x: -1, z: -1 }, movements));
500
+ tryAdd(tryMoveDiagonal(bot, current, { x: 1, z: -1 }, movements));
501
+ tryAdd(tryMoveDiagonal(bot, current, { x: -1, z: 1 }, movements));
502
+ }
503
+ if (movements.allow1by1towers) {
504
+ tryAdd(tryMovePillar(bot, current, { x: 0, z: 0 }, movements));
505
+ }
506
+ }
507
+
508
+ if (bestPartial && bestPartialDist >= PARTIAL_MIN_DIST * PARTIAL_MIN_DIST && bestPartial !== startNode) {
509
+ return { status: "partial", nodes: reconstruct(bestPartial) };
510
+ }
511
+ return { status: "noPath", nodes: [] };
512
+ }
513
+
514
+ export { chooseBestTool, estimateDigSeconds };
@@ -0,0 +1,84 @@
1
+ export interface PathGoal {
2
+ isEnd(nodePos: { x: number; y: number; z: number }): boolean;
3
+ isReached(botPos: { x: number; y: number; z: number }): boolean;
4
+ heuristic(nodePos: { x: number; y: number; z: number }): number;
5
+ }
6
+
7
+ function dist3(ax: number, ay: number, az: number, bx: number, by: number, bz: number): number {
8
+ return Math.sqrt((ax - bx) ** 2 + (ay - by) ** 2 + (az - bz) ** 2);
9
+ }
10
+
11
+ function distXZ(ax: number, az: number, bx: number, bz: number): number {
12
+ return Math.sqrt((ax - bx) ** 2 + (az - bz) ** 2);
13
+ }
14
+
15
+ export class GoalFollow implements PathGoal {
16
+ constructor(public entity: any, public distance: number) {}
17
+ isEnd(nodePos: { x: number; y: number; z: number }): boolean {
18
+ if (!this.entity?.position) return false;
19
+ const p = this.entity.position;
20
+ return dist3(nodePos.x + 0.5, nodePos.y, nodePos.z + 0.5, p.x, p.y, p.z) <= this.distance;
21
+ }
22
+ isReached(botPos: { x: number; y: number; z: number }): boolean {
23
+ if (!this.entity?.position) return false;
24
+ const p = this.entity.position;
25
+ return dist3(botPos.x, botPos.y, botPos.z, p.x, p.y, p.z) <= this.distance;
26
+ }
27
+ heuristic(nodePos: { x: number; y: number; z: number }): number {
28
+ if (!this.entity?.position) return Infinity;
29
+ const p = this.entity.position;
30
+ return dist3(nodePos.x + 0.5, nodePos.y, nodePos.z + 0.5, p.x, p.y, p.z);
31
+ }
32
+ }
33
+
34
+ export class GoalXZ implements PathGoal {
35
+ constructor(public x: number, public z: number) {}
36
+ isEnd(nodePos: { x: number; y: number; z: number }): boolean {
37
+ return nodePos.x === this.x && nodePos.z === this.z;
38
+ }
39
+ isReached(botPos: { x: number; y: number; z: number }): boolean {
40
+ return (
41
+ Math.abs(botPos.x - (this.x + 0.5)) <= 0.5 &&
42
+ Math.abs(botPos.z - (this.z + 0.5)) <= 0.5
43
+ );
44
+ }
45
+ heuristic(nodePos: { x: number; y: number; z: number }): number {
46
+ return distXZ(nodePos.x, nodePos.z, this.x, this.z);
47
+ }
48
+ }
49
+
50
+ export class GoalNear implements PathGoal {
51
+ constructor(public x: number, public y: number, public z: number, public range: number) {}
52
+ isEnd(nodePos: { x: number; y: number; z: number }): boolean {
53
+ return dist3(nodePos.x + 0.5, nodePos.y, nodePos.z + 0.5, this.x, this.y, this.z) <= this.range;
54
+ }
55
+ isReached(botPos: { x: number; y: number; z: number }): boolean {
56
+ return dist3(botPos.x, botPos.y, botPos.z, this.x, this.y, this.z) <= this.range;
57
+ }
58
+ heuristic(nodePos: { x: number; y: number; z: number }): number {
59
+ return dist3(nodePos.x + 0.5, nodePos.y, nodePos.z + 0.5, this.x, this.y, this.z);
60
+ }
61
+ }
62
+
63
+ export class GoalGetToBlock implements PathGoal {
64
+ constructor(public x: number, public y: number, public z: number) {}
65
+ isEnd(nodePos: { x: number; y: number; z: number }): boolean {
66
+ const dx = Math.abs(nodePos.x - this.x);
67
+ const dz = Math.abs(nodePos.z - this.z);
68
+ const dy = nodePos.y - this.y;
69
+ return dx <= 1 && dz <= 1 && dy >= -1 && dy <= 2;
70
+ }
71
+ isReached(botPos: { x: number; y: number; z: number }): boolean {
72
+ const cx = this.x + 0.5;
73
+ const cz = this.z + 0.5;
74
+ const dx = Math.abs(botPos.x - cx);
75
+ const dz = Math.abs(botPos.z - cz);
76
+ const dy = botPos.y - this.y;
77
+ return dx <= 1.5 && dz <= 1.5 && dy >= -1 && dy <= 2;
78
+ }
79
+ heuristic(nodePos: { x: number; y: number; z: number }): number {
80
+ const cx = this.x + 0.5;
81
+ const cz = this.z + 0.5;
82
+ return Math.sqrt((nodePos.x + 0.5 - cx) ** 2 + (nodePos.z + 0.5 - cz) ** 2);
83
+ }
84
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./goals";
2
+ export * from "./movements";
3
+ export * from "./astar";
4
+ export * from "./path-engine";
@@ -0,0 +1,67 @@
1
+ import type { Bot } from "mineflayer";
2
+ import { Vec3 } from "vec3";
3
+
4
+ export interface MovementsConfig {
5
+ canDig: boolean;
6
+ allowParkour: boolean;
7
+ allowSprinting: boolean;
8
+ allow1by1towers: boolean;
9
+ maxDropDown: number;
10
+ }
11
+
12
+ export const DEFAULT_MOVEMENTS: MovementsConfig = {
13
+ canDig: true,
14
+ allowParkour: true,
15
+ allowSprinting: true,
16
+ allow1by1towers: true,
17
+ maxDropDown: 4,
18
+ };
19
+
20
+ export interface BlockInfo {
21
+ name: string;
22
+ type: number;
23
+ boundingBox: "block" | "empty";
24
+ diggable: boolean;
25
+ hardness: number;
26
+ physical: boolean;
27
+ shapes: number[][];
28
+ position: { x: number; y: number; z: number };
29
+ }
30
+
31
+ export function getBlock(bot: Bot, x: number, y: number, z: number): BlockInfo | null {
32
+ try {
33
+ const block = bot.blockAt(
34
+ new Vec3(Math.floor(x), Math.floor(y), Math.floor(z)),
35
+ false,
36
+ ) as any;
37
+ if (!block) return null;
38
+ return {
39
+ name: String(block.name ?? ""),
40
+ type: block.type ?? 0,
41
+ boundingBox: block.boundingBox === "block" ? "block" : "empty",
42
+ diggable: !!block.diggable,
43
+ hardness: block.hardness ?? 0,
44
+ physical: !!block.physical,
45
+ shapes: block.shapes ?? [],
46
+ position: { x: block.position.x, y: block.position.y, z: block.position.z },
47
+ };
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ export function getRawBlock(bot: Bot, x: number, y: number, z: number): any | null {
54
+ try {
55
+ return bot.blockAt(new Vec3(Math.floor(x), Math.floor(y), Math.floor(z)), false) as any;
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ export function isSolid(b: BlockInfo | null): boolean {
62
+ return !!b && b.boundingBox === "block";
63
+ }
64
+
65
+ export function isAir(b: BlockInfo | null): boolean {
66
+ return !b || b.boundingBox === "empty";
67
+ }