@block_factory/lib 0.0.5 → 0.0.8

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 (69) hide show
  1. package/_module/BlockFactory.ts +10 -20
  2. package/_module/util/Command.ts +1 -9
  3. package/_module/util/Forms/Form.ts +5 -31
  4. package/_module/util/Forms/FormAction.ts +2 -1
  5. package/_module/util/Forms/FormMessage.ts +2 -1
  6. package/_module/util/Forms/FormModal.ts +9 -10
  7. package/_module/util/Forms/FormRegistry.ts +20 -23
  8. package/_module/util/Forms/IForm.ts +31 -0
  9. package/_module/util/Globals.ts +26 -0
  10. package/_module/util/ISystem.ts +58 -0
  11. package/_module/util/IVector.ts +420 -0
  12. package/_module/util/Math.ts +2 -0
  13. package/_module/util/Navigation.ts +130 -0
  14. package/_module/util/Signal.ts +71 -7
  15. package/_module/util/TempLeaker.ts +137 -0
  16. package/_module/util/Wrapper/IEntity.ts +93 -25
  17. package/_module/util/Wrapper/IItemStack.ts +63 -0
  18. package/_module/util/Wrapper/IPlayer.ts +73 -29
  19. package/_module/util/Wrapper/_Common.ts +130 -0
  20. package/_module/util/Wrapper/{Container.ts → _Container.ts} +5 -5
  21. package/index.js +3911 -521
  22. package/package.json +10 -4
  23. package/typedoc.json +6 -0
  24. package/_module/Framework/EntityTasks.ts +0 -203
  25. package/_module/Framework/Threads.ts +0 -72
  26. package/_module/Framework/_INIT.ts +0 -39
  27. package/_module/Types.ts +0 -10
  28. package/_module/util/System.ts +0 -21
  29. package/_module/util/Vector.ts +0 -388
  30. package/_types/_module/BlockFactory.d.ts +0 -19
  31. package/_types/_module/BlockFactory.d.ts.map +0 -1
  32. package/_types/_module/Framework/EntityTasks.d.ts +0 -40
  33. package/_types/_module/Framework/EntityTasks.d.ts.map +0 -1
  34. package/_types/_module/Framework/Threads.d.ts +0 -22
  35. package/_types/_module/Framework/Threads.d.ts.map +0 -1
  36. package/_types/_module/Framework/_INIT.d.ts +0 -19
  37. package/_types/_module/Framework/_INIT.d.ts.map +0 -1
  38. package/_types/_module/Types.d.ts +0 -10
  39. package/_types/_module/Types.d.ts.map +0 -1
  40. package/_types/_module/util/Command.d.ts +0 -92
  41. package/_types/_module/util/Command.d.ts.map +0 -1
  42. package/_types/_module/util/Forms/Form.d.ts +0 -12
  43. package/_types/_module/util/Forms/Form.d.ts.map +0 -1
  44. package/_types/_module/util/Forms/FormAction.d.ts +0 -73
  45. package/_types/_module/util/Forms/FormAction.d.ts.map +0 -1
  46. package/_types/_module/util/Forms/FormMessage.d.ts +0 -24
  47. package/_types/_module/util/Forms/FormMessage.d.ts.map +0 -1
  48. package/_types/_module/util/Forms/FormModal.d.ts +0 -66
  49. package/_types/_module/util/Forms/FormModal.d.ts.map +0 -1
  50. package/_types/_module/util/Forms/FormRegistry.d.ts +0 -12
  51. package/_types/_module/util/Forms/FormRegistry.d.ts.map +0 -1
  52. package/_types/_module/util/Math.d.ts +0 -20
  53. package/_types/_module/util/Math.d.ts.map +0 -1
  54. package/_types/_module/util/RawText.d.ts +0 -60
  55. package/_types/_module/util/RawText.d.ts.map +0 -1
  56. package/_types/_module/util/Signal.d.ts +0 -11
  57. package/_types/_module/util/Signal.d.ts.map +0 -1
  58. package/_types/_module/util/System.d.ts +0 -4
  59. package/_types/_module/util/System.d.ts.map +0 -1
  60. package/_types/_module/util/Vector.d.ts +0 -212
  61. package/_types/_module/util/Vector.d.ts.map +0 -1
  62. package/_types/_module/util/Wrapper/Container.d.ts +0 -9
  63. package/_types/_module/util/Wrapper/Container.d.ts.map +0 -1
  64. package/_types/_module/util/Wrapper/IEntity.d.ts +0 -12
  65. package/_types/_module/util/Wrapper/IEntity.d.ts.map +0 -1
  66. package/_types/_module/util/Wrapper/IPlayer.d.ts +0 -13
  67. package/_types/_module/util/Wrapper/IPlayer.d.ts.map +0 -1
  68. package/_types/index.d.ts +0 -3
  69. package/_types/index.d.ts.map +0 -1
