@aethervtt/shard-nav 0.0.1
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.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/agents.d.ts +48 -0
- package/dist/agents.js +703 -0
- package/dist/bake.d.ts +26 -0
- package/dist/bake.js +710 -0
- package/dist/cache.d.ts +31 -0
- package/dist/cache.js +90 -0
- package/dist/components.d.ts +88 -0
- package/dist/components.js +279 -0
- package/dist/geometry.d.ts +33 -0
- package/dist/geometry.js +176 -0
- package/dist/grid.d.ts +96 -0
- package/dist/grid.js +463 -0
- package/dist/hash.d.ts +20 -0
- package/dist/hash.js +61 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +105 -0
- package/dist/methods.d.ts +97 -0
- package/dist/methods.js +179 -0
- package/dist/navmesh.d.ts +101 -0
- package/dist/navmesh.js +194 -0
- package/dist/overlay.d.ts +1 -0
- package/dist/overlay.js +166 -0
- package/dist/plugin.d.ts +11 -0
- package/dist/plugin.js +79 -0
- package/dist/query.d.ts +83 -0
- package/dist/query.js +391 -0
- package/dist/recast.d.ts +39 -0
- package/dist/recast.js +161 -0
- package/dist/state.d.ts +119 -0
- package/dist/state.js +49 -0
- package/dist/test-level.d.ts +19 -0
- package/dist/test-level.js +55 -0
- package/dist/test-maze.d.ts +8 -0
- package/dist/test-maze.js +56 -0
- package/package.json +42 -0
package/dist/grid.js
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import { ShardError } from "@aethervtt/shard-core";
|
|
2
|
+
const DIAGONAL_MODES = ["no-corners", "never", "always"];
|
|
3
|
+
const DIAGONAL_NO_CORNERS = 0;
|
|
4
|
+
const DIAGONAL_NEVER = 1;
|
|
5
|
+
const DIAGONAL_ALWAYS = 2;
|
|
6
|
+
const DX = new Int32Array([1, -1, 0, 0, 1, 1, -1, -1]);
|
|
7
|
+
const DY = new Int32Array([0, 0, 1, -1, 1, -1, 1, -1]);
|
|
8
|
+
class NavGridData {
|
|
9
|
+
width;
|
|
10
|
+
height;
|
|
11
|
+
costs;
|
|
12
|
+
/**
|
|
13
|
+
* Bumps on every edit, so grids and agents built from it know to refresh. `set` bumps it; code
|
|
14
|
+
* that writes `costs` directly must bump it too.
|
|
15
|
+
*/
|
|
16
|
+
version = 0;
|
|
17
|
+
masks = [void 0, void 0, void 0];
|
|
18
|
+
maskVersions = [-1, -1, -1];
|
|
19
|
+
constructor(width, height, costs) {
|
|
20
|
+
if (!(width >= 1 && height >= 1) || !Number.isInteger(width) || !Number.isInteger(height)) {
|
|
21
|
+
throw new ShardError("nav/invalid-grid", `A grid needs a whole size of at least 1\xD71`, {
|
|
22
|
+
hint: `Got ${width}\xD7${height}.`
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
this.width = width;
|
|
26
|
+
this.height = height;
|
|
27
|
+
this.costs = costs ?? new Uint8Array(width * height).fill(1);
|
|
28
|
+
if (this.costs.length !== width * height) {
|
|
29
|
+
throw new ShardError(
|
|
30
|
+
"nav/invalid-grid",
|
|
31
|
+
`A ${width}\xD7${height} grid needs ${width * height} costs, got ${this.costs.length}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
get(x, y) {
|
|
36
|
+
if (x < 0 || y < 0 || x >= this.width || y >= this.height) return 0;
|
|
37
|
+
return this.costs[y * this.width + x];
|
|
38
|
+
}
|
|
39
|
+
/** Sets a cell's cost (0 blocks it). Out-of-range cells are `nav/out-of-bounds`. */
|
|
40
|
+
set(x, y, cost) {
|
|
41
|
+
if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
|
|
42
|
+
throw new ShardError(
|
|
43
|
+
"nav/out-of-bounds",
|
|
44
|
+
`Cell (${x}, ${y}) is outside the ${this.width}\xD7${this.height} grid`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const i = y * this.width + x;
|
|
48
|
+
if (this.costs[i] === cost) return;
|
|
49
|
+
this.costs[i] = cost;
|
|
50
|
+
this.version++;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Per cell, a bit per direction (see `DX`, `DY`) that A* may step in under the diagonal rule:
|
|
54
|
+
* in bounds, walkable, and not cutting a blocked corner when that's not allowed. Cached until
|
|
55
|
+
* `version` changes, so searches skip walls without testing them.
|
|
56
|
+
*/
|
|
57
|
+
neighbors(diagonal) {
|
|
58
|
+
const cached = this.masks[diagonal];
|
|
59
|
+
if (cached && this.maskVersions[diagonal] === this.version) return cached;
|
|
60
|
+
const w = this.width;
|
|
61
|
+
const h = this.height;
|
|
62
|
+
const costs = this.costs;
|
|
63
|
+
const out = cached?.length === w * h ? cached : new Uint8Array(w * h);
|
|
64
|
+
const dirs = diagonal === DIAGONAL_NEVER ? 4 : 8;
|
|
65
|
+
for (let y = 0; y < h; y++) {
|
|
66
|
+
for (let x = 0; x < w; x++) {
|
|
67
|
+
let mask = 0;
|
|
68
|
+
for (let d = 0; d < dirs; d++) {
|
|
69
|
+
const nx = x + DX[d];
|
|
70
|
+
const ny = y + DY[d];
|
|
71
|
+
if (nx < 0 || ny < 0 || nx >= w || ny >= h || costs[ny * w + nx] === 0) continue;
|
|
72
|
+
if (d >= 4 && diagonal === DIAGONAL_NO_CORNERS && (costs[y * w + nx] === 0 || costs[ny * w + x] === 0))
|
|
73
|
+
continue;
|
|
74
|
+
mask |= 1 << d;
|
|
75
|
+
}
|
|
76
|
+
out[y * w + x] = mask;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
this.masks[diagonal] = out;
|
|
80
|
+
this.maskVersions[diagonal] = this.version;
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
copyFrom(other) {
|
|
84
|
+
this.width = other.width;
|
|
85
|
+
this.height = other.height;
|
|
86
|
+
this.costs = other.costs;
|
|
87
|
+
this.version++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
class GridSearch {
|
|
91
|
+
capacity = 0;
|
|
92
|
+
g = new Float64Array(0);
|
|
93
|
+
parent = new Int32Array(0);
|
|
94
|
+
/** `gen`: seen this search (open). `gen + 1`: closed. Anything else: untouched. */
|
|
95
|
+
stamp = new Uint32Array(0);
|
|
96
|
+
heap = new Int32Array(0);
|
|
97
|
+
heapF = new Float64Array(0);
|
|
98
|
+
heapG = new Float64Array(0);
|
|
99
|
+
heapPos = new Int32Array(0);
|
|
100
|
+
stack = new Int32Array(0);
|
|
101
|
+
gen = 0;
|
|
102
|
+
/** The last path's cells (y · width + x), start first. */
|
|
103
|
+
cells = new Int32Array(0);
|
|
104
|
+
/** How many cells `cells` holds. */
|
|
105
|
+
count = 0;
|
|
106
|
+
/** Path cost of the last search (world distance × cell cost). */
|
|
107
|
+
cost = 0;
|
|
108
|
+
/** Whether the last search reached the goal; false means `cells` ends at the closest cell. */
|
|
109
|
+
reached = false;
|
|
110
|
+
/** Cells expanded by the last search. */
|
|
111
|
+
expanded = 0;
|
|
112
|
+
ensure(n) {
|
|
113
|
+
if (n <= this.capacity) return;
|
|
114
|
+
this.capacity = n;
|
|
115
|
+
this.g = new Float64Array(n);
|
|
116
|
+
this.parent = new Int32Array(n);
|
|
117
|
+
this.stamp = new Uint32Array(n);
|
|
118
|
+
this.heap = new Int32Array(n);
|
|
119
|
+
this.heapF = new Float64Array(n);
|
|
120
|
+
this.heapG = new Float64Array(n);
|
|
121
|
+
this.heapPos = new Int32Array(n);
|
|
122
|
+
this.stack = new Int32Array(n);
|
|
123
|
+
this.cells = new Int32Array(n);
|
|
124
|
+
this.gen = 0;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Finds the cheapest path from cell (sx, sy) to (gx, gy). Moving costs the world distance
|
|
128
|
+
* (cell size `csx` × `csy`) times the entered cell's cost. Returns true when the goal was
|
|
129
|
+
* reached; otherwise `cells` leads to the reachable cell closest to the goal. Both cells must
|
|
130
|
+
* be walkable (see `nearestWalkable`).
|
|
131
|
+
*/
|
|
132
|
+
search(grid, sx, sy, gx, gy, diagonal, csx = 1, csy = 1) {
|
|
133
|
+
const w = grid.width;
|
|
134
|
+
const h = grid.height;
|
|
135
|
+
this.ensure(w * h);
|
|
136
|
+
this.gen += 2;
|
|
137
|
+
if (this.gen >= 4294967280) {
|
|
138
|
+
this.stamp.fill(0);
|
|
139
|
+
this.gen = 2;
|
|
140
|
+
}
|
|
141
|
+
const open = this.gen;
|
|
142
|
+
const closed = open + 1;
|
|
143
|
+
const costs = grid.costs;
|
|
144
|
+
const g = this.g;
|
|
145
|
+
const parent = this.parent;
|
|
146
|
+
const stamp = this.stamp;
|
|
147
|
+
const heap = this.heap;
|
|
148
|
+
const heapF = this.heapF;
|
|
149
|
+
const heapG = this.heapG;
|
|
150
|
+
const heapPos = this.heapPos;
|
|
151
|
+
const stack = this.stack;
|
|
152
|
+
const cd = Math.sqrt(csx * csx + csy * csy);
|
|
153
|
+
const never = diagonal === DIAGONAL_NEVER;
|
|
154
|
+
const masks = grid.neighbors(diagonal);
|
|
155
|
+
const start = sy * w + sx;
|
|
156
|
+
const goal = gy * w + gx;
|
|
157
|
+
let size = 0;
|
|
158
|
+
let top = 0;
|
|
159
|
+
let expanded = 0;
|
|
160
|
+
const dx0 = sx > gx ? sx - gx : gx - sx;
|
|
161
|
+
const dy0 = sy > gy ? sy - gy : gy - sy;
|
|
162
|
+
const m0 = dx0 < dy0 ? dx0 : dy0;
|
|
163
|
+
const h0 = never ? dx0 * csx + dy0 * csy : m0 * cd + (dx0 - m0) * csx + (dy0 - m0) * csy;
|
|
164
|
+
g[start] = 0;
|
|
165
|
+
parent[start] = -1;
|
|
166
|
+
stamp[start] = open;
|
|
167
|
+
heap[0] = start;
|
|
168
|
+
heapF[0] = h0;
|
|
169
|
+
heapG[0] = 0;
|
|
170
|
+
heapPos[start] = 0;
|
|
171
|
+
size = 1;
|
|
172
|
+
let best = start;
|
|
173
|
+
let bestH = h0;
|
|
174
|
+
let found = false;
|
|
175
|
+
let curF = h0;
|
|
176
|
+
while (top > 0 || size > 0) {
|
|
177
|
+
let cur;
|
|
178
|
+
if (top > 0) cur = stack[--top];
|
|
179
|
+
else {
|
|
180
|
+
cur = heap[0];
|
|
181
|
+
curF = heapF[0];
|
|
182
|
+
size--;
|
|
183
|
+
if (size > 0) {
|
|
184
|
+
const node = heap[size];
|
|
185
|
+
const nf = heapF[size];
|
|
186
|
+
const ng = heapG[size];
|
|
187
|
+
let i = 0;
|
|
188
|
+
for (; ; ) {
|
|
189
|
+
const l = 2 * i + 1;
|
|
190
|
+
if (l >= size) break;
|
|
191
|
+
let c = l;
|
|
192
|
+
const r = l + 1;
|
|
193
|
+
if (r < size) {
|
|
194
|
+
const fr = heapF[r];
|
|
195
|
+
const fl = heapF[l];
|
|
196
|
+
if (fr < fl || fr === fl && heapG[r] > heapG[l]) c = r;
|
|
197
|
+
}
|
|
198
|
+
const fc = heapF[c];
|
|
199
|
+
if (fc > nf || fc === nf && heapG[c] <= ng) break;
|
|
200
|
+
const moved = heap[c];
|
|
201
|
+
heap[i] = moved;
|
|
202
|
+
heapF[i] = fc;
|
|
203
|
+
heapG[i] = heapG[c];
|
|
204
|
+
heapPos[moved] = i;
|
|
205
|
+
i = c;
|
|
206
|
+
}
|
|
207
|
+
heap[i] = node;
|
|
208
|
+
heapF[i] = nf;
|
|
209
|
+
heapG[i] = ng;
|
|
210
|
+
heapPos[node] = i;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (cur === goal) {
|
|
214
|
+
found = true;
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
stamp[cur] = closed;
|
|
218
|
+
expanded++;
|
|
219
|
+
const cy = cur / w | 0;
|
|
220
|
+
const cx = cur - cy * w;
|
|
221
|
+
const gc = g[cur];
|
|
222
|
+
const hc = curF - gc;
|
|
223
|
+
if (hc < bestH) {
|
|
224
|
+
bestH = hc;
|
|
225
|
+
best = cur;
|
|
226
|
+
}
|
|
227
|
+
let mask = masks[cur];
|
|
228
|
+
while (mask !== 0) {
|
|
229
|
+
const bit = mask & -mask;
|
|
230
|
+
mask ^= bit;
|
|
231
|
+
const d = 31 - Math.clz32(bit);
|
|
232
|
+
const nx = cx + DX[d];
|
|
233
|
+
const ny = cy + DY[d];
|
|
234
|
+
const next = ny * w + nx;
|
|
235
|
+
const st = stamp[next];
|
|
236
|
+
if (st === closed) continue;
|
|
237
|
+
const step = d < 2 ? csx : d < 4 ? csy : cd;
|
|
238
|
+
const cost = costs[next];
|
|
239
|
+
const tentative = gc + step * cost;
|
|
240
|
+
let i;
|
|
241
|
+
let f;
|
|
242
|
+
if (st === open) {
|
|
243
|
+
const old = g[next];
|
|
244
|
+
if (tentative >= old) continue;
|
|
245
|
+
i = heapPos[next];
|
|
246
|
+
g[next] = tentative;
|
|
247
|
+
parent[next] = cur;
|
|
248
|
+
if (i === -1) continue;
|
|
249
|
+
f = heapF[i] - old + tentative;
|
|
250
|
+
} else {
|
|
251
|
+
stamp[next] = open;
|
|
252
|
+
const hx = nx > gx ? nx - gx : gx - nx;
|
|
253
|
+
const hy = ny > gy ? ny - gy : gy - ny;
|
|
254
|
+
const hm = hx < hy ? hx : hy;
|
|
255
|
+
f = tentative + (never ? hx * csx + hy * csy : hm * cd + (hx - hm) * csx + (hy - hm) * csy);
|
|
256
|
+
g[next] = tentative;
|
|
257
|
+
parent[next] = cur;
|
|
258
|
+
if (f <= curF) {
|
|
259
|
+
stack[top++] = next;
|
|
260
|
+
heapPos[next] = -1;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
i = size++;
|
|
264
|
+
}
|
|
265
|
+
while (i > 0) {
|
|
266
|
+
const p = i - 1 >> 1;
|
|
267
|
+
const fp = heapF[p];
|
|
268
|
+
if (fp < f || fp === f && heapG[p] >= tentative) break;
|
|
269
|
+
const moved = heap[p];
|
|
270
|
+
heap[i] = moved;
|
|
271
|
+
heapF[i] = fp;
|
|
272
|
+
heapG[i] = heapG[p];
|
|
273
|
+
heapPos[moved] = i;
|
|
274
|
+
i = p;
|
|
275
|
+
}
|
|
276
|
+
heap[i] = next;
|
|
277
|
+
heapF[i] = f;
|
|
278
|
+
heapG[i] = tentative;
|
|
279
|
+
heapPos[next] = i;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const end = found ? goal : best;
|
|
283
|
+
this.reached = found;
|
|
284
|
+
this.cost = g[end];
|
|
285
|
+
this.expanded = expanded;
|
|
286
|
+
let count = 0;
|
|
287
|
+
const cells = this.cells;
|
|
288
|
+
for (let c = end; c !== -1; c = parent[c]) cells[count++] = c;
|
|
289
|
+
for (let i = 0, j = count - 1; i < j; i++, j--) {
|
|
290
|
+
const tmp = cells[i];
|
|
291
|
+
cells[i] = cells[j];
|
|
292
|
+
cells[j] = tmp;
|
|
293
|
+
}
|
|
294
|
+
this.count = count;
|
|
295
|
+
return found;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const SEG = new Float64Array(4);
|
|
299
|
+
const SMOOTH = new Float64Array(6);
|
|
300
|
+
function gridTrace(grid, ax, ay, bx, by, diagonal, maxCost, hit) {
|
|
301
|
+
SEG[0] = ax;
|
|
302
|
+
SEG[1] = ay;
|
|
303
|
+
SEG[2] = bx;
|
|
304
|
+
SEG[3] = by;
|
|
305
|
+
return traceSegment(grid, SEG, diagonal, maxCost, hit);
|
|
306
|
+
}
|
|
307
|
+
function traceSegment(grid, seg, diagonal, maxCost, hit) {
|
|
308
|
+
const w = grid.width;
|
|
309
|
+
const h = grid.height;
|
|
310
|
+
const costs = grid.costs;
|
|
311
|
+
const ax = seg[0];
|
|
312
|
+
const ay = seg[1];
|
|
313
|
+
const bx = seg[2];
|
|
314
|
+
const by = seg[3];
|
|
315
|
+
let cx = Math.floor(ax);
|
|
316
|
+
let cy = Math.floor(ay);
|
|
317
|
+
const ex = Math.floor(bx);
|
|
318
|
+
const ey = Math.floor(by);
|
|
319
|
+
const dx = bx - ax;
|
|
320
|
+
const dy = by - ay;
|
|
321
|
+
const stepX = dx > 0 ? 1 : dx < 0 ? -1 : 0;
|
|
322
|
+
const stepY = dy > 0 ? 1 : dy < 0 ? -1 : 0;
|
|
323
|
+
const tDeltaX = stepX !== 0 ? Math.abs(1 / dx) : Infinity;
|
|
324
|
+
const tDeltaY = stepY !== 0 ? Math.abs(1 / dy) : Infinity;
|
|
325
|
+
let tMaxX = stepX > 0 ? (cx + 1 - ax) / dx : stepX < 0 ? (cx - ax) / dx : Infinity;
|
|
326
|
+
let tMaxY = stepY > 0 ? (cy + 1 - ay) / dy : stepY < 0 ? (cy - ay) / dy : Infinity;
|
|
327
|
+
let t = 0;
|
|
328
|
+
let clear = true;
|
|
329
|
+
let guard = Math.abs(ex - cx) + Math.abs(ey - cy) + 2;
|
|
330
|
+
for (; ; ) {
|
|
331
|
+
let cost = 0;
|
|
332
|
+
if (cx >= 0 && cy >= 0 && cx < w && cy < h) cost = costs[cy * w + cx];
|
|
333
|
+
if (cost === 0 || cost > maxCost) {
|
|
334
|
+
clear = false;
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
if (cx === ex && cy === ey || --guard < 0) break;
|
|
338
|
+
const diff = tMaxX - tMaxY;
|
|
339
|
+
if (Math.abs(diff) < 1e-9) {
|
|
340
|
+
if (diagonal !== DIAGONAL_ALWAYS) {
|
|
341
|
+
const sx = cx + stepX;
|
|
342
|
+
const sy = cy + stepY;
|
|
343
|
+
const a = sx >= 0 && sx < w && cy >= 0 && cy < h ? costs[cy * w + sx] : 0;
|
|
344
|
+
const b = cx >= 0 && cx < w && sy >= 0 && sy < h ? costs[sy * w + cx] : 0;
|
|
345
|
+
if (a === 0 || b === 0) {
|
|
346
|
+
t = tMaxX;
|
|
347
|
+
clear = false;
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
t = tMaxX;
|
|
352
|
+
cx += stepX;
|
|
353
|
+
cy += stepY;
|
|
354
|
+
tMaxX += tDeltaX;
|
|
355
|
+
tMaxY += tDeltaY;
|
|
356
|
+
guard--;
|
|
357
|
+
} else if (diff < 0) {
|
|
358
|
+
t = tMaxX;
|
|
359
|
+
cx += stepX;
|
|
360
|
+
tMaxX += tDeltaX;
|
|
361
|
+
} else {
|
|
362
|
+
t = tMaxY;
|
|
363
|
+
cy += stepY;
|
|
364
|
+
tMaxY += tDeltaY;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (hit) {
|
|
368
|
+
const c = clear ? 1 : t < 0 ? 0 : t > 1 ? 1 : t;
|
|
369
|
+
hit.t = c;
|
|
370
|
+
hit.x = ax + dx * c;
|
|
371
|
+
hit.y = ay + dy * c;
|
|
372
|
+
}
|
|
373
|
+
return clear;
|
|
374
|
+
}
|
|
375
|
+
function smoothGridPath(grid, cells, count, ends, diagonal, out, stride, maxCorners) {
|
|
376
|
+
const w = grid.width;
|
|
377
|
+
const costs = grid.costs;
|
|
378
|
+
let n = 1;
|
|
379
|
+
out[0] = ends[0];
|
|
380
|
+
out[1] = ends[1];
|
|
381
|
+
if (count <= 1) {
|
|
382
|
+
if (maxCorners > 1 && (ends[2] !== ends[0] || ends[3] !== ends[1])) {
|
|
383
|
+
out[stride] = ends[2];
|
|
384
|
+
out[stride + 1] = ends[3];
|
|
385
|
+
n = 2;
|
|
386
|
+
}
|
|
387
|
+
return n;
|
|
388
|
+
}
|
|
389
|
+
const p = SMOOTH;
|
|
390
|
+
p[0] = ends[0];
|
|
391
|
+
p[1] = ends[1];
|
|
392
|
+
p[2] = ends[0];
|
|
393
|
+
p[3] = ends[1];
|
|
394
|
+
p[4] = ends[2];
|
|
395
|
+
p[5] = ends[3];
|
|
396
|
+
let anchorCost = costs[cells[0]];
|
|
397
|
+
let prevCost = anchorCost;
|
|
398
|
+
for (let i = 1; i < count; i++) {
|
|
399
|
+
const c = cells[i];
|
|
400
|
+
const cy = c / w | 0;
|
|
401
|
+
const last = i === count - 1;
|
|
402
|
+
SEG[0] = p[0];
|
|
403
|
+
SEG[1] = p[1];
|
|
404
|
+
SEG[2] = last ? p[4] : c - cy * w + 0.5;
|
|
405
|
+
SEG[3] = last ? p[5] : cy + 0.5;
|
|
406
|
+
const cost = costs[c];
|
|
407
|
+
const limit = cost > anchorCost ? cost : anchorCost;
|
|
408
|
+
if (!traceSegment(grid, SEG, diagonal, limit)) {
|
|
409
|
+
if (n >= maxCorners - 1) break;
|
|
410
|
+
out[n * stride] = p[2];
|
|
411
|
+
out[n * stride + 1] = p[3];
|
|
412
|
+
n++;
|
|
413
|
+
p[0] = p[2];
|
|
414
|
+
p[1] = p[3];
|
|
415
|
+
anchorCost = prevCost;
|
|
416
|
+
}
|
|
417
|
+
p[2] = SEG[2];
|
|
418
|
+
p[3] = SEG[3];
|
|
419
|
+
prevCost = cost;
|
|
420
|
+
}
|
|
421
|
+
out[n * stride] = p[4];
|
|
422
|
+
out[n * stride + 1] = p[5];
|
|
423
|
+
return n + 1;
|
|
424
|
+
}
|
|
425
|
+
function nearestWalkable(grid, x, y, radius) {
|
|
426
|
+
const w = grid.width;
|
|
427
|
+
const h = grid.height;
|
|
428
|
+
const cx = Math.floor(x);
|
|
429
|
+
const cy = Math.floor(y);
|
|
430
|
+
if (cx >= 0 && cy >= 0 && cx < w && cy < h && grid.costs[cy * w + cx] > 0) return cy * w + cx;
|
|
431
|
+
let best = -1;
|
|
432
|
+
let bestD = Infinity;
|
|
433
|
+
for (let r = 1; r <= radius; r++) {
|
|
434
|
+
for (let j = cy - r; j <= cy + r; j++) {
|
|
435
|
+
if (j < 0 || j >= h) continue;
|
|
436
|
+
const edge = j === cy - r || j === cy + r;
|
|
437
|
+
for (let i = cx - r; i <= cx + r; i += edge ? 1 : 2 * r) {
|
|
438
|
+
if (i < 0 || i >= w || grid.costs[j * w + i] === 0) continue;
|
|
439
|
+
const ddx = i + 0.5 - x;
|
|
440
|
+
const ddy = j + 0.5 - y;
|
|
441
|
+
const d = ddx * ddx + ddy * ddy;
|
|
442
|
+
if (d < bestD) {
|
|
443
|
+
bestD = d;
|
|
444
|
+
best = j * w + i;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (best !== -1 && Math.sqrt(bestD) <= r + 0.5) return best;
|
|
449
|
+
}
|
|
450
|
+
return best;
|
|
451
|
+
}
|
|
452
|
+
export {
|
|
453
|
+
DIAGONAL_ALWAYS,
|
|
454
|
+
DIAGONAL_MODES,
|
|
455
|
+
DIAGONAL_NEVER,
|
|
456
|
+
DIAGONAL_NO_CORNERS,
|
|
457
|
+
GridSearch,
|
|
458
|
+
NavGridData,
|
|
459
|
+
gridTrace,
|
|
460
|
+
nearestWalkable,
|
|
461
|
+
smoothGridPath,
|
|
462
|
+
traceSegment
|
|
463
|
+
};
|
package/dist/hash.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A streaming 64-bit hash (two independent 32-bit murmur3-style lanes) over 32-bit words, for
|
|
3
|
+
* keying navmesh tiles by the exact geometry that built them. Not cryptographic.
|
|
4
|
+
*/
|
|
5
|
+
export declare class Hash64 {
|
|
6
|
+
private a;
|
|
7
|
+
private b;
|
|
8
|
+
private n;
|
|
9
|
+
private readonly f32;
|
|
10
|
+
private readonly u32;
|
|
11
|
+
reset(seed?: number): this;
|
|
12
|
+
u32word(v: number): this;
|
|
13
|
+
/** Hashes a float by its 32-bit representation (so 0.1 and 0.1f agree, -0 and 0 don't). */
|
|
14
|
+
f32word(v: number): this;
|
|
15
|
+
string(s: string): this;
|
|
16
|
+
/** The finished hash as two 32-bit lanes. */
|
|
17
|
+
lanes(out: Uint32Array, offset?: number): void;
|
|
18
|
+
/** The hash as 16 hex digits. */
|
|
19
|
+
hex(): string;
|
|
20
|
+
}
|
package/dist/hash.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
class Hash64 {
|
|
2
|
+
a = 2538058380;
|
|
3
|
+
b = 2246822507;
|
|
4
|
+
n = 0;
|
|
5
|
+
f32 = new Float32Array(1);
|
|
6
|
+
u32 = new Uint32Array(this.f32.buffer);
|
|
7
|
+
reset(seed = 0) {
|
|
8
|
+
this.a = (2538058380 ^ seed) >>> 0;
|
|
9
|
+
this.b = (2246822507 ^ Math.imul(seed, 668265263)) >>> 0;
|
|
10
|
+
this.n = 0;
|
|
11
|
+
return this;
|
|
12
|
+
}
|
|
13
|
+
u32word(v) {
|
|
14
|
+
let k = Math.imul(v >>> 0, 3432918353);
|
|
15
|
+
k = k << 15 | k >>> 17;
|
|
16
|
+
k = Math.imul(k, 461845907);
|
|
17
|
+
this.a ^= k;
|
|
18
|
+
this.a = this.a << 13 | this.a >>> 19;
|
|
19
|
+
this.a = Math.imul(this.a, 5) + 3864292196 >>> 0;
|
|
20
|
+
let j = Math.imul(v >>> 0, 2246822519);
|
|
21
|
+
j = j << 13 | j >>> 19;
|
|
22
|
+
j = Math.imul(j, 3266489917);
|
|
23
|
+
this.b ^= j;
|
|
24
|
+
this.b = this.b << 17 | this.b >>> 15;
|
|
25
|
+
this.b = Math.imul(this.b, 3) + 1390208809 >>> 0;
|
|
26
|
+
this.n++;
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
/** Hashes a float by its 32-bit representation (so 0.1 and 0.1f agree, -0 and 0 don't). */
|
|
30
|
+
f32word(v) {
|
|
31
|
+
this.f32[0] = v;
|
|
32
|
+
return this.u32word(this.u32[0]);
|
|
33
|
+
}
|
|
34
|
+
string(s) {
|
|
35
|
+
for (let i = 0; i < s.length; i++) this.u32word(s.charCodeAt(i));
|
|
36
|
+
return this.u32word(s.length);
|
|
37
|
+
}
|
|
38
|
+
/** The finished hash as two 32-bit lanes. */
|
|
39
|
+
lanes(out, offset = 0) {
|
|
40
|
+
const a = fmix(this.a ^ this.n ^ this.b >>> 7);
|
|
41
|
+
out[offset] = a;
|
|
42
|
+
out[offset + 1] = fmix(this.b ^ this.n ^ a >>> 11);
|
|
43
|
+
}
|
|
44
|
+
/** The hash as 16 hex digits. */
|
|
45
|
+
hex() {
|
|
46
|
+
this.lanes(LANES);
|
|
47
|
+
return LANES[0].toString(16).padStart(8, "0") + LANES[1].toString(16).padStart(8, "0");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const LANES = new Uint32Array(2);
|
|
51
|
+
function fmix(h) {
|
|
52
|
+
h ^= h >>> 16;
|
|
53
|
+
h = Math.imul(h, 2246822507);
|
|
54
|
+
h ^= h >>> 13;
|
|
55
|
+
h = Math.imul(h, 3266489909);
|
|
56
|
+
h ^= h >>> 16;
|
|
57
|
+
return h >>> 0;
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
Hash64
|
|
61
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { World } from '@aethervtt/shard-core';
|
|
2
|
+
import { type NavAreasValue } from './components.js';
|
|
3
|
+
export { navAgents } from './agents.js';
|
|
4
|
+
export { liveTileKeys, navBake, updateNavigation } from './bake.js';
|
|
5
|
+
export { loadNavCache, NAV_CACHE_PATH, NavCache, NavCacheStore, saveNavCache } from './cache.js';
|
|
6
|
+
export { AGENT_STATUSES, type AgentStatus, DRIVES, type Drive, GRID_SOURCES, type GridSource, NavAgent, type NavAgentEventData, NavAgentState, NavAreas, type NavAreasValue, NavArrived, NavGrid, NavGridDataAssetType, NavGridDataImporter, NavGridDataSchema, NavGridDataStore, NavGridDatas, NavMesh, NavSource, NavUnreachable, navGridFromJson, navGridToJson, OffMeshLink, } from './components.js';
|
|
7
|
+
export { DIAGONAL_MODES, type DiagonalMode, type GridHit, GridSearch, gridTrace, NavGridData, nearestWalkable, smoothGridPath, traceSegment, } from './grid.js';
|
|
8
|
+
export { describeNav, navMethods } from './methods.js';
|
|
9
|
+
export { type BakeStats, NavMeshRuntime, type TileEntry } from './navmesh.js';
|
|
10
|
+
export { navGridPlugin, navPlugin } from './plugin.js';
|
|
11
|
+
export { createNavPath, createNavRayHit, type FindPathOptions, findPath, type NavPath, type NavRayHit, type NavTarget, navAt, navRaycast, nearestPoint, PATH_STATUSES, type PathStatus, } from './query.js';
|
|
12
|
+
export { type BakeSettings, loadRecast } from './recast.js';
|
|
13
|
+
export { type AgentRecord, type GridRecord, Nav, NavState, navState } from './state.js';
|
|
14
|
+
/** Replaces the navmesh area costs; agents and queries use them from the next frame. */
|
|
15
|
+
export declare function setNavAreas(world: World, areas: NavAreasValue): void;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { NavAreas } from "./components.js";
|
|
2
|
+
import { navAgents } from "./agents.js";
|
|
3
|
+
import { liveTileKeys, navBake, updateNavigation } from "./bake.js";
|
|
4
|
+
import { loadNavCache, NAV_CACHE_PATH, NavCache, NavCacheStore, saveNavCache } from "./cache.js";
|
|
5
|
+
import {
|
|
6
|
+
AGENT_STATUSES,
|
|
7
|
+
DRIVES,
|
|
8
|
+
GRID_SOURCES,
|
|
9
|
+
NavAgent,
|
|
10
|
+
NavAgentState,
|
|
11
|
+
NavAreas as NavAreas2,
|
|
12
|
+
NavArrived,
|
|
13
|
+
NavGrid,
|
|
14
|
+
NavGridDataAssetType,
|
|
15
|
+
NavGridDataImporter,
|
|
16
|
+
NavGridDataSchema,
|
|
17
|
+
NavGridDataStore,
|
|
18
|
+
NavGridDatas,
|
|
19
|
+
NavMesh,
|
|
20
|
+
NavSource,
|
|
21
|
+
NavUnreachable,
|
|
22
|
+
navGridFromJson,
|
|
23
|
+
navGridToJson,
|
|
24
|
+
OffMeshLink
|
|
25
|
+
} from "./components.js";
|
|
26
|
+
import {
|
|
27
|
+
DIAGONAL_MODES,
|
|
28
|
+
GridSearch,
|
|
29
|
+
gridTrace,
|
|
30
|
+
NavGridData,
|
|
31
|
+
nearestWalkable,
|
|
32
|
+
smoothGridPath,
|
|
33
|
+
traceSegment
|
|
34
|
+
} from "./grid.js";
|
|
35
|
+
import { describeNav, navMethods } from "./methods.js";
|
|
36
|
+
import { NavMeshRuntime } from "./navmesh.js";
|
|
37
|
+
import { navGridPlugin, navPlugin } from "./plugin.js";
|
|
38
|
+
import {
|
|
39
|
+
createNavPath,
|
|
40
|
+
createNavRayHit,
|
|
41
|
+
findPath,
|
|
42
|
+
navAt,
|
|
43
|
+
navRaycast,
|
|
44
|
+
nearestPoint,
|
|
45
|
+
PATH_STATUSES
|
|
46
|
+
} from "./query.js";
|
|
47
|
+
import { loadRecast } from "./recast.js";
|
|
48
|
+
import { Nav, NavState, navState } from "./state.js";
|
|
49
|
+
function setNavAreas(world, areas) {
|
|
50
|
+
world.insertResource(NavAreas, { ...areas });
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
AGENT_STATUSES,
|
|
54
|
+
DIAGONAL_MODES,
|
|
55
|
+
DRIVES,
|
|
56
|
+
GRID_SOURCES,
|
|
57
|
+
GridSearch,
|
|
58
|
+
NAV_CACHE_PATH,
|
|
59
|
+
Nav,
|
|
60
|
+
NavAgent,
|
|
61
|
+
NavAgentState,
|
|
62
|
+
NavAreas2 as NavAreas,
|
|
63
|
+
NavArrived,
|
|
64
|
+
NavCache,
|
|
65
|
+
NavCacheStore,
|
|
66
|
+
NavGrid,
|
|
67
|
+
NavGridData,
|
|
68
|
+
NavGridDataAssetType,
|
|
69
|
+
NavGridDataImporter,
|
|
70
|
+
NavGridDataSchema,
|
|
71
|
+
NavGridDataStore,
|
|
72
|
+
NavGridDatas,
|
|
73
|
+
NavMesh,
|
|
74
|
+
NavMeshRuntime,
|
|
75
|
+
NavSource,
|
|
76
|
+
NavState,
|
|
77
|
+
NavUnreachable,
|
|
78
|
+
OffMeshLink,
|
|
79
|
+
PATH_STATUSES,
|
|
80
|
+
createNavPath,
|
|
81
|
+
createNavRayHit,
|
|
82
|
+
describeNav,
|
|
83
|
+
findPath,
|
|
84
|
+
gridTrace,
|
|
85
|
+
liveTileKeys,
|
|
86
|
+
loadNavCache,
|
|
87
|
+
loadRecast,
|
|
88
|
+
navAgents,
|
|
89
|
+
navAt,
|
|
90
|
+
navBake,
|
|
91
|
+
navGridFromJson,
|
|
92
|
+
navGridPlugin,
|
|
93
|
+
navGridToJson,
|
|
94
|
+
navMethods,
|
|
95
|
+
navPlugin,
|
|
96
|
+
navRaycast,
|
|
97
|
+
navState,
|
|
98
|
+
nearestPoint,
|
|
99
|
+
nearestWalkable,
|
|
100
|
+
saveNavCache,
|
|
101
|
+
setNavAreas,
|
|
102
|
+
smoothGridPath,
|
|
103
|
+
traceSegment,
|
|
104
|
+
updateNavigation
|
|
105
|
+
};
|