spectra-physics 1.2.0 → 1.3.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.
@@ -0,0 +1,169 @@
1
+ 'use strict'
2
+ // How the pathfinder sees blocks: where a player can stand (with slabs, stairs and carpets), whether its
3
+ // body fits, water and climbables, what hurts, and what can be dug and how long that takes.
4
+
5
+ const { COST } = require('./costs')
6
+
7
+ const PLAYER_HEIGHT = 1.8
8
+ const STEP = 0.6
9
+ const MAX_FLOOR = 1 // never stand on fences and walls (1.5 high)
10
+
11
+ // entering these hurts, traps or slows the player for a long time
12
+ const DANGER = new Set([
13
+ 'lava', 'fire', 'soul_fire', 'cactus', 'sweet_berry_bush', 'wither_rose', 'campfire', 'soul_campfire',
14
+ 'magma_block', 'powder_snow', 'nether_portal', 'end_portal', 'end_gateway', 'pointed_dripstone'
15
+ ])
16
+ const SLOW = new Set(['cobweb', 'soul_sand', 'honey_block', 'web'])
17
+ const NO_BREAK = new Set([
18
+ 'chest', 'trapped_chest', 'ender_chest', 'barrel', 'spawner', 'trial_spawner', 'vault', 'beacon',
19
+ 'furnace', 'blast_furnace', 'smoker', 'hopper', 'dispenser', 'dropper', 'brewing_stand', 'lectern'
20
+ ])
21
+ const FALLING = /^(sand|red_sand|gravel|suspicious_sand|suspicious_gravel|.*_concrete_powder|anvil|chipped_anvil|damaged_anvil|pointed_dripstone|scaffolding)$/
22
+
23
+ class Terrain {
24
+ constructor (world, options = {}) {
25
+ this.world = world
26
+ this.registry = world.registry
27
+ this.Block = world.Block
28
+ this.options = options
29
+ this.traits = []
30
+ this.digTicks = new Map()
31
+ this.tools = [null]
32
+ this.effects = {}
33
+ }
34
+
35
+ // the inventory and effects at planning time decide which blocks are worth digging
36
+ prepare (bot) {
37
+ this.digTicks.clear()
38
+ const seen = new Set()
39
+ this.tools = [null]
40
+ for (const item of bot.inventory?.items?.() ?? []) {
41
+ if (seen.has(item.type)) continue
42
+ seen.add(item.type)
43
+ this.tools.push(item)
44
+ }
45
+ this.effects = bot.entity?.effects ?? {}
46
+ }
47
+
48
+ trait (stateId) {
49
+ let t = this.traits[stateId]
50
+ if (t) return t
51
+ const info = this.world.info(stateId)
52
+ let top = 0
53
+ for (const s of info.shapes) top = Math.max(top, s[4])
54
+ const block = this.registry.blocksByStateId?.[stateId]
55
+ // ladders and vines are thin panels against the wall: the body fits beside them
56
+ t = {
57
+ name: info.name,
58
+ empty: info.shapes.length === 0 || info.climbable || info.isLadder,
59
+ top,
60
+ water: info.fluid?.type === 'water' && !info.shapes.length,
61
+ waterlogged: info.fluid?.type === 'water',
62
+ lava: info.fluid?.type === 'lava',
63
+ climbable: info.climbable || info.isLadder,
64
+ danger: DANGER.has(info.name) || info.fluid?.type === 'lava',
65
+ slow: SLOW.has(info.name),
66
+ falls: FALLING.test(info.name),
67
+ breakable: !!block && block.diggable !== false && (block.hardness ?? 0) >= 0 && !NO_BREAK.has(info.name) && !info.fluid,
68
+ stateId
69
+ }
70
+ this.traits[stateId] = t
71
+ return t
72
+ }
73
+
74
+ // null when the chunk is not loaded
75
+ at (x, y, z) {
76
+ const s = this.world.stateAt(x, y, z)
77
+ if (s == null) return null
78
+ return this.trait(s)
79
+ }
80
+
81
+ // ticks to break a block with the best tool in the inventory
82
+ digCost (t) {
83
+ if (!t.breakable) return Infinity
84
+ let ticks = this.digTicks.get(t.stateId)
85
+ if (ticks !== undefined) return ticks
86
+ ticks = Infinity
87
+ let block
88
+ try { block = this.Block.fromStateId(t.stateId, 0) } catch { block = null }
89
+ if (block) {
90
+ const effects = this.effects
91
+ for (const tool of this.tools) {
92
+ let enchants = []
93
+ try { enchants = tool?.enchants ?? [] } catch { enchants = [] }
94
+ const ms = block.digTime(tool ? tool.type : null, false, false, false, Array.isArray(enchants) ? enchants : [], effects)
95
+ if (ms < Infinity) ticks = Math.min(ticks, Math.ceil(ms / 50))
96
+ }
97
+ }
98
+ if (this.options.maxDigTicks != null && ticks > this.options.maxDigTicks) ticks = Infinity
99
+ this.digTicks.set(t.stateId, ticks)
100
+ return ticks
101
+ }
102
+
103
+ // feet height when standing with the feet in block y, or null
104
+ floorAt (x, y, z) {
105
+ const feet = this.at(x, y, z)
106
+ if (!feet || feet.danger) return null
107
+ if (!feet.empty) {
108
+ if (feet.top <= 0.5) return y + feet.top
109
+ return null
110
+ }
111
+ const below = this.at(x, y - 1, z)
112
+ if (!below || below.danger || below.empty || below.top <= 0.5 || below.top > MAX_FLOOR) return null
113
+ return y - 1 + below.top
114
+ }
115
+
116
+ // the floor the block under the feet would give once the feet and head blocks are dug out
117
+ floorUnder (x, y, z) {
118
+ const below = this.at(x, y - 1, z)
119
+ if (!below || below.danger || below.empty || below.top <= 0.5 || below.top > MAX_FLOOR) return null
120
+ return y - 1 + below.top
121
+ }
122
+
123
+ // the body fits between `floor` and floor + 1.8 in column x/z; blocks in the way are added to `dig`
124
+ bodyFits (x, floor, z, dig) {
125
+ const y0 = Math.floor(floor)
126
+ const y1 = Math.floor(floor + PLAYER_HEIGHT - 1e-6)
127
+ for (let y = y0; y <= y1; y++) {
128
+ const t = this.at(x, y, z)
129
+ if (!t) return false
130
+ if (t.danger) return false
131
+ if (t.empty) continue
132
+ if (y === y0 && y + t.top <= floor + 1e-6) continue // what we stand on
133
+ if (!dig || !this.canDigAt(x, y, z, t)) return false
134
+ dig.push([x, y, z, t])
135
+ }
136
+ return true
137
+ }
138
+
139
+ // digging must not let liquid or a falling block into the hole
140
+ canDigAt (x, y, z, t) {
141
+ if (!this.options.canDig || !t.breakable) return false
142
+ if (this.digCost(t) === Infinity) return false
143
+ const above = this.at(x, y + 1, z)
144
+ if (!above || above.falls || above.lava || above.water) return false
145
+ for (const [dx, dz] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
146
+ const side = this.at(x + dx, y, z + dz)
147
+ if (!side || side.lava || side.water) return false
148
+ }
149
+ return true
150
+ }
151
+
152
+ isWater (x, y, z) {
153
+ const t = this.at(x, y, z)
154
+ return !!t && t.water
155
+ }
156
+
157
+ isClimbable (x, y, z) {
158
+ const t = this.at(x, y, z)
159
+ return !!t && t.climbable
160
+ }
161
+
162
+ // extra ticks for standing somewhere slow
163
+ floorPenalty (x, floor, z) {
164
+ const t = this.at(x, Math.floor(floor) - (floor % 1 === 0 ? 1 : 0), z)
165
+ return t && t.slow ? COST.avoid : 0
166
+ }
167
+ }
168
+
169
+ module.exports = { Terrain, PLAYER_HEIGHT, STEP }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "spectra-physics",
3
- "version": "1.2.0",
4
- "description": "Vanilla-accurate client physics and movement for mineflayer (1.16.5 - 26.1)",
3
+ "version": "1.3.0",
4
+ "description": "Vanilla-accurate client physics, movement and pathfinding for mineflayer (1.16.5 - 26.1)",
5
5
  "main": "index.js",
6
6
  "files": [
7
7
  "index.js",
@@ -18,7 +18,8 @@
18
18
  "physics",
19
19
  "movement",
20
20
  "bot",
21
- "prismarine"
21
+ "prismarine",
22
+ "pathfinder"
22
23
  ],
23
24
  "author": "trqxyz",
24
25
  "license": "GPL-3.0-only",