@@ -0,0 +1,420 @@
1
+ /*
2
+ **************************************************
3
+ Copyright (c) Block Factory - All rights reserved.
4
+ **************************************************
5
+ Author: Donthedev <https://github.com/voxeldon>
6
+ **************************************************
7
+ */
8
+
9
+ /**
10
+ * 2D vector utility class.
11
+ * Provides common vector math operations for 2D space.
12
+ */
13
+ export class IVector2 {
14
+
15
+ /** Constant zero vector (0, 0) */
16
+ static ZERO: IVector2 = new IVector2(0, 0);
17
+
18
+ /** Up direction (0, 1) */
19
+ static UP: IVector2 = new IVector2(0, 1);
20
+
21
+ /** Down direction (0, -1) */
22
+ static DOWN: IVector2 = new IVector2(0, -1);
23
+
24
+ /** Left direction (-1, 0) */
25
+ static LEFT: IVector2 = new IVector2(-1, 0);
26
+
27
+ /** Right direction (1, 0) */
28
+ static RIGHT: IVector2 = new IVector2(1, 0);
29
+
30
+ /**
31
+ * Creates a new IVector2.
32
+ * @param x X component
33
+ * @param y Y component
34
+ */
35
+ constructor(public x: number, public y: number) { }
36
+
37
+ /**
38
+ * Adds a vector or scalar to a vector.
39
+ * @param a Base vector
40
+ * @param b Vector or scalar to add
41
+ */
42
+ static add(a: IVector2, b: IVector2 | number): IVector2 {
43
+ if (typeof b === 'number') return new IVector2(a.x + b, a.y + b);
44
+ else return new IVector2(a.x + b.x, a.y + b.y);
45
+ }
46
+
47
+ /**
48
+ * Subtracts a vector or scalar from a vector.
49
+ * @param a Base vector
50
+ * @param b Vector or scalar to subtract
51
+ */
52
+ static subtract(a: IVector2, b: IVector2 | number): IVector2 {
53
+ if (typeof b === 'number') return new IVector2(a.x - b, a.y - b);
54
+ else return new IVector2(a.x - b.x, a.y - b.y);
55
+ }
56
+
57
+ /**
58
+ * Multiplies a vector by another vector (component-wise) or scalar.
59
+ */
60
+ static multiply(a: IVector2, b: IVector2 | number): IVector2 {
61
+ if (typeof b === 'number') return new IVector2(a.x * b, a.y * b);
62
+ else return new IVector2(a.x * b.x, a.y * b.y);
63
+ }
64
+
65
+ /**
66
+ * Divides a vector by another vector (component-wise) or scalar.
67
+ */
68
+ static divide(a: IVector2, b: IVector2 | number): IVector2 {
69
+ if (typeof b === 'number') return new IVector2(a.x / b, a.y / b);
70
+ else return new IVector2(a.x / b.x, a.y / b.y);
71
+ }
72
+
73
+ /**
74
+ * Linearly interpolates between two vectors.
75
+ * @param t Interpolation factor (0–1)
76
+ */
77
+ static lerp(a: IVector2, b: IVector2, t: number): IVector2 {
78
+ return new IVector2(
79
+ a.x + (b.x - a.x) * t,
80
+ a.y + (b.y - a.y) * t
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Clamps a vector between a minimum and maximum vector.
86
+ */
87
+ static clamp(v: IVector2, min: IVector2, max: IVector2): IVector2 {
88
+ return new IVector2(
89
+ Math.min(Math.max(v.x, min.x), max.x),
90
+ Math.min(Math.max(v.y, min.y), max.y)
91
+ );
92
+ }
93
+
94
+ /**
95
+ * Dot product of two vectors.
96
+ */
97
+ static dot(a: IVector2, b: IVector2): number {
98
+ return a.x * b.x + a.y * b.y;
99
+ }
100
+
101
+ /**
102
+ * Length (magnitude) of a vector.
103
+ */
104
+ static magnitude(a: IVector2): number {
105
+ return Math.sqrt(a.x * a.x + a.y * a.y);
106
+ }
107
+
108
+ /**
109
+ * Returns a normalized (unit length) vector.
110
+ * If magnitude is zero, returns (0, 0).
111
+ */
112
+ static normalize(a: IVector2): IVector2 {
113
+ const mag = IVector2.magnitude(a);
114
+ return mag === 0 ? new IVector2(0, 0) : new IVector2(a.x / mag, a.y / mag);
115
+ }
116
+
117
+ /**
118
+ * Distance between two vectors.
119
+ */
120
+ static distance(a: IVector2, b: IVector2): number {
121
+ return IVector2.magnitude(IVector2.subtract(a, b));
122
+ }
123
+
124
+ /**
125
+ * Returns the negated vector.
126
+ */
127
+ static negate(a: IVector2): IVector2 {
128
+ return new IVector2(-a.x, -a.y);
129
+ }
130
+
131
+ /**
132
+ * Checks exact equality between two vectors.
133
+ */
134
+ static equals(a: IVector2, b: IVector2): boolean {
135
+ return a.x === b.x && a.y === b.y;
136
+ }
137
+
138
+ /**
139
+ * Checks approximate equality between two vectors.
140
+ * @param epsilon Allowed difference threshold
141
+ */
142
+ static approxEquals(a: IVector2, b: IVector2, epsilon = 0.0001): boolean {
143
+ return (
144
+ Math.abs(a.x - b.x) < epsilon &&
145
+ Math.abs(a.y - b.y) < epsilon
146
+ );
147
+ }
148
+
149
+ /**
150
+ * Returns the angle (in radians) between two vectors.
151
+ * Returns 0 if either vector has zero length.
152
+ */
153
+ static angle(a: IVector2, b: IVector2): number {
154
+ const magA = IVector2.magnitude(a);
155
+ const magB = IVector2.magnitude(b);
156
+ const denom = magA * magB;
157
+ if (denom === 0) return 0;
158
+
159
+ let c = IVector2.dot(a, b) / denom;
160
+ c = Math.max(-1, Math.min(1, c));
161
+ return Math.acos(c);
162
+ }
163
+
164
+ /**
165
+ * Returns a perpendicular vector (-y, x).
166
+ */
167
+ static perpendicular(a: IVector2): IVector2 {
168
+ return new IVector2(-a.y, a.x);
169
+ }
170
+
171
+ /**
172
+ * Floors each vector paramater.
173
+ */
174
+ static floor(v: IVector2): IVector2 {
175
+ return new IVector2(Math.floor(v.x), Math.floor(v.y));
176
+ }
177
+
178
+ /**
179
+ * Return vector as a string
180
+ * @param format Returns string with x: y: z: format.
181
+ */
182
+ static toString(v: IVector2, format = false): string {
183
+ if (format) return `x:${v.x} y:${v.y}`;
184
+ return `${v.x} ${v.y}`;
185
+ }
186
+ }
187
+
188
+ /**
189
+ * 3D vector utility class.
190
+ * Provides common vector math operations for 3D space.
191
+ */
192
+ export class IVector3 {
193
+
194
+ /** Constant zero vector (0, 0, 0) */
195
+ static ZERO: IVector3 = new IVector3(0, 0, 0);
196
+
197
+ /** Up direction (0, 1, 0) */
198
+ static UP: IVector3 = new IVector3(0, 1, 0);
199
+
200
+ /** Down direction (0, -1, 0) */
201
+ static DOWN: IVector3 = new IVector3(0, -1, 0);
202
+
203
+ /** Left direction (-1, 0, 0) */
204
+ static LEFT: IVector3 = new IVector3(-1, 0, 0);
205
+
206
+ /** Right direction (1, 0, 0) */
207
+ static RIGHT: IVector3 = new IVector3(1, 0, 0);
208
+
209
+ /** Forward direction (0, 0, 1) */
210
+ static FORWARD: IVector3 = new IVector3(0, 0, 1);
211
+
212
+ /** Backward direction (0, 0, -1) */
213
+ static BACK: IVector3 = new IVector3(0, 0, -1);
214
+
215
+ /** West direction (-1, 0, 0) */
216
+ static WEST: IVector3 = new IVector3(-1, 0, 0);
217
+
218
+ /** East direction (1, 0, 0) */
219
+ static EAST: IVector3 = new IVector3(1, 0, 0);
220
+
221
+ /** North direction (0, 0, 1) */
222
+ static NORTH: IVector3 = new IVector3(0, 0, 1);
223
+
224
+ /** South direction (0, 0, -1) */
225
+ static SOUTH: IVector3 = new IVector3(0, 0, -1);
226
+
227
+ /**
228
+ * Creates a new IVector3.
229
+ * @param x X component
230
+ * @param y Y component
231
+ * @param z Z component
232
+ */
233
+ constructor(public x: number, public y: number, public z: number) { }
234
+
235
+ /**
236
+ * Adds a vector or scalar to a vector.
237
+ * @param a Base vector
238
+ * @param b Vector or scalar to add
239
+ */
240
+ static add(a: IVector3, b: IVector3 | number): IVector3 {
241
+ if (typeof b === 'number') return new IVector3(a.x + b, a.y + b, a.z + b);
242
+ else return new IVector3(a.x + b.x, a.y + b.y, a.z + b.z);
243
+ }
244
+
245
+ /**
246
+ * Subtracts a vector or scalar from a vector.
247
+ * @param a Base vector
248
+ * @param b Vector or scalar to subtract
249
+ */
250
+ static subtract(a: IVector3, b: IVector3 | number): IVector3 {
251
+ if (typeof b === 'number') return new IVector3(a.x - b, a.y - b, a.z - b);
252
+ else return new IVector3(a.x - b.x, a.y - b.y, a.z - b.z);
253
+ }
254
+
255
+ /**
256
+ * Multiplies a vector by another vector (component-wise) or scalar.
257
+ */
258
+ static multiply(a: IVector3, b: IVector3 | number): IVector3 {
259
+ if (typeof b === 'number') return new IVector3(a.x * b, a.y * b, a.z * b);
260
+ else return new IVector3(a.x * b.x, a.y * b.y, a.z * b.z);
261
+ }
262
+
263
+ /**
264
+ * Divides a vector by another vector (component-wise) or scalar.
265
+ */
266
+ static divide(a: IVector3, b: IVector3 | number): IVector3 {
267
+ if (typeof b === 'number') return new IVector3(a.x / b, a.y / b, a.z / b);
268
+ else return new IVector3(a.x / b.x, a.y / b.y, a.z / b.z);
269
+ }
270
+
271
+ /**
272
+ * Linearly interpolates between two vectors.
273
+ * @param t Interpolation factor (0–1)
274
+ */
275
+ static lerp(a: IVector3, b: IVector3, t: number): IVector3 {
276
+ return new IVector3(
277
+ a.x + (b.x - a.x) * t,
278
+ a.y + (b.y - a.y) * t,
279
+ a.z + (b.z - a.z) * t
280
+ );
281
+ }
282
+
283
+ /**
284
+ * Clamps a vector between a minimum and maximum vector.
285
+ */
286
+ static clamp(v: IVector3, min: IVector3, max: IVector3): IVector3 {
287
+ return new IVector3(
288
+ Math.min(Math.max(v.x, min.x), max.x),
289
+ Math.min(Math.max(v.y, min.y), max.y),
290
+ Math.min(Math.max(v.z, min.z), max.z)
291
+ );
292
+ }
293
+
294
+ /**
295
+ * Dot product of two vectors.
296
+ */
297
+ static dot(a: IVector3, b: IVector3): number {
298
+ return a.x * b.x + a.y * b.y + a.z * b.z;
299
+ }
300
+
301
+ /**
302
+ * Cross product of two vectors.
303
+ * Returns a vector perpendicular to both inputs.
304
+ */
305
+ static cross(a: IVector3, b: IVector3): IVector3 {
306
+ return new IVector3(
307
+ a.y * b.z - a.z * b.y,
308
+ a.z * b.x - a.x * b.z,
309
+ a.x * b.y - a.y * b.x
310
+ );
311
+ }
312
+
313
+ /**
314
+ * Length (magnitude) of a vector.
315
+ */
316
+ static magnitude(a: IVector3): number {
317
+ return Math.sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
318
+ }
319
+
320
+ /**
321
+ * Returns a normalized (unit length) vector.
322
+ * If magnitude is zero, returns (0, 0, 0).
323
+ */
324
+ static normalize(a: IVector3): IVector3 {
325
+ const mag = IVector3.magnitude(a);
326
+ return mag === 0
327
+ ? new IVector3(0, 0, 0)
328
+ : new IVector3(a.x / mag, a.y / mag, a.z / mag);
329
+ }
330
+
331
+ /**
332
+ * Distance between two vectors.
333
+ */
334
+ static distance(a: IVector3, b: IVector3): number {
335
+ return IVector3.magnitude(IVector3.subtract(a, b));
336
+ }
337
+
338
+ /**
339
+ * Returns the negated vector.
340
+ */
341
+ static negate(a: IVector3): IVector3 {
342
+ return new IVector3(-a.x, -a.y, -a.z);
343
+ }
344
+
345
+ /**
346
+ * Checks exact equality between two vectors.
347
+ */
348
+ static equals(a: IVector3, b: IVector3): boolean {
349
+ return a.x === b.x && a.y === b.y && a.z === b.z;
350
+ }
351
+
352
+ /**
353
+ * Checks approximate equality between two vectors.
354
+ * @param epsilon Allowed difference threshold
355
+ */
356
+ static approxEquals(a: IVector3, b: IVector3, epsilon = 0.0001): boolean {
357
+ return (
358
+ Math.abs(a.x - b.x) < epsilon &&
359
+ Math.abs(a.y - b.y) < epsilon &&
360
+ Math.abs(a.z - b.z) < epsilon
361
+ );
362
+ }
363
+
364
+ /**
365
+ * Returns the angle (in radians) between two vectors.
366
+ * Returns 0 if either vector has zero length.
367
+ */
368
+ static angle(a: IVector3, b: IVector3): number {
369
+ const magA = IVector3.magnitude(a);
370
+ const magB = IVector3.magnitude(b);
371
+ const denom = magA * magB;
372
+ if (denom === 0) return 0;
373
+
374
+ let c = IVector3.dot(a, b) / denom;
375
+ c = Math.max(-1, Math.min(1, c));
376
+ return Math.acos(c);
377
+ }
378
+
379
+ /**
380
+ * Projects vector `a` onto vector `onto`.
381
+ * If `onto` has zero length, returns (0, 0, 0).
382
+ */
383
+ static project(a: IVector3, onto: IVector3): IVector3 {
384
+ const denom = IVector3.dot(onto, onto);
385
+ if (denom === 0) return new IVector3(0, 0, 0);
386
+
387
+ const scale = IVector3.dot(a, onto) / denom;
388
+ return IVector3.multiply(onto, scale);
389
+ }
390
+
391
+ /**
392
+ * Converts spherical coordinates to cartesian coordinates.
393
+ * @param radius Distance from origin
394
+ * @param theta Azimuth angle (radians)
395
+ * @param phi Polar angle (radians)
396
+ */
397
+ static cartesian(radius: number, theta: number, phi: number): IVector3 {
398
+ return new IVector3(
399
+ radius * Math.sin(phi) * Math.cos(theta),
400
+ radius * Math.sin(phi) * Math.sin(theta),
401
+ radius * Math.cos(phi)
402
+ );
403
+ }
404
+
405
+ /**
406
+ * Floors each vector paramater.
407
+ */
408
+ static floor(v: IVector3): IVector3 {
409
+ return new IVector3(Math.floor(v.x), Math.floor(v.y), Math.floor(v.z));
410
+ }
411
+
412
+ /**
413
+ * Return vector as a string
414
+ * @param format Returns string with x: y: z: format.
415
+ */
416
+ static toString(v: IVector3, format = false): string {
417
+ if (format) return `x:${v.x} y:${v.y} z:${v.z}`;
418
+ return `${v.x} ${v.y} ${v.z}`;
419
+ }
420
+ }
@@ -21,6 +21,7 @@ export namespace MathUtils {
21
21
  return radians * (180 / Math.PI);
22
22
  };
23
23
 
24
+ /** Converts a Minecraft-style rotation (pitch/yaw in degrees) into a unit direction vector. */
24
25
  export function fromRotation(rotation: Vector2): Vector3 {
25
26
  const rotationH = toRadians(rotation.y * -1);
26
27
  const z0 = Math.cos(rotationH);
@@ -37,6 +38,7 @@ export namespace MathUtils {
37
38
  };
38
39
  };
39
40
 
41
+ /** Rotates a local-space offset around the Y axis by a yaw angle (degrees)*/
40
42
  export function rotateOffset(offset: Vector3, yawDegrees: number): Vector3 {
41
43
  const yaw = -yawDegrees * Math.PI / 180;
42
44
  const cos = Math.cos(yaw);
@@ -0,0 +1,130 @@
1
+ import { Block, Entity, system, TicksPerSecond } from "@minecraft/server";
2
+ import { Signal } from "./Signal";
3
+ import { IVector3 } from "./IVector";
4
+ import { ICustomEntity, IEntity } from "../BlockFactory";
5
+
6
+ export type OnNavPointReachedEvent<T extends ICustomEntity = ICustomEntity> = {
7
+ entity: IEntity<T>;
8
+ targetLocation: IVector3;
9
+ originLocation: IVector3;
10
+ };
11
+
12
+ export type OnNavigatingEntityStuckEvent<T extends ICustomEntity = ICustomEntity> = {
13
+ entity: IEntity<T>;
14
+ targetLocation: IVector3;
15
+ originLocation: IVector3;
16
+ };
17
+
18
+ const MAX_NAV_INSTS: number = 2;
19
+ const NAV_CYCLE_TAG_ID: string = "e547e9b28165";
20
+
21
+ export class Navigation<T extends ICustomEntity = ICustomEntity> {
22
+ public static onNavPointReachedEvent = new Signal<OnNavPointReachedEvent>();
23
+ public static onNavigatingEntityStuck = new Signal<OnNavigatingEntityStuckEvent>();
24
+ private source: IEntity<T>;
25
+ private targetId: string;
26
+ private static instanceIndex = new Map<string, number>();
27
+ private instanceId: number | undefined;
28
+ private typeId: string;
29
+ private intervalId: number = -1;
30
+ private navTarget: Entity | undefined
31
+ private originLocation: IVector3 = IVector3.ZERO;
32
+
33
+
34
+ public constructor(source: IEntity<T>, targetId: string) {
35
+ this.source = source;
36
+ this.targetId = targetId;
37
+ this.typeId = source.typeId;
38
+ }
39
+ private obstructionFix(location: IVector3): IVector3 {
40
+ let block: Block | undefined = this.source.dimension.getBlock(location);
41
+ if (block === undefined) throw new Error("obstructionFix error");
42
+ if (block.isAir) return location;
43
+ const ray = this.source.dimension.getBlockFromRay(new IVector3(location.x, 300, location.z), IVector3.DOWN);
44
+ if (!ray?.block) throw new Error("obstructionFix raycast error");
45
+ return new IVector3(ray.block.x, ray.block.y +1, ray.block.z);
46
+
47
+ }
48
+
49
+ public async setNavPoint(location: IVector3): Promise<void> {
50
+ console.warn(`Trying to set nav point @${IVector3.toString(IVector3.floor(location))}`);
51
+ if (!this.source || !this.source.dimension) throw Error("Critical source error");
52
+ if (this.instanceId !== undefined) {
53
+ return;
54
+ }
55
+ const i: number = Navigation.instanceIndex.get(this.source.typeId) || 0;
56
+ this.instanceId = i;
57
+ if (i === MAX_NAV_INSTS) {
58
+ console.warn(`Max navigation instances exeded. | Tried: ${i} - Max: ${MAX_NAV_INSTS}`);
59
+ return;
60
+ };
61
+ Navigation.instanceIndex.set(this.source.typeId, i + 1);
62
+ location = this.obstructionFix(location);
63
+ const target: Entity = this.source.dimension.spawnEntity(this.targetId, location);
64
+ this.navTarget = target;
65
+ const startDist: number = IVector3.distance(this.source.location, target.location);
66
+ if (startDist > 64) {
67
+ this.clearNavPoint();
68
+ return;
69
+ }
70
+ await system.waitTicks(1);
71
+ const tagId: string = `${NAV_CYCLE_TAG_ID}_${this.instanceId}`;
72
+ target.addTag(tagId);
73
+ this.source.addTag(tagId);
74
+ this.source.isNavigating = true;
75
+ this.processNavCycle(target, location);
76
+ this.originLocation = this.source.location;
77
+
78
+ }
79
+
80
+ private processNavCycle(target: Entity, location: IVector3): void {
81
+ let cycles = 0;
82
+ const maxCycles: number = 10;
83
+
84
+ const intervalId: number = system.runInterval(() => {
85
+ try {
86
+ if (!this.source.isValid || !target.isValid) {
87
+ this.clearNavPoint();
88
+ return;
89
+ }
90
+ const v = this.source.getVelocity();
91
+ const horizSpeed = Math.hypot(v.x, v.z);
92
+
93
+ if (horizSpeed < 0.05) {
94
+ cycles++;
95
+
96
+ if (cycles >= maxCycles) {
97
+ Navigation.onNavigatingEntityStuck.emit({ entity: this.source, targetLocation: location, originLocation: this.originLocation })
98
+ this.clearNavPoint();
99
+ return;
100
+ }
101
+ } else cycles = 0;
102
+
103
+ const distance = IVector3.distance(this.source.location, target.location);
104
+ if (distance > 2) return;
105
+
106
+ Navigation.onNavPointReachedEvent.emit({ entity: this.source, targetLocation: location, originLocation: this.originLocation });
107
+ this.clearNavPoint();
108
+ } catch (e) {
109
+ console.warn(e);
110
+ this.clearNavPoint();
111
+ }
112
+ }, TicksPerSecond / 4);
113
+ this.intervalId = intervalId;
114
+ }
115
+
116
+ public clearNavPoint(): void {
117
+ if (this.intervalId === -1 || !this.navTarget) return;
118
+ this.instanceId = undefined;
119
+ const i: number = Navigation.instanceIndex.get(this.typeId) || 0;
120
+ if (i > 0) Navigation.instanceIndex.set(this.typeId, i - 1);
121
+ if (this.source.isValid) {
122
+ this.source.isNavigating = false;
123
+ this.source.removeTag(`${NAV_CYCLE_TAG_ID}_${this.instanceId}`);
124
+ }
125
+ if (this.navTarget.isValid) this.navTarget.remove();
126
+ system.clearRun(this.intervalId);
127
+ this.intervalId = -1;
128
+ this.navTarget = undefined;
129
+ }
130
+ }
@@ -1,33 +1,97 @@
1
- export type Callback<T> = (data: T) => void;
1
+ /**
2
+ * Function signature for signal subscription callbacks.
3
+ *
4
+ * @template T Payload type emitted by the signal
5
+ */
6
+ export type SubscriptionCallback<T> = (data: T) => void;
2
7
 
8
+ /**
9
+ * Lightweight signal / event dispatcher inspired by Godot's signal system.
10
+ *
11
+ * Signals allow decoupled communication by emitting typed payloads to
12
+ * subscribed listeners.
13
+ *
14
+ * @template T Payload type (use `void` for no data)
15
+ */
3
16
  export class Signal<T = void> {
4
- private readonly listeners = new Set<Callback<T>>();
17
+ /**
18
+ * Registered signal listeners.
19
+ */
20
+ private readonly listeners = new Set<SubscriptionCallback<T>>();
5
21
 
22
+ /**
23
+ * Number of currently subscribed listeners.
24
+ */
6
25
  public get count(): number {
7
26
  return this.listeners.size;
8
27
  }
9
28
 
10
- public connect(callback: Callback<T>): void {
29
+ /**
30
+ * Subscribes a callback to this signal.
31
+ *
32
+ * The callback will be invoked every time the signal is emitted
33
+ * until it is explicitly unsubscribed or the signal is cleared.
34
+ *
35
+ * @param callback Function invoked on signal emission
36
+ */
37
+ public subscribe(callback: SubscriptionCallback<T>): void {
11
38
  this.listeners.add(callback);
12
39
  }
13
40
 
14
- public disconnect(callback: Callback<T>): boolean {
41
+ /**
42
+ * Unsubscribes a previously registered callback.
43
+ *
44
+ * @param callback Callback to remove
45
+ * @returns `true` if the callback was removed, `false` otherwise
46
+ */
47
+ public unsubscribe(callback: SubscriptionCallback<T>): boolean {
15
48
  return this.listeners.delete(callback);
16
49
  }
17
50
 
51
+ /**
52
+ * Removes all subscribed listeners from this signal.
53
+ */
18
54
  public clear(): void {
19
55
  this.listeners.clear();
20
56
  }
21
57
 
22
- public isConnected(callback: Callback<T>): boolean {
58
+ /**
59
+ * Checks whether a callback is currently subscribed.
60
+ *
61
+ * @param callback Callback to test
62
+ * @returns `true` if the callback is subscribed
63
+ */
64
+ public isSubscribed(callback: SubscriptionCallback<T>): boolean {
23
65
  return this.listeners.has(callback);
24
66
  }
25
67
 
68
+ /**
69
+ * Emits the signal immediately, invoking all subscribed callbacks.
70
+ *
71
+ * Listener errors are caught and logged to prevent a single failure
72
+ * from interrupting signal propagation.
73
+ *
74
+ * @param data Payload to pass to listeners
75
+ */
26
76
  public emit(data: T): void {
27
77
  for (const callback of Array.from(this.listeners)) {
28
- try { callback(data) } catch (err) {
29
- console.error("BFLIB: Signal listener error:", err);
78
+ try {
79
+ callback(data);
80
+ } catch (err) {
81
+ console.error("BFLIB: Subscription listener error:", err);
30
82
  }
31
83
  }
32
84
  }
85
+
86
+ /**
87
+ * Emits the signal asynchronously on the microtask queue.
88
+ *
89
+ * Useful for deferring execution to avoid re-entrancy issues
90
+ * or emitting during unsafe execution phases.
91
+ *
92
+ * @param data Payload to pass to listeners
93
+ */
94
+ public emitDeferred(data: T): void {
95
+ queueMicrotask(() => this.emit(data));
96
+ }
33
97
  }