@rpgjs/common 5.0.0-alpha.27 → 5.0.0-alpha.28
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/dist/Player.d.ts +70 -0
- package/dist/index.js +330 -12
- package/dist/index.js.map +1 -1
- package/dist/movement/MovementManager.d.ts +10 -2
- package/dist/movement/index.d.ts +1 -1
- package/package.json +2 -2
- package/src/Player.ts +88 -0
- package/src/movement/MovementManager.ts +11 -2
- package/src/movement/index.ts +1 -1
- package/src/rooms/Map.ts +83 -0
package/dist/Player.d.ts
CHANGED
|
@@ -94,6 +94,70 @@ export declare abstract class RpgCommonPlayer {
|
|
|
94
94
|
componentsRight: import('@signe/reactive').WritableSignal<string | null>;
|
|
95
95
|
isConnected: import('@signe/reactive').WritableSignal<boolean>;
|
|
96
96
|
private _intendedDirection;
|
|
97
|
+
private _directionFixed;
|
|
98
|
+
private _animationFixed;
|
|
99
|
+
/**
|
|
100
|
+
* Get whether direction changes are locked
|
|
101
|
+
*
|
|
102
|
+
* @returns True if direction is locked and cannot be changed automatically
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* if (player.directionFixed) {
|
|
107
|
+
* // Direction is locked, won't change automatically
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
get directionFixed(): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Set whether direction changes are locked
|
|
114
|
+
*
|
|
115
|
+
* When set to true, the player's direction will not change automatically
|
|
116
|
+
* during movement or from physics engine callbacks.
|
|
117
|
+
*
|
|
118
|
+
* @param value - True to lock direction, false to allow automatic changes
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* // Lock direction during a special animation
|
|
123
|
+
* player.directionFixed = true;
|
|
124
|
+
* player.setAnimation('attack');
|
|
125
|
+
* // ... later
|
|
126
|
+
* player.directionFixed = false;
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
set directionFixed(value: boolean);
|
|
130
|
+
/**
|
|
131
|
+
* Get whether animation changes are locked
|
|
132
|
+
*
|
|
133
|
+
* @returns True if animation is locked and cannot be changed automatically
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* if (player.animationFixed) {
|
|
138
|
+
* // Animation is locked, won't change automatically
|
|
139
|
+
* }
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
get animationFixed(): boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Set whether animation changes are locked
|
|
145
|
+
*
|
|
146
|
+
* When set to true, the player's animation will not change automatically
|
|
147
|
+
* during movement or from physics engine callbacks.
|
|
148
|
+
*
|
|
149
|
+
* @param value - True to lock animation, false to allow automatic changes
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* // Lock animation during a special skill
|
|
154
|
+
* player.animationFixed = true;
|
|
155
|
+
* player.setAnimation('skill');
|
|
156
|
+
* // ... later
|
|
157
|
+
* player.animationFixed = false;
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
set animationFixed(value: boolean);
|
|
97
161
|
pendingInputs: any[];
|
|
98
162
|
/**
|
|
99
163
|
* Change the player's facing direction
|
|
@@ -103,12 +167,18 @@ export declare abstract class RpgCommonPlayer {
|
|
|
103
167
|
* intends to move in a specific direction, not when they are pushed
|
|
104
168
|
* by physics or sliding.
|
|
105
169
|
*
|
|
170
|
+
* If `directionFixed` is true, this method will not change the direction.
|
|
171
|
+
*
|
|
106
172
|
* @param direction - The new direction to face
|
|
107
173
|
*
|
|
108
174
|
* @example
|
|
109
175
|
* ```ts
|
|
110
176
|
* // Player presses right arrow key
|
|
111
177
|
* player.changeDirection(Direction.Right);
|
|
178
|
+
*
|
|
179
|
+
* // Lock direction to prevent automatic changes
|
|
180
|
+
* player.directionFixed = true;
|
|
181
|
+
* player.changeDirection(Direction.Up); // This will be ignored
|
|
112
182
|
* ```
|
|
113
183
|
*/
|
|
114
184
|
changeDirection(direction: Direction): void;
|
package/dist/index.js
CHANGED
|
@@ -2542,8 +2542,81 @@ class RpgCommonPlayer {
|
|
|
2542
2542
|
this.isConnected = signal(false);
|
|
2543
2543
|
// Store intended movement direction (not synced, only used locally)
|
|
2544
2544
|
this._intendedDirection = null;
|
|
2545
|
+
// Direction and animation locking (server-side only, not synced)
|
|
2546
|
+
this._directionFixed = signal(false);
|
|
2547
|
+
this._animationFixed = signal(false);
|
|
2545
2548
|
this.pendingInputs = [];
|
|
2546
2549
|
}
|
|
2550
|
+
/**
|
|
2551
|
+
* Get whether direction changes are locked
|
|
2552
|
+
*
|
|
2553
|
+
* @returns True if direction is locked and cannot be changed automatically
|
|
2554
|
+
*
|
|
2555
|
+
* @example
|
|
2556
|
+
* ```ts
|
|
2557
|
+
* if (player.directionFixed) {
|
|
2558
|
+
* // Direction is locked, won't change automatically
|
|
2559
|
+
* }
|
|
2560
|
+
* ```
|
|
2561
|
+
*/
|
|
2562
|
+
get directionFixed() {
|
|
2563
|
+
return this._directionFixed();
|
|
2564
|
+
}
|
|
2565
|
+
/**
|
|
2566
|
+
* Set whether direction changes are locked
|
|
2567
|
+
*
|
|
2568
|
+
* When set to true, the player's direction will not change automatically
|
|
2569
|
+
* during movement or from physics engine callbacks.
|
|
2570
|
+
*
|
|
2571
|
+
* @param value - True to lock direction, false to allow automatic changes
|
|
2572
|
+
*
|
|
2573
|
+
* @example
|
|
2574
|
+
* ```ts
|
|
2575
|
+
* // Lock direction during a special animation
|
|
2576
|
+
* player.directionFixed = true;
|
|
2577
|
+
* player.setAnimation('attack');
|
|
2578
|
+
* // ... later
|
|
2579
|
+
* player.directionFixed = false;
|
|
2580
|
+
* ```
|
|
2581
|
+
*/
|
|
2582
|
+
set directionFixed(value) {
|
|
2583
|
+
this._directionFixed.set(value);
|
|
2584
|
+
}
|
|
2585
|
+
/**
|
|
2586
|
+
* Get whether animation changes are locked
|
|
2587
|
+
*
|
|
2588
|
+
* @returns True if animation is locked and cannot be changed automatically
|
|
2589
|
+
*
|
|
2590
|
+
* @example
|
|
2591
|
+
* ```ts
|
|
2592
|
+
* if (player.animationFixed) {
|
|
2593
|
+
* // Animation is locked, won't change automatically
|
|
2594
|
+
* }
|
|
2595
|
+
* ```
|
|
2596
|
+
*/
|
|
2597
|
+
get animationFixed() {
|
|
2598
|
+
return this._animationFixed();
|
|
2599
|
+
}
|
|
2600
|
+
/**
|
|
2601
|
+
* Set whether animation changes are locked
|
|
2602
|
+
*
|
|
2603
|
+
* When set to true, the player's animation will not change automatically
|
|
2604
|
+
* during movement or from physics engine callbacks.
|
|
2605
|
+
*
|
|
2606
|
+
* @param value - True to lock animation, false to allow automatic changes
|
|
2607
|
+
*
|
|
2608
|
+
* @example
|
|
2609
|
+
* ```ts
|
|
2610
|
+
* // Lock animation during a special skill
|
|
2611
|
+
* player.animationFixed = true;
|
|
2612
|
+
* player.setAnimation('skill');
|
|
2613
|
+
* // ... later
|
|
2614
|
+
* player.animationFixed = false;
|
|
2615
|
+
* ```
|
|
2616
|
+
*/
|
|
2617
|
+
set animationFixed(value) {
|
|
2618
|
+
this._animationFixed.set(value);
|
|
2619
|
+
}
|
|
2547
2620
|
/**
|
|
2548
2621
|
* Change the player's facing direction
|
|
2549
2622
|
*
|
|
@@ -2551,6 +2624,8 @@ class RpgCommonPlayer {
|
|
|
2551
2624
|
* and directional abilities. This should be called when the player
|
|
2552
2625
|
* intends to move in a specific direction, not when they are pushed
|
|
2553
2626
|
* by physics or sliding.
|
|
2627
|
+
*
|
|
2628
|
+
* If `directionFixed` is true, this method will not change the direction.
|
|
2554
2629
|
*
|
|
2555
2630
|
* @param direction - The new direction to face
|
|
2556
2631
|
*
|
|
@@ -2558,9 +2633,16 @@ class RpgCommonPlayer {
|
|
|
2558
2633
|
* ```ts
|
|
2559
2634
|
* // Player presses right arrow key
|
|
2560
2635
|
* player.changeDirection(Direction.Right);
|
|
2636
|
+
*
|
|
2637
|
+
* // Lock direction to prevent automatic changes
|
|
2638
|
+
* player.directionFixed = true;
|
|
2639
|
+
* player.changeDirection(Direction.Up); // This will be ignored
|
|
2561
2640
|
* ```
|
|
2562
2641
|
*/
|
|
2563
2642
|
changeDirection(direction) {
|
|
2643
|
+
if (this._directionFixed()) {
|
|
2644
|
+
return;
|
|
2645
|
+
}
|
|
2564
2646
|
this.direction.set(direction);
|
|
2565
2647
|
}
|
|
2566
2648
|
/**
|
|
@@ -3633,6 +3715,8 @@ class Entity {
|
|
|
3633
3715
|
this.enterTileHandlers = /* @__PURE__ */ new Set();
|
|
3634
3716
|
this.leaveTileHandlers = /* @__PURE__ */ new Set();
|
|
3635
3717
|
this.canEnterTileHandlers = /* @__PURE__ */ new Set();
|
|
3718
|
+
this.collisionFilterHandlers = /* @__PURE__ */ new Set();
|
|
3719
|
+
this.resolutionFilterHandlers = /* @__PURE__ */ new Set();
|
|
3636
3720
|
this.wasMoving = this.velocity.lengthSquared() > MOVEMENT_EPSILON_SQ;
|
|
3637
3721
|
}
|
|
3638
3722
|
/**
|
|
@@ -4179,9 +4263,46 @@ class Entity {
|
|
|
4179
4263
|
this.angularVelocity = Math.sign(this.angularVelocity) * this.maxAngularVelocity;
|
|
4180
4264
|
}
|
|
4181
4265
|
}
|
|
4266
|
+
/**
|
|
4267
|
+
* Adds a collision filter to this entity
|
|
4268
|
+
*
|
|
4269
|
+
* Collision filters allow dynamic, conditional collision filtering beyond static bitmasks.
|
|
4270
|
+
* Each filter is called when checking if this entity can collide with another.
|
|
4271
|
+
* If any filter returns `false`, the collision is ignored.
|
|
4272
|
+
*
|
|
4273
|
+
* This enables scenarios like:
|
|
4274
|
+
* - Players passing through other players (`throughOtherPlayer`)
|
|
4275
|
+
* - Entities passing through all characters (`through`)
|
|
4276
|
+
* - Custom game-specific collision rules
|
|
4277
|
+
*
|
|
4278
|
+
* @param filter - Function that returns `true` to allow collision, `false` to ignore
|
|
4279
|
+
* @returns Unsubscribe function to remove the filter
|
|
4280
|
+
*
|
|
4281
|
+
* @example
|
|
4282
|
+
* ```typescript
|
|
4283
|
+
* // Allow entity to pass through other players
|
|
4284
|
+
* const unsubscribe = entity.addCollisionFilter((self, other) => {
|
|
4285
|
+
* const otherOwner = (other as any).owner;
|
|
4286
|
+
* if (otherOwner?.type === 'player') {
|
|
4287
|
+
* return false; // No collision with players
|
|
4288
|
+
* }
|
|
4289
|
+
* return true; // Collide with everything else
|
|
4290
|
+
* });
|
|
4291
|
+
*
|
|
4292
|
+
* // Later, remove the filter
|
|
4293
|
+
* unsubscribe();
|
|
4294
|
+
* ```
|
|
4295
|
+
*/
|
|
4296
|
+
addCollisionFilter(filter) {
|
|
4297
|
+
this.collisionFilterHandlers.add(filter);
|
|
4298
|
+
return () => this.collisionFilterHandlers.delete(filter);
|
|
4299
|
+
}
|
|
4182
4300
|
/**
|
|
4183
4301
|
* Checks if this entity can collide with another entity
|
|
4184
4302
|
*
|
|
4303
|
+
* First checks collision masks (bitmask filtering), then executes all registered
|
|
4304
|
+
* collision filters. If any filter returns `false`, the collision is ignored.
|
|
4305
|
+
*
|
|
4185
4306
|
* @param other - Other entity to check
|
|
4186
4307
|
* @returns True if collision is possible
|
|
4187
4308
|
*/
|
|
@@ -4190,7 +4311,77 @@ class Entity {
|
|
|
4190
4311
|
const maskA = this.collisionMask;
|
|
4191
4312
|
const categoryB = other.collisionCategory;
|
|
4192
4313
|
const maskB = other.collisionMask;
|
|
4193
|
-
|
|
4314
|
+
if ((categoryA & maskB) === 0 || (categoryB & maskA) === 0) {
|
|
4315
|
+
return false;
|
|
4316
|
+
}
|
|
4317
|
+
for (const filter of this.collisionFilterHandlers) {
|
|
4318
|
+
if (!filter(this, other)) {
|
|
4319
|
+
return false;
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
for (const filter of other.collisionFilterHandlers) {
|
|
4323
|
+
if (!filter(other, this)) {
|
|
4324
|
+
return false;
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
return true;
|
|
4328
|
+
}
|
|
4329
|
+
/**
|
|
4330
|
+
* Adds a resolution filter to this entity
|
|
4331
|
+
*
|
|
4332
|
+
* Resolution filters determine whether a collision should be **resolved** (blocking)
|
|
4333
|
+
* or just **detected** (notification only). Unlike collision filters which prevent
|
|
4334
|
+
* detection entirely, resolution filters allow collision events to fire while
|
|
4335
|
+
* optionally skipping the physical blocking.
|
|
4336
|
+
*
|
|
4337
|
+
* This enables scenarios like:
|
|
4338
|
+
* - Players passing through other players but still triggering touch events
|
|
4339
|
+
* - Entities passing through characters but still calling onPlayerTouch hooks
|
|
4340
|
+
* - Ghost mode where collisions are detected but not resolved
|
|
4341
|
+
*
|
|
4342
|
+
* @param filter - Function that returns `true` to resolve (block), `false` to skip
|
|
4343
|
+
* @returns Unsubscribe function to remove the filter
|
|
4344
|
+
*
|
|
4345
|
+
* @example
|
|
4346
|
+
* ```typescript
|
|
4347
|
+
* // Allow entity to pass through players but still trigger events
|
|
4348
|
+
* const unsubscribe = entity.addResolutionFilter((self, other) => {
|
|
4349
|
+
* const otherOwner = (other as any).owner;
|
|
4350
|
+
* if (otherOwner?.type === 'player') {
|
|
4351
|
+
* return false; // Pass through but events still fire
|
|
4352
|
+
* }
|
|
4353
|
+
* return true; // Block other entities
|
|
4354
|
+
* });
|
|
4355
|
+
*
|
|
4356
|
+
* // Later, remove the filter
|
|
4357
|
+
* unsubscribe();
|
|
4358
|
+
* ```
|
|
4359
|
+
*/
|
|
4360
|
+
addResolutionFilter(filter) {
|
|
4361
|
+
this.resolutionFilterHandlers.add(filter);
|
|
4362
|
+
return () => this.resolutionFilterHandlers.delete(filter);
|
|
4363
|
+
}
|
|
4364
|
+
/**
|
|
4365
|
+
* Checks if this entity should resolve (block) a collision with another entity
|
|
4366
|
+
*
|
|
4367
|
+
* This is called by the CollisionResolver to determine if the collision should
|
|
4368
|
+
* result in physical blocking or just notification.
|
|
4369
|
+
*
|
|
4370
|
+
* @param other - Other entity to check
|
|
4371
|
+
* @returns True if collision should be resolved (blocking), false to pass through
|
|
4372
|
+
*/
|
|
4373
|
+
shouldResolveCollisionWith(other) {
|
|
4374
|
+
for (const filter of this.resolutionFilterHandlers) {
|
|
4375
|
+
if (!filter(this, other)) {
|
|
4376
|
+
return false;
|
|
4377
|
+
}
|
|
4378
|
+
}
|
|
4379
|
+
for (const filter of other.resolutionFilterHandlers) {
|
|
4380
|
+
if (!filter(other, this)) {
|
|
4381
|
+
return false;
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
return true;
|
|
4194
4385
|
}
|
|
4195
4386
|
/**
|
|
4196
4387
|
* @internal
|
|
@@ -6344,6 +6535,9 @@ class CollisionResolver {
|
|
|
6344
6535
|
* Resolves a collision
|
|
6345
6536
|
*
|
|
6346
6537
|
* Separates entities and applies collision response.
|
|
6538
|
+
* First checks if the collision should be resolved using resolution filters.
|
|
6539
|
+
* If any entity has a resolution filter that returns false, the collision
|
|
6540
|
+
* is skipped (entities pass through) but events are still fired.
|
|
6347
6541
|
*
|
|
6348
6542
|
* @param collision - Collision information to resolve
|
|
6349
6543
|
*/
|
|
@@ -6352,6 +6546,9 @@ class CollisionResolver {
|
|
|
6352
6546
|
if (depth < this.config.minPenetrationDepth) {
|
|
6353
6547
|
return;
|
|
6354
6548
|
}
|
|
6549
|
+
if (!entityA.shouldResolveCollisionWith(entityB)) {
|
|
6550
|
+
return;
|
|
6551
|
+
}
|
|
6355
6552
|
this.separateEntities(entityA, entityB, normal, depth);
|
|
6356
6553
|
this.resolveVelocities(entityA, entityB, normal);
|
|
6357
6554
|
}
|
|
@@ -7515,20 +7712,64 @@ let MovementManager$1 = class MovementManager {
|
|
|
7515
7712
|
}
|
|
7516
7713
|
/**
|
|
7517
7714
|
* Adds a movement strategy to an entity.
|
|
7715
|
+
*
|
|
7716
|
+
* Returns a Promise that resolves when the movement completes (when `isFinished()` returns true).
|
|
7717
|
+
* If the strategy doesn't implement `isFinished()`, the Promise resolves immediately after adding.
|
|
7518
7718
|
*
|
|
7519
7719
|
* @param target - Entity instance or entity UUID when a resolver is configured
|
|
7520
7720
|
* @param strategy - Strategy to execute
|
|
7721
|
+
* @param options - Optional callbacks for movement lifecycle events
|
|
7722
|
+
* @returns Promise that resolves when the movement completes
|
|
7723
|
+
*
|
|
7724
|
+
* @example
|
|
7725
|
+
* ```typescript
|
|
7726
|
+
* // Simple usage - fire and forget
|
|
7727
|
+
* manager.add(player, new Dash(8, { x: 1, y: 0 }, 200));
|
|
7728
|
+
*
|
|
7729
|
+
* // Wait for completion
|
|
7730
|
+
* await manager.add(player, new Dash(8, { x: 1, y: 0 }, 200));
|
|
7731
|
+
* console.log('Dash finished!');
|
|
7732
|
+
*
|
|
7733
|
+
* // With callbacks
|
|
7734
|
+
* await manager.add(player, new Knockback({ x: -1, y: 0 }, 5, 300), {
|
|
7735
|
+
* onStart: () => {
|
|
7736
|
+
* player.directionFixed = true;
|
|
7737
|
+
* player.animationFixed = true;
|
|
7738
|
+
* },
|
|
7739
|
+
* onComplete: () => {
|
|
7740
|
+
* player.directionFixed = false;
|
|
7741
|
+
* player.animationFixed = false;
|
|
7742
|
+
* }
|
|
7743
|
+
* });
|
|
7744
|
+
* ```
|
|
7521
7745
|
*/
|
|
7522
|
-
add(target, strategy) {
|
|
7746
|
+
add(target, strategy, options) {
|
|
7523
7747
|
const body = this.resolveTarget(target);
|
|
7524
7748
|
const key = body.id;
|
|
7525
7749
|
if (!this.entries.has(key)) {
|
|
7526
7750
|
this.entries.set(key, { body, strategies: [] });
|
|
7527
7751
|
}
|
|
7528
|
-
|
|
7752
|
+
if (!strategy.isFinished) {
|
|
7753
|
+
const entry = { strategy, started: false };
|
|
7754
|
+
if (options) {
|
|
7755
|
+
entry.options = options;
|
|
7756
|
+
}
|
|
7757
|
+
this.entries.get(key).strategies.push(entry);
|
|
7758
|
+
return Promise.resolve();
|
|
7759
|
+
}
|
|
7760
|
+
return new Promise((resolve) => {
|
|
7761
|
+
const entry = { strategy, resolve, started: false };
|
|
7762
|
+
if (options) {
|
|
7763
|
+
entry.options = options;
|
|
7764
|
+
}
|
|
7765
|
+
this.entries.get(key).strategies.push(entry);
|
|
7766
|
+
});
|
|
7529
7767
|
}
|
|
7530
7768
|
/**
|
|
7531
7769
|
* Removes a specific strategy from an entity.
|
|
7770
|
+
*
|
|
7771
|
+
* Note: This will NOT trigger the onComplete callback or resolve the Promise.
|
|
7772
|
+
* Use this when you want to cancel a movement without completion.
|
|
7532
7773
|
*
|
|
7533
7774
|
* @param target - Entity instance or identifier
|
|
7534
7775
|
* @param strategy - Strategy instance to remove
|
|
@@ -7540,7 +7781,7 @@ let MovementManager$1 = class MovementManager {
|
|
|
7540
7781
|
if (!entry) {
|
|
7541
7782
|
return false;
|
|
7542
7783
|
}
|
|
7543
|
-
const index = entry.strategies.
|
|
7784
|
+
const index = entry.strategies.findIndex((e) => e.strategy === strategy);
|
|
7544
7785
|
if (index === -1) {
|
|
7545
7786
|
return false;
|
|
7546
7787
|
}
|
|
@@ -7619,13 +7860,21 @@ let MovementManager$1 = class MovementManager {
|
|
|
7619
7860
|
getStrategies(target) {
|
|
7620
7861
|
const body = this.resolveTarget(target);
|
|
7621
7862
|
const entry = this.entries.get(body.id);
|
|
7622
|
-
return entry ?
|
|
7863
|
+
return entry ? entry.strategies.map((e) => e.strategy) : [];
|
|
7623
7864
|
}
|
|
7624
7865
|
/**
|
|
7625
7866
|
* Updates all registered strategies.
|
|
7626
7867
|
*
|
|
7627
7868
|
* Call this method once per frame before `PhysicsEngine.step()` so that the
|
|
7628
7869
|
* physics simulation integrates the velocities that strategies configure.
|
|
7870
|
+
*
|
|
7871
|
+
* This method handles the movement lifecycle:
|
|
7872
|
+
* - Triggers `onStart` callback on first update
|
|
7873
|
+
* - Calls `strategy.update()` each frame
|
|
7874
|
+
* - When `isFinished()` returns true:
|
|
7875
|
+
* - Calls `strategy.onFinished()` if defined
|
|
7876
|
+
* - Triggers `onComplete` callback
|
|
7877
|
+
* - Resolves the Promise returned by `add()`
|
|
7629
7878
|
*
|
|
7630
7879
|
* @param dt - Time delta in seconds
|
|
7631
7880
|
*/
|
|
@@ -7637,14 +7886,22 @@ let MovementManager$1 = class MovementManager {
|
|
|
7637
7886
|
continue;
|
|
7638
7887
|
}
|
|
7639
7888
|
for (let i = strategies.length - 1; i >= 0; i -= 1) {
|
|
7640
|
-
const
|
|
7641
|
-
if (!
|
|
7889
|
+
const strategyEntry = strategies[i];
|
|
7890
|
+
if (!strategyEntry) {
|
|
7642
7891
|
continue;
|
|
7643
7892
|
}
|
|
7644
|
-
|
|
7645
|
-
if (
|
|
7893
|
+
const { strategy, options, resolve } = strategyEntry;
|
|
7894
|
+
if (!strategyEntry.started) {
|
|
7895
|
+
strategyEntry.started = true;
|
|
7896
|
+
options?.onStart?.();
|
|
7897
|
+
}
|
|
7898
|
+
strategy.update(body, dt);
|
|
7899
|
+
const isFinished = strategy.isFinished?.();
|
|
7900
|
+
if (isFinished) {
|
|
7646
7901
|
strategies.splice(i, 1);
|
|
7647
|
-
|
|
7902
|
+
strategy.onFinished?.();
|
|
7903
|
+
options?.onComplete?.();
|
|
7904
|
+
resolve?.();
|
|
7648
7905
|
}
|
|
7649
7906
|
}
|
|
7650
7907
|
if (strategies.length === 0) {
|
|
@@ -9449,8 +9706,16 @@ class MovementManager {
|
|
|
9449
9706
|
get core() {
|
|
9450
9707
|
return this.physicProvider().getMovementManager();
|
|
9451
9708
|
}
|
|
9452
|
-
|
|
9453
|
-
|
|
9709
|
+
/**
|
|
9710
|
+
* Adds a movement strategy and returns a Promise that resolves when it completes.
|
|
9711
|
+
*
|
|
9712
|
+
* @param id - Entity identifier
|
|
9713
|
+
* @param strategy - Movement strategy to add
|
|
9714
|
+
* @param options - Optional callbacks for movement lifecycle events
|
|
9715
|
+
* @returns Promise that resolves when the movement completes
|
|
9716
|
+
*/
|
|
9717
|
+
add(id, strategy, options) {
|
|
9718
|
+
return this.core.add(id, strategy, options);
|
|
9454
9719
|
}
|
|
9455
9720
|
remove(id, strategy) {
|
|
9456
9721
|
return this.core.remove(id, strategy);
|
|
@@ -10303,11 +10568,14 @@ class RpgCommonMap {
|
|
|
10303
10568
|
const owner2 = entity.owner;
|
|
10304
10569
|
if (!owner2) return;
|
|
10305
10570
|
if (cardinalDirection === "idle") return;
|
|
10571
|
+
if (owner2.directionFixed) return;
|
|
10306
10572
|
owner2.changeDirection(cardinalDirection);
|
|
10307
10573
|
});
|
|
10308
10574
|
entity.onMovementChange(({ isMoving, intensity }) => {
|
|
10575
|
+
if (!("$send" in this)) return;
|
|
10309
10576
|
const owner2 = entity.owner;
|
|
10310
10577
|
if (!owner2) return;
|
|
10578
|
+
if (owner2.animationFixed) return;
|
|
10311
10579
|
const LOW_INTENSITY_THRESHOLD = 10;
|
|
10312
10580
|
const hasSetAnimation = typeof owner2.setAnimation === "function";
|
|
10313
10581
|
const animationNameSignal = owner2.animationName;
|
|
@@ -10344,6 +10612,56 @@ class RpgCommonMap {
|
|
|
10344
10612
|
owner.applyFrames?.();
|
|
10345
10613
|
}
|
|
10346
10614
|
});
|
|
10615
|
+
entity.addResolutionFilter((self, other) => {
|
|
10616
|
+
const selfOwner = self.owner;
|
|
10617
|
+
const otherOwner = other.owner;
|
|
10618
|
+
if (!selfOwner || !otherOwner) {
|
|
10619
|
+
return true;
|
|
10620
|
+
}
|
|
10621
|
+
if (selfOwner.z() !== otherOwner.z()) {
|
|
10622
|
+
return false;
|
|
10623
|
+
}
|
|
10624
|
+
if (typeof selfOwner._through === "function") {
|
|
10625
|
+
try {
|
|
10626
|
+
if (selfOwner._through() === true) {
|
|
10627
|
+
return false;
|
|
10628
|
+
}
|
|
10629
|
+
} catch {
|
|
10630
|
+
}
|
|
10631
|
+
} else if (selfOwner.through === true) {
|
|
10632
|
+
return false;
|
|
10633
|
+
}
|
|
10634
|
+
const playersMap = this.players();
|
|
10635
|
+
const eventsMap = this.events();
|
|
10636
|
+
const isSelfPlayer = !!playersMap[self.uuid];
|
|
10637
|
+
const isOtherPlayer = !!playersMap[other.uuid];
|
|
10638
|
+
const isOtherEvent = !!eventsMap[other.uuid];
|
|
10639
|
+
if (isSelfPlayer && isOtherPlayer) {
|
|
10640
|
+
if (typeof selfOwner._throughOtherPlayer === "function") {
|
|
10641
|
+
try {
|
|
10642
|
+
if (selfOwner._throughOtherPlayer() === true) {
|
|
10643
|
+
return false;
|
|
10644
|
+
}
|
|
10645
|
+
} catch {
|
|
10646
|
+
}
|
|
10647
|
+
} else if (selfOwner.throughOtherPlayer === true) {
|
|
10648
|
+
return false;
|
|
10649
|
+
}
|
|
10650
|
+
}
|
|
10651
|
+
if (isSelfPlayer && isOtherEvent) {
|
|
10652
|
+
if (typeof selfOwner._throughEvent === "function") {
|
|
10653
|
+
try {
|
|
10654
|
+
if (selfOwner._throughEvent() === true) {
|
|
10655
|
+
return false;
|
|
10656
|
+
}
|
|
10657
|
+
} catch {
|
|
10658
|
+
}
|
|
10659
|
+
} else if (selfOwner.throughEvent === true) {
|
|
10660
|
+
return false;
|
|
10661
|
+
}
|
|
10662
|
+
}
|
|
10663
|
+
return true;
|
|
10664
|
+
});
|
|
10347
10665
|
return id;
|
|
10348
10666
|
}
|
|
10349
10667
|
/**
|