@rpgjs/server 5.0.0-beta.5 → 5.0.0-beta.6
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/index.js +11 -12
- package/dist/index.js.map +1 -1
- package/dist/{module-BmvXIvlE.js → module-Dy124Jyk.js} +432 -239
- package/dist/module-Dy124Jyk.js.map +1 -0
- package/dist/node/index.js +1 -1
- package/package.json +4 -4
- package/src/Player/Player.ts +1 -1
- package/src/rooms/map.ts +21 -15
- package/tests/action-interaction.spec.ts +97 -0
- package/tests/item.spec.ts +72 -1
- package/tests/move.spec.ts +65 -1
- package/tests/world-maps.spec.ts +4 -0
- package/dist/module-BmvXIvlE.js.map +0 -1
|
@@ -2461,6 +2461,24 @@ __decorate([sync(), __decorateMetadata("design:type", Object)], Skill.prototype,
|
|
|
2461
2461
|
__decorate([sync(), __decorateMetadata("design:type", Object)], Skill.prototype, "icon", void 0);
|
|
2462
2462
|
//#endregion
|
|
2463
2463
|
//#region ../common/src/Player.ts
|
|
2464
|
+
var readReactiveValue = (value) => {
|
|
2465
|
+
if (typeof value === "function" && value.observable) return value();
|
|
2466
|
+
return value;
|
|
2467
|
+
};
|
|
2468
|
+
var toCloneableSyncValue = (value, seen = /* @__PURE__ */ new WeakSet()) => {
|
|
2469
|
+
const resolved = readReactiveValue(value);
|
|
2470
|
+
if (resolved == null || typeof resolved !== "object") return typeof resolved === "function" ? void 0 : resolved;
|
|
2471
|
+
if (seen.has(resolved)) return;
|
|
2472
|
+
seen.add(resolved);
|
|
2473
|
+
if (Array.isArray(resolved)) return resolved.map((item) => toCloneableSyncValue(item, seen)).filter((item) => item !== void 0);
|
|
2474
|
+
const output = {};
|
|
2475
|
+
for (const [key, child] of Object.entries(resolved)) {
|
|
2476
|
+
if (key.startsWith("$") || key === "_itemInstance" || key === "_subject" || key === "observable" || key === "options") continue;
|
|
2477
|
+
const childValue = toCloneableSyncValue(child, seen);
|
|
2478
|
+
if (childValue !== void 0) output[key.startsWith("__") ? key.slice(2) : key] = childValue;
|
|
2479
|
+
}
|
|
2480
|
+
return output;
|
|
2481
|
+
};
|
|
2464
2482
|
var Direction = /* @__PURE__ */ function(Direction) {
|
|
2465
2483
|
Direction["Up"] = "up";
|
|
2466
2484
|
Direction["Down"] = "down";
|
|
@@ -2644,8 +2662,11 @@ __decorate([sync(), __decorateMetadata("design:type", Object)], RpgCommonPlayer.
|
|
|
2644
2662
|
__decorate([sync(), __decorateMetadata("design:type", Object)], RpgCommonPlayer.prototype, "_exp", void 0);
|
|
2645
2663
|
__decorate([sync(), __decorateMetadata("design:type", Object)], RpgCommonPlayer.prototype, "_level", void 0);
|
|
2646
2664
|
__decorate([sync(), __decorateMetadata("design:type", Object)], RpgCommonPlayer.prototype, "_class", void 0);
|
|
2647
|
-
__decorate([sync(
|
|
2648
|
-
|
|
2665
|
+
__decorate([sync({
|
|
2666
|
+
classType: Item,
|
|
2667
|
+
transform: toCloneableSyncValue
|
|
2668
|
+
}), __decorateMetadata("design:type", Object)], RpgCommonPlayer.prototype, "items", void 0);
|
|
2669
|
+
__decorate([sync({ transform: toCloneableSyncValue }), __decorateMetadata("design:type", Object)], RpgCommonPlayer.prototype, "equipments", void 0);
|
|
2649
2670
|
__decorate([sync(), __decorateMetadata("design:type", Object)], RpgCommonPlayer.prototype, "states", void 0);
|
|
2650
2671
|
__decorate([sync(Skill), __decorateMetadata("design:type", Object)], RpgCommonPlayer.prototype, "skills", void 0);
|
|
2651
2672
|
__decorate([sync(), __decorateMetadata("design:type", Object)], RpgCommonPlayer.prototype, "_effects", void 0);
|
|
@@ -4766,6 +4787,18 @@ var CapsuleCollider = class CapsuleCollider {
|
|
|
4766
4787
|
}
|
|
4767
4788
|
};
|
|
4768
4789
|
//#endregion
|
|
4790
|
+
//#region ../physic/src/collision/collider-cache.ts
|
|
4791
|
+
var colliderCache = /* @__PURE__ */ new WeakMap();
|
|
4792
|
+
function getCachedCollider(entity) {
|
|
4793
|
+
return colliderCache.get(entity);
|
|
4794
|
+
}
|
|
4795
|
+
function setCachedCollider(entity, collider) {
|
|
4796
|
+
colliderCache.set(entity, collider);
|
|
4797
|
+
}
|
|
4798
|
+
function invalidateCollider(entity) {
|
|
4799
|
+
colliderCache.delete(entity);
|
|
4800
|
+
}
|
|
4801
|
+
//#endregion
|
|
4769
4802
|
//#region ../physic/src/collision/PolygonCollider.ts
|
|
4770
4803
|
/**
|
|
4771
4804
|
* Weak registry to attach polygon configurations to entities
|
|
@@ -4785,6 +4818,7 @@ var entityToPolygonConfig = /* @__PURE__ */ new WeakMap();
|
|
|
4785
4818
|
*/
|
|
4786
4819
|
function assignPolygonCollider(entity, config) {
|
|
4787
4820
|
entityToPolygonConfig.set(entity, config);
|
|
4821
|
+
invalidateCollider(entity);
|
|
4788
4822
|
}
|
|
4789
4823
|
/**
|
|
4790
4824
|
* Polygon collider implementation (convex via SAT; concave via convex parts)
|
|
@@ -5090,7 +5124,6 @@ function polygonCentroid(poly) {
|
|
|
5090
5124
|
}
|
|
5091
5125
|
//#endregion
|
|
5092
5126
|
//#region ../physic/src/collision/detector.ts
|
|
5093
|
-
var colliderCache = /* @__PURE__ */ new WeakMap();
|
|
5094
5127
|
/**
|
|
5095
5128
|
* Collision detector
|
|
5096
5129
|
*
|
|
@@ -5104,14 +5137,14 @@ var colliderCache = /* @__PURE__ */ new WeakMap();
|
|
|
5104
5137
|
* @returns Appropriate collider instance
|
|
5105
5138
|
*/
|
|
5106
5139
|
function createCollider(entity) {
|
|
5107
|
-
const cached =
|
|
5140
|
+
const cached = getCachedCollider(entity);
|
|
5108
5141
|
if (cached) return cached;
|
|
5109
5142
|
let collider = null;
|
|
5110
5143
|
if (entityToPolygonConfig.has(entity)) collider = new PolygonCollider(entity);
|
|
5111
5144
|
else if (entity.capsule) collider = new CapsuleCollider(entity);
|
|
5112
5145
|
else if (entity.radius > 0) collider = new CircleCollider(entity);
|
|
5113
5146
|
else if (entity.width > 0 && entity.height > 0) collider = new AABBCollider(entity);
|
|
5114
|
-
if (collider)
|
|
5147
|
+
if (collider) setCachedCollider(entity, collider);
|
|
5115
5148
|
return collider;
|
|
5116
5149
|
}
|
|
5117
5150
|
/**
|
|
@@ -5127,7 +5160,24 @@ function testCollision(entityA, entityB) {
|
|
|
5127
5160
|
const colliderA = createCollider(entityA);
|
|
5128
5161
|
const colliderB = createCollider(entityB);
|
|
5129
5162
|
if (!colliderA || !colliderB) return null;
|
|
5130
|
-
|
|
5163
|
+
const directCollision = colliderA.testCollision(colliderB);
|
|
5164
|
+
if (directCollision) return directCollision;
|
|
5165
|
+
const reverseCollision = colliderB.testCollision(colliderA);
|
|
5166
|
+
if (!reverseCollision) return null;
|
|
5167
|
+
return reverseCollisionInfo(reverseCollision);
|
|
5168
|
+
}
|
|
5169
|
+
function reverseCollisionInfo(collision) {
|
|
5170
|
+
return {
|
|
5171
|
+
entityA: collision.entityB,
|
|
5172
|
+
entityB: collision.entityA,
|
|
5173
|
+
contacts: collision.contacts.map((contact) => ({
|
|
5174
|
+
point: contact.point,
|
|
5175
|
+
normal: contact.normal.mul(-1),
|
|
5176
|
+
depth: contact.depth
|
|
5177
|
+
})),
|
|
5178
|
+
normal: collision.normal.mul(-1),
|
|
5179
|
+
depth: collision.depth
|
|
5180
|
+
};
|
|
5131
5181
|
}
|
|
5132
5182
|
//#endregion
|
|
5133
5183
|
//#region ../physic/src/collision/spatial-hash.ts
|
|
@@ -5476,148 +5526,6 @@ var Ray = class {
|
|
|
5476
5526
|
}
|
|
5477
5527
|
};
|
|
5478
5528
|
//#endregion
|
|
5479
|
-
//#region ../physic/src/collision/raycast.ts
|
|
5480
|
-
/**
|
|
5481
|
-
* Casts a ray in the world using the spatial partition for broad-phase, then shape-specific narrow-phase.
|
|
5482
|
-
* Direction will be normalized internally.
|
|
5483
|
-
*
|
|
5484
|
-
* @param partition - Spatial partition to query
|
|
5485
|
-
* @param origin - Ray origin
|
|
5486
|
-
* @param direction - Ray direction (any length)
|
|
5487
|
-
* @param maxDistance - Maximum distance
|
|
5488
|
-
* @param mask - Optional collision mask (layer)
|
|
5489
|
-
* @param filter - Optional filter function (return true to include entity)
|
|
5490
|
-
* @returns Nearest hit or null
|
|
5491
|
-
*
|
|
5492
|
-
* @example
|
|
5493
|
-
* ```typescript
|
|
5494
|
-
* const hit = raycast(worldPartition, new Vector2(0,0), new Vector2(1,0), 1000);
|
|
5495
|
-
* if (hit) {
|
|
5496
|
-
* // handle
|
|
5497
|
-
* }
|
|
5498
|
-
* ```
|
|
5499
|
-
*/
|
|
5500
|
-
function raycast(partition, origin, direction, maxDistance, mask, filter) {
|
|
5501
|
-
const dir = direction.length() > 0 ? direction.normalize() : new Vector2(1, 0);
|
|
5502
|
-
const end = origin.add(dir.mul(maxDistance));
|
|
5503
|
-
const candidates = partition.raycast(new Ray(origin, dir, maxDistance), mask, filter);
|
|
5504
|
-
if (candidates) return candidates;
|
|
5505
|
-
const bounds = new AABB(Math.min(origin.x, end.x), Math.min(origin.y, end.y), Math.max(origin.x, end.x), Math.max(origin.y, end.y));
|
|
5506
|
-
const entities = partition.queryAABB(bounds);
|
|
5507
|
-
let best = null;
|
|
5508
|
-
for (const e of entities) {
|
|
5509
|
-
if (mask !== void 0 && (e.collisionCategory & mask) === 0) continue;
|
|
5510
|
-
if (filter && !filter(e)) continue;
|
|
5511
|
-
const collider = createCollider(e);
|
|
5512
|
-
if (!collider) continue;
|
|
5513
|
-
const hit = raycastCollider(collider, origin, dir, maxDistance);
|
|
5514
|
-
if (!hit) continue;
|
|
5515
|
-
if (!best || hit.distance < best.distance) best = hit;
|
|
5516
|
-
}
|
|
5517
|
-
return best;
|
|
5518
|
-
}
|
|
5519
|
-
function raycastCollider(collider, origin, dir, maxDistance) {
|
|
5520
|
-
if (collider instanceof CircleCollider) return raycastCircle(collider, origin, dir, maxDistance);
|
|
5521
|
-
if (collider instanceof AABBCollider) return raycastAABB(collider, origin, dir, maxDistance);
|
|
5522
|
-
if (collider instanceof PolygonCollider) return raycastPolygon(collider, origin, dir, maxDistance);
|
|
5523
|
-
return null;
|
|
5524
|
-
}
|
|
5525
|
-
function raycastCircle(circle, origin, dir, maxDistance) {
|
|
5526
|
-
const c = circle.getCenter();
|
|
5527
|
-
const r = circle.getRadius();
|
|
5528
|
-
const m = origin.sub(c);
|
|
5529
|
-
const b = m.dot(dir);
|
|
5530
|
-
const cval = m.dot(m) - r * r;
|
|
5531
|
-
if (cval > 0 && b > 0) return null;
|
|
5532
|
-
const discr = b * b - cval;
|
|
5533
|
-
if (discr < 0) return null;
|
|
5534
|
-
const t = -b - Math.sqrt(discr);
|
|
5535
|
-
if (t < 0) return null;
|
|
5536
|
-
if (t > maxDistance) return null;
|
|
5537
|
-
const point = origin.add(dir.mul(t));
|
|
5538
|
-
const normal = point.sub(c).normalize();
|
|
5539
|
-
return {
|
|
5540
|
-
entity: circle.getEntity(),
|
|
5541
|
-
point,
|
|
5542
|
-
normal,
|
|
5543
|
-
distance: t
|
|
5544
|
-
};
|
|
5545
|
-
}
|
|
5546
|
-
function raycastAABB(box, origin, dir, maxDistance) {
|
|
5547
|
-
const b = box.getBounds();
|
|
5548
|
-
let tmin = 0;
|
|
5549
|
-
let tmax = maxDistance;
|
|
5550
|
-
const invDx = 1 / (dir.x === 0 ? 1e-9 : dir.x);
|
|
5551
|
-
const invDy = 1 / (dir.y === 0 ? 1e-9 : dir.y);
|
|
5552
|
-
let tx1 = (b.minX - origin.x) * invDx;
|
|
5553
|
-
let tx2 = (b.maxX - origin.x) * invDx;
|
|
5554
|
-
let ty1 = (b.minY - origin.y) * invDy;
|
|
5555
|
-
let ty2 = (b.maxY - origin.y) * invDy;
|
|
5556
|
-
const tminX = Math.min(tx1, tx2);
|
|
5557
|
-
const tmaxX = Math.max(tx1, tx2);
|
|
5558
|
-
const tminY = Math.min(ty1, ty2);
|
|
5559
|
-
const tmaxY = Math.max(ty1, ty2);
|
|
5560
|
-
tmin = Math.max(tmin, Math.max(tminX, tminY));
|
|
5561
|
-
tmax = Math.min(tmax, Math.min(tmaxX, tmaxY));
|
|
5562
|
-
if (tmax < tmin || tmin < 0 || tmin > maxDistance) return null;
|
|
5563
|
-
const point = origin.add(dir.mul(tmin));
|
|
5564
|
-
let normal;
|
|
5565
|
-
if (tmin === tminX) normal = new Vector2(dir.x > 0 ? -1 : 1, 0);
|
|
5566
|
-
else normal = new Vector2(0, dir.y > 0 ? -1 : 1);
|
|
5567
|
-
return {
|
|
5568
|
-
entity: box.getEntity(),
|
|
5569
|
-
point,
|
|
5570
|
-
normal,
|
|
5571
|
-
distance: tmin
|
|
5572
|
-
};
|
|
5573
|
-
}
|
|
5574
|
-
function raycastPolygon(poly, origin, dir, maxDistance) {
|
|
5575
|
-
const end = origin.add(dir.mul(maxDistance));
|
|
5576
|
-
let bestT = Number.POSITIVE_INFINITY;
|
|
5577
|
-
let bestPoint = null;
|
|
5578
|
-
let bestNormal = null;
|
|
5579
|
-
const any = poly;
|
|
5580
|
-
const parts = any["getWorldParts"] ? any["getWorldParts"]() : [];
|
|
5581
|
-
for (const part of parts) for (let i = 0; i < part.length; i++) {
|
|
5582
|
-
const a = part[i];
|
|
5583
|
-
const b = part[(i + 1) % part.length];
|
|
5584
|
-
if (!a || !b) continue;
|
|
5585
|
-
const hit = segmentRayIntersection(a, b, origin, end);
|
|
5586
|
-
if (!hit) continue;
|
|
5587
|
-
const t = hit.distance;
|
|
5588
|
-
if (t >= 0 && t <= maxDistance && t < bestT) {
|
|
5589
|
-
bestT = t;
|
|
5590
|
-
bestPoint = hit.point;
|
|
5591
|
-
const edge = b.sub(a);
|
|
5592
|
-
bestNormal = new Vector2(-edge.y, edge.x).normalize();
|
|
5593
|
-
}
|
|
5594
|
-
}
|
|
5595
|
-
if (!bestPoint || !bestNormal || bestT === Number.POSITIVE_INFINITY) return null;
|
|
5596
|
-
return {
|
|
5597
|
-
entity: poly.getEntity(),
|
|
5598
|
-
point: bestPoint,
|
|
5599
|
-
normal: bestNormal,
|
|
5600
|
-
distance: bestT
|
|
5601
|
-
};
|
|
5602
|
-
}
|
|
5603
|
-
function segmentRayIntersection(a, b, r0, r1) {
|
|
5604
|
-
const v1 = r0.sub(a);
|
|
5605
|
-
const v2 = b.sub(a);
|
|
5606
|
-
const v3 = new Vector2(-(r1.y - r0.y), r1.x - r0.x);
|
|
5607
|
-
const denom = v2.dot(v3);
|
|
5608
|
-
if (Math.abs(denom) < 1e-9) return null;
|
|
5609
|
-
const t1 = v2.cross(v1) / denom;
|
|
5610
|
-
const t2 = v1.dot(v3) / denom;
|
|
5611
|
-
if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) {
|
|
5612
|
-
const hitPoint = new Vector2(r0.x + (r1.x - r0.x) * t1, r0.y + (r1.y - r0.y) * t1);
|
|
5613
|
-
return {
|
|
5614
|
-
point: hitPoint,
|
|
5615
|
-
distance: hitPoint.sub(r0).length()
|
|
5616
|
-
};
|
|
5617
|
-
}
|
|
5618
|
-
return null;
|
|
5619
|
-
}
|
|
5620
|
-
//#endregion
|
|
5621
5529
|
//#region ../physic/src/collision/resolver.ts
|
|
5622
5530
|
/**
|
|
5623
5531
|
* Collision resolver
|
|
@@ -6077,13 +5985,41 @@ var World = class {
|
|
|
6077
5985
|
*/
|
|
6078
5986
|
addEntity(entity) {
|
|
6079
5987
|
this.entities.add(entity);
|
|
6080
|
-
|
|
6081
|
-
else this.dynamicEntities.add(entity);
|
|
5988
|
+
this.syncEntityCollection(entity);
|
|
6082
5989
|
this.spatialPartition.insert(entity);
|
|
6083
5990
|
this.events.emitEntityAdded(entity);
|
|
6084
5991
|
return entity;
|
|
6085
5992
|
}
|
|
6086
5993
|
/**
|
|
5994
|
+
* Synchronizes an entity with the world's broad-phase structures.
|
|
5995
|
+
*
|
|
5996
|
+
* Call this after manually changing an entity position, dimensions, or state
|
|
5997
|
+
* outside the normal physics step.
|
|
5998
|
+
*
|
|
5999
|
+
* @param entity - Entity to synchronize
|
|
6000
|
+
*/
|
|
6001
|
+
updateEntity(entity) {
|
|
6002
|
+
if (!this.entities.has(entity)) return;
|
|
6003
|
+
invalidateCollider(entity);
|
|
6004
|
+
this.syncEntity(entity);
|
|
6005
|
+
}
|
|
6006
|
+
syncEntity(entity) {
|
|
6007
|
+
this.syncEntityCollection(entity);
|
|
6008
|
+
this.spatialPartition.update(entity);
|
|
6009
|
+
}
|
|
6010
|
+
/**
|
|
6011
|
+
* Queries entities in an AABB region.
|
|
6012
|
+
*
|
|
6013
|
+
* The returned array comes from the broad-phase partition and may contain
|
|
6014
|
+
* false positives from overlapping cells.
|
|
6015
|
+
*
|
|
6016
|
+
* @param bounds - AABB to query
|
|
6017
|
+
* @returns Array of entities in overlapping partition cells
|
|
6018
|
+
*/
|
|
6019
|
+
queryAABB(bounds) {
|
|
6020
|
+
return Array.from(this.spatialPartition.queryAABB(bounds));
|
|
6021
|
+
}
|
|
6022
|
+
/**
|
|
6087
6023
|
* Performs a raycast against all entities in the world.
|
|
6088
6024
|
*
|
|
6089
6025
|
* @param origin - Starting point of the ray
|
|
@@ -6143,13 +6079,14 @@ var World = class {
|
|
|
6143
6079
|
* Updates all entities, detects and resolves collisions.
|
|
6144
6080
|
*/
|
|
6145
6081
|
step() {
|
|
6146
|
-
this.
|
|
6082
|
+
this.refreshEntitiesInPartition();
|
|
6147
6083
|
for (const entity of this.dynamicEntities) if (!entity.isSleeping()) {
|
|
6148
6084
|
const startPos = entity.position.clone();
|
|
6149
6085
|
this.integrator.integrate(entity);
|
|
6150
6086
|
this.updateEntityTile(entity, startPos);
|
|
6151
6087
|
if (entity.continuous) this.performCCD(entity);
|
|
6152
6088
|
}
|
|
6089
|
+
this.refreshDynamicEntitiesInPartition();
|
|
6153
6090
|
let firstPassCollisions = [];
|
|
6154
6091
|
for (let iteration = 0; iteration < this.resolverIterations; iteration++) {
|
|
6155
6092
|
const collisions = this.detectCollisions();
|
|
@@ -6157,9 +6094,12 @@ var World = class {
|
|
|
6157
6094
|
if (collisions.length === 0) break;
|
|
6158
6095
|
this.sortCollisionsForDeterminism(collisions);
|
|
6159
6096
|
this.resolver.resolveAll(collisions);
|
|
6160
|
-
|
|
6097
|
+
this.refreshDynamicEntitiesInPartition();
|
|
6098
|
+
}
|
|
6099
|
+
if (this.positionQuantizationStep !== null || this.velocityQuantizationStep !== null) {
|
|
6100
|
+
this.quantizeEntities();
|
|
6101
|
+
this.refreshDynamicEntitiesInPartition();
|
|
6161
6102
|
}
|
|
6162
|
-
if (this.positionQuantizationStep !== null || this.velocityQuantizationStep !== null) this.quantizeEntities();
|
|
6163
6103
|
this.handleCollisionEvents(firstPassCollisions);
|
|
6164
6104
|
if (this.enableSleep) this.updateSleepState();
|
|
6165
6105
|
}
|
|
@@ -6249,7 +6189,10 @@ var World = class {
|
|
|
6249
6189
|
clear() {
|
|
6250
6190
|
for (const entity of this.entities) this.events.emitEntityRemoved(entity);
|
|
6251
6191
|
this.entities.clear();
|
|
6192
|
+
this.staticEntities.clear();
|
|
6193
|
+
this.dynamicEntities.clear();
|
|
6252
6194
|
this.spatialPartition.clear();
|
|
6195
|
+
this.queryResults.clear();
|
|
6253
6196
|
this.previousCollisions.clear();
|
|
6254
6197
|
}
|
|
6255
6198
|
quantizeEntities() {
|
|
@@ -6270,6 +6213,18 @@ var World = class {
|
|
|
6270
6213
|
refreshDynamicEntitiesInPartition() {
|
|
6271
6214
|
for (const entity of this.dynamicEntities) this.spatialPartition.update(entity);
|
|
6272
6215
|
}
|
|
6216
|
+
refreshEntitiesInPartition() {
|
|
6217
|
+
for (const entity of this.entities) this.syncEntity(entity);
|
|
6218
|
+
}
|
|
6219
|
+
syncEntityCollection(entity) {
|
|
6220
|
+
if (entity.isStatic()) {
|
|
6221
|
+
this.dynamicEntities.delete(entity);
|
|
6222
|
+
this.staticEntities.add(entity);
|
|
6223
|
+
} else {
|
|
6224
|
+
this.staticEntities.delete(entity);
|
|
6225
|
+
this.dynamicEntities.add(entity);
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6273
6228
|
/**
|
|
6274
6229
|
* Gets statistics about the world
|
|
6275
6230
|
*
|
|
@@ -6358,6 +6313,10 @@ var World = class {
|
|
|
6358
6313
|
*
|
|
6359
6314
|
* Represents an independent simulation zone that can contain entities.
|
|
6360
6315
|
* Regions can overlap to allow smooth entity transitions.
|
|
6316
|
+
*
|
|
6317
|
+
* @experimental Region simulation is not the recommended default path for
|
|
6318
|
+
* RPG-JS server physics yet. Prefer a single `PhysicsEngine` world until
|
|
6319
|
+
* region migration semantics are fully benchmarked and documented.
|
|
6361
6320
|
*
|
|
6362
6321
|
* @example
|
|
6363
6322
|
* ```typescript
|
|
@@ -6509,6 +6468,11 @@ var Region = class {
|
|
|
6509
6468
|
*
|
|
6510
6469
|
* Manages multiple regions in a distributed physics world.
|
|
6511
6470
|
* Handles entity migration between regions and region activation/deactivation.
|
|
6471
|
+
*
|
|
6472
|
+
* @experimental Region simulation is not the recommended default path for
|
|
6473
|
+
* RPG-JS server physics yet. Prefer `PhysicsEngine` without regions until
|
|
6474
|
+
* migration semantics, events, stats, and config propagation are fully
|
|
6475
|
+
* benchmarked and documented.
|
|
6512
6476
|
*
|
|
6513
6477
|
* @example
|
|
6514
6478
|
* ```typescript
|
|
@@ -6617,6 +6581,37 @@ var RegionManager = class {
|
|
|
6617
6581
|
}
|
|
6618
6582
|
}
|
|
6619
6583
|
/**
|
|
6584
|
+
* Synchronizes an entity with its current region, migrating it when needed.
|
|
6585
|
+
*
|
|
6586
|
+
* @param entity - Entity to synchronize
|
|
6587
|
+
*/
|
|
6588
|
+
updateEntity(entity) {
|
|
6589
|
+
const currentRegion = this.entityRegionMap.get(entity);
|
|
6590
|
+
const newRegion = this.getRegionAt(entity.position);
|
|
6591
|
+
if (!newRegion) {
|
|
6592
|
+
if (currentRegion) {
|
|
6593
|
+
currentRegion.removeEntity(entity);
|
|
6594
|
+
this.entityRegionMap.delete(entity);
|
|
6595
|
+
}
|
|
6596
|
+
return;
|
|
6597
|
+
}
|
|
6598
|
+
if (!currentRegion) {
|
|
6599
|
+
newRegion.addEntity(entity);
|
|
6600
|
+
this.entityRegionMap.set(entity, newRegion);
|
|
6601
|
+
if (this.config.autoActivate) newRegion.activate();
|
|
6602
|
+
return;
|
|
6603
|
+
}
|
|
6604
|
+
if (newRegion !== currentRegion) {
|
|
6605
|
+
currentRegion.removeEntity(entity);
|
|
6606
|
+
newRegion.addEntity(entity);
|
|
6607
|
+
this.entityRegionMap.set(entity, newRegion);
|
|
6608
|
+
if (this.config.autoActivate) newRegion.activate();
|
|
6609
|
+
if (this.config.autoActivate && currentRegion.getEntities().length === 0) currentRegion.deactivate();
|
|
6610
|
+
return;
|
|
6611
|
+
}
|
|
6612
|
+
currentRegion.getWorld().updateEntity(entity);
|
|
6613
|
+
}
|
|
6614
|
+
/**
|
|
6620
6615
|
* Updates entity positions and migrates them between regions if needed
|
|
6621
6616
|
*/
|
|
6622
6617
|
updateEntities() {
|
|
@@ -6628,13 +6623,7 @@ var RegionManager = class {
|
|
|
6628
6623
|
newRegion
|
|
6629
6624
|
});
|
|
6630
6625
|
}
|
|
6631
|
-
for (const { entity
|
|
6632
|
-
const oldRegion = this.entityRegionMap.get(entity);
|
|
6633
|
-
if (oldRegion) oldRegion.removeEntity(entity);
|
|
6634
|
-
newRegion.addEntity(entity);
|
|
6635
|
-
this.entityRegionMap.set(entity, newRegion);
|
|
6636
|
-
if (this.config.autoActivate) newRegion.activate();
|
|
6637
|
-
}
|
|
6626
|
+
for (const { entity } of entitiesToMigrate) this.updateEntity(entity);
|
|
6638
6627
|
}
|
|
6639
6628
|
/**
|
|
6640
6629
|
* Steps all active regions
|
|
@@ -7046,7 +7035,8 @@ var ZoneManager = class {
|
|
|
7046
7035
|
* ```
|
|
7047
7036
|
*/
|
|
7048
7037
|
createZone(config, callbacks) {
|
|
7049
|
-
const id = generateUUID();
|
|
7038
|
+
const id = config.id ?? generateUUID();
|
|
7039
|
+
if (this.zones.has(id)) throw new Error(`Zone "${id}" already exists`);
|
|
7050
7040
|
const radius = config.radius;
|
|
7051
7041
|
if (typeof radius !== "number" || radius <= 0) throw new Error("Zone radius must be a positive number");
|
|
7052
7042
|
const angle = config.angle ?? 360;
|
|
@@ -7370,6 +7360,7 @@ var PhysicsEngine = class {
|
|
|
7370
7360
|
this.regionManager = null;
|
|
7371
7361
|
this.movementManager = null;
|
|
7372
7362
|
this.zoneManager = null;
|
|
7363
|
+
this.rpgSpeeds = /* @__PURE__ */ new Map();
|
|
7373
7364
|
this.tick = 0;
|
|
7374
7365
|
this.useRegions = config.enableRegions ?? false;
|
|
7375
7366
|
if (this.useRegions) {
|
|
@@ -7460,6 +7451,19 @@ var PhysicsEngine = class {
|
|
|
7460
7451
|
return this.tick;
|
|
7461
7452
|
}
|
|
7462
7453
|
/**
|
|
7454
|
+
* Applies a frame of RPG movement inputs, advances the simulation, and updates sensors.
|
|
7455
|
+
*
|
|
7456
|
+
* @param inputs - Map of entity id to direction input
|
|
7457
|
+
* @returns Current tick index after stepping
|
|
7458
|
+
*/
|
|
7459
|
+
stepFrame(inputs = {}) {
|
|
7460
|
+
for (const [id, input] of Object.entries(inputs)) if (this.isFrameInputObject(input)) this.moveEntity(id, input.direction, input.speed);
|
|
7461
|
+
else this.moveEntity(id, input);
|
|
7462
|
+
this.step();
|
|
7463
|
+
if (this.zoneManager) this.zoneManager.update();
|
|
7464
|
+
return this.tick;
|
|
7465
|
+
}
|
|
7466
|
+
/**
|
|
7463
7467
|
* Advances the simulation by a fixed number of ticks.
|
|
7464
7468
|
*
|
|
7465
7469
|
* @param ticks - Number of ticks to simulate (>= 1)
|
|
@@ -7494,6 +7498,93 @@ var PhysicsEngine = class {
|
|
|
7494
7498
|
return entity;
|
|
7495
7499
|
}
|
|
7496
7500
|
/**
|
|
7501
|
+
* Creates a dynamic RPG character with a stable id, hitbox, and default movement speed.
|
|
7502
|
+
*
|
|
7503
|
+
* This is the recommended creation path for players and NPCs in server-side RPG
|
|
7504
|
+
* simulations because the entity is registered and ready for `moveEntity` and
|
|
7505
|
+
* `stepFrame` immediately.
|
|
7506
|
+
*
|
|
7507
|
+
* @param id - Stable entity identifier
|
|
7508
|
+
* @param options - Character configuration
|
|
7509
|
+
* @returns Created entity
|
|
7510
|
+
*/
|
|
7511
|
+
createCharacter(id, options) {
|
|
7512
|
+
const { x, y, hitbox: hitboxOption, speed, velocity, maxLinearVelocity, ...entityOptions } = options;
|
|
7513
|
+
const hitbox = this.resolveHitbox(hitboxOption);
|
|
7514
|
+
const config = {
|
|
7515
|
+
...entityOptions,
|
|
7516
|
+
...hitbox,
|
|
7517
|
+
uuid: id,
|
|
7518
|
+
position: {
|
|
7519
|
+
x,
|
|
7520
|
+
y
|
|
7521
|
+
},
|
|
7522
|
+
mass: options.mass ?? 1,
|
|
7523
|
+
maxLinearVelocity: maxLinearVelocity ?? speed
|
|
7524
|
+
};
|
|
7525
|
+
if (velocity !== void 0) config.velocity = velocity;
|
|
7526
|
+
const entity = this.createEntity(config);
|
|
7527
|
+
this.rpgSpeeds.set(entity.uuid, speed);
|
|
7528
|
+
return entity;
|
|
7529
|
+
}
|
|
7530
|
+
/**
|
|
7531
|
+
* Creates a static rectangular obstacle for RPG maps.
|
|
7532
|
+
*
|
|
7533
|
+
* @param id - Stable entity identifier
|
|
7534
|
+
* @param options - Obstacle configuration
|
|
7535
|
+
* @returns Created static entity
|
|
7536
|
+
*/
|
|
7537
|
+
createStaticObstacle(id, options) {
|
|
7538
|
+
return this.createEntity({
|
|
7539
|
+
...options,
|
|
7540
|
+
uuid: id,
|
|
7541
|
+
position: {
|
|
7542
|
+
x: options.x,
|
|
7543
|
+
y: options.y
|
|
7544
|
+
},
|
|
7545
|
+
width: options.width,
|
|
7546
|
+
height: options.height,
|
|
7547
|
+
mass: 0
|
|
7548
|
+
});
|
|
7549
|
+
}
|
|
7550
|
+
/**
|
|
7551
|
+
* Creates a static or attached sensor zone with a stable id.
|
|
7552
|
+
*
|
|
7553
|
+
* Sensors detect entities through the `ZoneManager` and do not create physical
|
|
7554
|
+
* collision responses.
|
|
7555
|
+
*
|
|
7556
|
+
* @param id - Stable sensor identifier
|
|
7557
|
+
* @param options - Sensor configuration
|
|
7558
|
+
* @returns Sensor identifier
|
|
7559
|
+
*/
|
|
7560
|
+
createSensor(id, options) {
|
|
7561
|
+
const { onEnter, onExit, entity, position, x, y, ...zoneOptions } = options;
|
|
7562
|
+
let callbacks;
|
|
7563
|
+
if (onEnter || onExit) {
|
|
7564
|
+
callbacks = {};
|
|
7565
|
+
if (onEnter) callbacks.onEnter = onEnter;
|
|
7566
|
+
if (onExit) callbacks.onExit = onExit;
|
|
7567
|
+
}
|
|
7568
|
+
if (entity) {
|
|
7569
|
+
const attachedEntity = this.resolveEntity(entity);
|
|
7570
|
+
if (!attachedEntity) throw new Error(`Cannot create sensor "${id}" for unknown entity`);
|
|
7571
|
+
return this.getZoneManager().createZone({
|
|
7572
|
+
...zoneOptions,
|
|
7573
|
+
id,
|
|
7574
|
+
entity: attachedEntity
|
|
7575
|
+
}, callbacks);
|
|
7576
|
+
}
|
|
7577
|
+
const resolvedPosition = position ?? {
|
|
7578
|
+
x: x ?? 0,
|
|
7579
|
+
y: y ?? 0
|
|
7580
|
+
};
|
|
7581
|
+
return this.getZoneManager().createZone({
|
|
7582
|
+
...zoneOptions,
|
|
7583
|
+
id,
|
|
7584
|
+
position: resolvedPosition
|
|
7585
|
+
}, callbacks);
|
|
7586
|
+
}
|
|
7587
|
+
/**
|
|
7497
7588
|
* Adds an existing entity to the engine
|
|
7498
7589
|
*
|
|
7499
7590
|
* @param entity - Entity to add
|
|
@@ -7510,6 +7601,7 @@ var PhysicsEngine = class {
|
|
|
7510
7601
|
* @param entity - Entity to remove
|
|
7511
7602
|
*/
|
|
7512
7603
|
removeEntity(entity) {
|
|
7604
|
+
this.rpgSpeeds.delete(entity.uuid);
|
|
7513
7605
|
if (this.useRegions && this.regionManager) this.regionManager.removeEntity(entity);
|
|
7514
7606
|
else this.world.removeEntity(entity);
|
|
7515
7607
|
}
|
|
@@ -7586,7 +7678,70 @@ var PhysicsEngine = class {
|
|
|
7586
7678
|
*/
|
|
7587
7679
|
teleport(entity, position) {
|
|
7588
7680
|
entity.teleport(position);
|
|
7589
|
-
|
|
7681
|
+
this.updateEntity(entity);
|
|
7682
|
+
}
|
|
7683
|
+
/**
|
|
7684
|
+
* Teleports an entity by id or entity reference.
|
|
7685
|
+
*
|
|
7686
|
+
* @param entity - Entity or UUID to teleport
|
|
7687
|
+
* @param position - New position
|
|
7688
|
+
* @returns True when the entity was found
|
|
7689
|
+
*/
|
|
7690
|
+
teleportEntity(entity, position) {
|
|
7691
|
+
const target = this.resolveEntity(entity);
|
|
7692
|
+
if (!target) return false;
|
|
7693
|
+
this.teleport(target, position);
|
|
7694
|
+
return true;
|
|
7695
|
+
}
|
|
7696
|
+
/**
|
|
7697
|
+
* Moves an entity in a cardinal or vector direction using its configured RPG speed.
|
|
7698
|
+
*
|
|
7699
|
+
* Pass `'idle'` or a zero vector to stop the entity.
|
|
7700
|
+
*
|
|
7701
|
+
* @param entity - Entity or UUID to move
|
|
7702
|
+
* @param direction - Cardinal direction or arbitrary vector
|
|
7703
|
+
* @param speed - Optional speed override for this command
|
|
7704
|
+
* @returns True when the entity was found
|
|
7705
|
+
*/
|
|
7706
|
+
moveEntity(entity, direction, speed) {
|
|
7707
|
+
const target = this.resolveEntity(entity);
|
|
7708
|
+
if (!target) return false;
|
|
7709
|
+
const vector = this.resolveDirection(direction);
|
|
7710
|
+
const magnitude = vector.length();
|
|
7711
|
+
if (magnitude === 0) {
|
|
7712
|
+
target.setVelocity({
|
|
7713
|
+
x: 0,
|
|
7714
|
+
y: 0
|
|
7715
|
+
});
|
|
7716
|
+
return true;
|
|
7717
|
+
}
|
|
7718
|
+
const resolvedSpeed = speed ?? this.rpgSpeeds.get(target.uuid) ?? target.maxLinearVelocity;
|
|
7719
|
+
if (!Number.isFinite(resolvedSpeed) || resolvedSpeed <= 0) {
|
|
7720
|
+
target.setVelocity({
|
|
7721
|
+
x: 0,
|
|
7722
|
+
y: 0
|
|
7723
|
+
});
|
|
7724
|
+
return true;
|
|
7725
|
+
}
|
|
7726
|
+
target.setVelocity({
|
|
7727
|
+
x: vector.x / magnitude * resolvedSpeed,
|
|
7728
|
+
y: vector.y / magnitude * resolvedSpeed
|
|
7729
|
+
});
|
|
7730
|
+
return true;
|
|
7731
|
+
}
|
|
7732
|
+
/**
|
|
7733
|
+
* Synchronizes an entity after manual position, shape, or state changes.
|
|
7734
|
+
*
|
|
7735
|
+
* Direct mutations such as `entity.position.set(...)`, `entity.width = ...`,
|
|
7736
|
+
* or `entity.freeze()` bypass the world's broad-phase structures. Call this
|
|
7737
|
+
* helper after such mutations so spatial queries and collisions use the
|
|
7738
|
+
* current entity state immediately.
|
|
7739
|
+
*
|
|
7740
|
+
* @param entity - Entity to synchronize
|
|
7741
|
+
*/
|
|
7742
|
+
updateEntity(entity) {
|
|
7743
|
+
if (this.useRegions && this.regionManager) this.regionManager.updateEntity(entity);
|
|
7744
|
+
else this.world.updateEntity(entity);
|
|
7590
7745
|
}
|
|
7591
7746
|
/**
|
|
7592
7747
|
* Freezes an entity (makes it static)
|
|
@@ -7595,6 +7750,7 @@ var PhysicsEngine = class {
|
|
|
7595
7750
|
*/
|
|
7596
7751
|
freeze(entity) {
|
|
7597
7752
|
entity.freeze();
|
|
7753
|
+
this.updateEntity(entity);
|
|
7598
7754
|
}
|
|
7599
7755
|
/**
|
|
7600
7756
|
* Unfreezes an entity (makes it dynamic)
|
|
@@ -7603,6 +7759,7 @@ var PhysicsEngine = class {
|
|
|
7603
7759
|
*/
|
|
7604
7760
|
unfreeze(entity) {
|
|
7605
7761
|
entity.unfreeze();
|
|
7762
|
+
this.updateEntity(entity);
|
|
7606
7763
|
}
|
|
7607
7764
|
/**
|
|
7608
7765
|
* Queries entities in an AABB region
|
|
@@ -7620,9 +7777,7 @@ var PhysicsEngine = class {
|
|
|
7620
7777
|
}
|
|
7621
7778
|
return entities;
|
|
7622
7779
|
}
|
|
7623
|
-
|
|
7624
|
-
if (world.spatialPartition) return Array.from(world.spatialPartition.queryAABB(bounds));
|
|
7625
|
-
return this.world.getEntities().filter((e) => bounds.contains(e.position));
|
|
7780
|
+
return this.world.queryAABB(bounds);
|
|
7626
7781
|
}
|
|
7627
7782
|
/**
|
|
7628
7783
|
* Clears all entities from the engine
|
|
@@ -7630,6 +7785,7 @@ var PhysicsEngine = class {
|
|
|
7630
7785
|
clear() {
|
|
7631
7786
|
if (this.useRegions && this.regionManager) this.regionManager.clear();
|
|
7632
7787
|
else this.world.clear();
|
|
7788
|
+
this.rpgSpeeds.clear();
|
|
7633
7789
|
this.tick = 0;
|
|
7634
7790
|
}
|
|
7635
7791
|
/**
|
|
@@ -7647,6 +7803,7 @@ var PhysicsEngine = class {
|
|
|
7647
7803
|
*/
|
|
7648
7804
|
assignPolygonCollider(entity, config) {
|
|
7649
7805
|
assignPolygonCollider(entity, config);
|
|
7806
|
+
this.updateEntity(entity);
|
|
7650
7807
|
}
|
|
7651
7808
|
/**
|
|
7652
7809
|
* Casts a ray in the physics world and returns the nearest hit, if any.
|
|
@@ -7664,9 +7821,7 @@ var PhysicsEngine = class {
|
|
|
7664
7821
|
* ```
|
|
7665
7822
|
*/
|
|
7666
7823
|
raycast(origin, direction, maxDistance, mask, filter) {
|
|
7667
|
-
|
|
7668
|
-
if (!partition) return null;
|
|
7669
|
-
return raycast(partition, origin, direction, maxDistance, mask, filter);
|
|
7824
|
+
return this.world.raycast(origin, direction, maxDistance, mask, filter);
|
|
7670
7825
|
}
|
|
7671
7826
|
/**
|
|
7672
7827
|
* Computes continuous collision detection (sweep test) time-of-impact between two entities
|
|
@@ -7766,6 +7921,7 @@ var PhysicsEngine = class {
|
|
|
7766
7921
|
entity.angularVelocity = state.angularVelocity;
|
|
7767
7922
|
if (state.sleeping) entity.sleep();
|
|
7768
7923
|
else entity.wakeUp();
|
|
7924
|
+
this.updateEntity(entity);
|
|
7769
7925
|
}
|
|
7770
7926
|
this.tick = snapshot.tick;
|
|
7771
7927
|
}
|
|
@@ -7777,6 +7933,43 @@ var PhysicsEngine = class {
|
|
|
7777
7933
|
getRegionManager() {
|
|
7778
7934
|
return this.regionManager;
|
|
7779
7935
|
}
|
|
7936
|
+
resolveEntity(entity) {
|
|
7937
|
+
if (entity instanceof Entity) return entity;
|
|
7938
|
+
return this.getEntityByUUID(entity);
|
|
7939
|
+
}
|
|
7940
|
+
resolveHitbox(hitbox) {
|
|
7941
|
+
if (typeof hitbox === "number") return { radius: hitbox };
|
|
7942
|
+
if ("type" in hitbox) {
|
|
7943
|
+
if (hitbox.type === "circle") return { radius: hitbox.radius };
|
|
7944
|
+
if (hitbox.type === "capsule") return { capsule: {
|
|
7945
|
+
radius: hitbox.radius,
|
|
7946
|
+
height: hitbox.height
|
|
7947
|
+
} };
|
|
7948
|
+
return {
|
|
7949
|
+
width: hitbox.width,
|
|
7950
|
+
height: hitbox.height
|
|
7951
|
+
};
|
|
7952
|
+
}
|
|
7953
|
+
if ("radius" in hitbox) return { radius: hitbox.radius };
|
|
7954
|
+
return {
|
|
7955
|
+
width: hitbox.width,
|
|
7956
|
+
height: hitbox.height
|
|
7957
|
+
};
|
|
7958
|
+
}
|
|
7959
|
+
resolveDirection(direction) {
|
|
7960
|
+
if (direction instanceof Vector2) return direction.clone();
|
|
7961
|
+
if (typeof direction === "string") switch (direction) {
|
|
7962
|
+
case "up": return new Vector2(0, -1);
|
|
7963
|
+
case "down": return new Vector2(0, 1);
|
|
7964
|
+
case "left": return new Vector2(-1, 0);
|
|
7965
|
+
case "right": return new Vector2(1, 0);
|
|
7966
|
+
default: return new Vector2(0, 0);
|
|
7967
|
+
}
|
|
7968
|
+
return new Vector2(direction.x, direction.y);
|
|
7969
|
+
}
|
|
7970
|
+
isFrameInputObject(input) {
|
|
7971
|
+
return typeof input === "object" && !(input instanceof Vector2) && "direction" in input;
|
|
7972
|
+
}
|
|
7780
7973
|
};
|
|
7781
7974
|
//#endregion
|
|
7782
7975
|
//#region ../physic/src/movement/strategies/Dash.ts
|
|
@@ -8381,17 +8574,7 @@ var ProjectileMovement = class {
|
|
|
8381
8574
|
const gravity = this.options.gravity ?? 30;
|
|
8382
8575
|
this.verticalVelocity -= gravity * dt;
|
|
8383
8576
|
this.currentHeight += this.verticalVelocity * dt;
|
|
8384
|
-
|
|
8385
|
-
else {
|
|
8386
|
-
const dispatcher = globalThis;
|
|
8387
|
-
if (typeof dispatcher.dispatchEvent === "function" && typeof CustomEvent !== "undefined") {
|
|
8388
|
-
const event = new CustomEvent("projectile:height", { detail: {
|
|
8389
|
-
id: body.id,
|
|
8390
|
-
height: this.currentHeight
|
|
8391
|
-
} });
|
|
8392
|
-
dispatcher.dispatchEvent(event);
|
|
8393
|
-
}
|
|
8394
|
-
}
|
|
8577
|
+
this.options.onHeightUpdate?.(this.currentHeight, body);
|
|
8395
8578
|
if (this.currentHeight <= 0) {
|
|
8396
8579
|
this.currentHeight = 0;
|
|
8397
8580
|
if (this.type === ProjectileType.Bounce) if (this.bounceCount < (this.options.maxBounces ?? 0)) {
|
|
@@ -8572,6 +8755,9 @@ var MovementManager = class {
|
|
|
8572
8755
|
};
|
|
8573
8756
|
//#endregion
|
|
8574
8757
|
//#region ../common/src/rooms/Map.ts
|
|
8758
|
+
var COLLISION_PROXIMITY_MARGIN = 1;
|
|
8759
|
+
var DEFAULT_INTERACTION_RANGE = 16;
|
|
8760
|
+
var INTERACTION_SIDE_PADDING = 4;
|
|
8575
8761
|
var RpgCommonMap = class {
|
|
8576
8762
|
constructor() {
|
|
8577
8763
|
this.data = signal(null);
|
|
@@ -8924,9 +9110,8 @@ var RpgCommonMap = class {
|
|
|
8924
9110
|
this.physicsAccumulatorMs -= fixedStepMs;
|
|
8925
9111
|
hooks?.beforeStep?.();
|
|
8926
9112
|
this.physic.updateMovements();
|
|
8927
|
-
const tick = this.physic.
|
|
9113
|
+
const tick = this.physic.stepFrame();
|
|
8928
9114
|
executed += 1;
|
|
8929
|
-
this.runPostTickUpdates();
|
|
8930
9115
|
hooks?.afterStep?.(tick);
|
|
8931
9116
|
}
|
|
8932
9117
|
return executed;
|
|
@@ -9006,8 +9191,7 @@ var RpgCommonMap = class {
|
|
|
9006
9191
|
forceSingleTick(hooks) {
|
|
9007
9192
|
hooks?.beforeStep?.();
|
|
9008
9193
|
this.physic.updateMovements();
|
|
9009
|
-
const tick = this.physic.
|
|
9010
|
-
this.runPostTickUpdates();
|
|
9194
|
+
const tick = this.physic.stepFrame();
|
|
9011
9195
|
hooks?.afterStep?.(tick);
|
|
9012
9196
|
const fixedMs = this.physic.getWorld().getTimeStep() * 1e3;
|
|
9013
9197
|
this.physicsAccumulatorMs = Math.max(0, this.physicsAccumulatorMs - fixedMs);
|
|
@@ -9023,12 +9207,10 @@ var RpgCommonMap = class {
|
|
|
9023
9207
|
return;
|
|
9024
9208
|
}
|
|
9025
9209
|
const hitbox = typeof owner.hitbox === "function" ? owner.hitbox() : owner.hitbox;
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
const radius = Math.max(width, height) / 2;
|
|
9210
|
+
hitbox?.w;
|
|
9211
|
+
hitbox?.h;
|
|
9029
9212
|
this.addCharacter({
|
|
9030
9213
|
owner,
|
|
9031
|
-
radius,
|
|
9032
9214
|
kind,
|
|
9033
9215
|
maxSpeed: owner.speed(),
|
|
9034
9216
|
collidesWithCharacters: !this.shouldDisableCharacterCollisions(owner),
|
|
@@ -9350,19 +9532,13 @@ var RpgCommonMap = class {
|
|
|
9350
9532
|
const centerY = y + height / 2;
|
|
9351
9533
|
boxWidth = Math.max(width, 1);
|
|
9352
9534
|
boxHeight = Math.max(height, 1);
|
|
9353
|
-
entity = this.physic.
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
x: centerX,
|
|
9357
|
-
y: centerY
|
|
9358
|
-
},
|
|
9535
|
+
entity = this.physic.createStaticObstacle(id, {
|
|
9536
|
+
x: centerX,
|
|
9537
|
+
y: centerY,
|
|
9359
9538
|
width: boxWidth,
|
|
9360
9539
|
height: boxHeight,
|
|
9361
|
-
mass: Infinity,
|
|
9362
|
-
state: EntityState.Static,
|
|
9363
9540
|
restitution: 0
|
|
9364
9541
|
});
|
|
9365
|
-
entity.freeze();
|
|
9366
9542
|
}
|
|
9367
9543
|
return id;
|
|
9368
9544
|
}
|
|
@@ -9407,25 +9583,24 @@ var RpgCommonMap = class {
|
|
|
9407
9583
|
const hitbox = typeof owner.hitbox === "function" ? owner.hitbox() : owner.hitbox;
|
|
9408
9584
|
const width = hitbox?.w ?? 32;
|
|
9409
9585
|
const height = hitbox?.h ?? 32;
|
|
9410
|
-
const radius = Math.max(width, height) / 2;
|
|
9411
9586
|
const topLeftX = owner.x();
|
|
9412
9587
|
const topLeftY = owner.y();
|
|
9413
9588
|
const centerX = topLeftX + width / 2;
|
|
9414
9589
|
const centerY = topLeftY + height / 2;
|
|
9415
9590
|
const isStatic = !!options.isStatic;
|
|
9416
|
-
const
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9591
|
+
const speed = options.maxSpeed ? options.maxSpeed * this.speedScalar : 200;
|
|
9592
|
+
const entity = this.physic.createCharacter(id, {
|
|
9593
|
+
x: centerX,
|
|
9594
|
+
y: centerY,
|
|
9595
|
+
hitbox: {
|
|
9596
|
+
width,
|
|
9597
|
+
height
|
|
9421
9598
|
},
|
|
9422
|
-
|
|
9423
|
-
width,
|
|
9424
|
-
height,
|
|
9599
|
+
speed,
|
|
9425
9600
|
mass: options.mass ?? (isStatic ? Infinity : 1),
|
|
9426
9601
|
friction: options.friction ?? .4,
|
|
9427
9602
|
linearDamping: isStatic ? 1 : .2,
|
|
9428
|
-
maxLinearVelocity:
|
|
9603
|
+
maxLinearVelocity: speed,
|
|
9429
9604
|
restitution: 0
|
|
9430
9605
|
});
|
|
9431
9606
|
if (isStatic) entity.freeze();
|
|
@@ -9543,7 +9718,10 @@ var RpgCommonMap = class {
|
|
|
9543
9718
|
const entityHeight = entity.height || entity.radius * 2 || 32;
|
|
9544
9719
|
const centerX = x + entityWidth / 2;
|
|
9545
9720
|
const centerY = y + entityHeight / 2;
|
|
9546
|
-
|
|
9721
|
+
this.physic.teleportEntity(entity, {
|
|
9722
|
+
x: centerX,
|
|
9723
|
+
y: centerY
|
|
9724
|
+
});
|
|
9547
9725
|
return true;
|
|
9548
9726
|
}
|
|
9549
9727
|
/**
|
|
@@ -9579,26 +9757,8 @@ var RpgCommonMap = class {
|
|
|
9579
9757
|
moveBody(player, direction) {
|
|
9580
9758
|
const entity = this.physic.getEntityByUUID(player.id);
|
|
9581
9759
|
if (!entity) return false;
|
|
9582
|
-
const
|
|
9583
|
-
|
|
9584
|
-
switch (direction) {
|
|
9585
|
-
case Direction.Left:
|
|
9586
|
-
vx = -speedValue * this.speedScalar;
|
|
9587
|
-
break;
|
|
9588
|
-
case Direction.Right:
|
|
9589
|
-
vx = speedValue * this.speedScalar;
|
|
9590
|
-
break;
|
|
9591
|
-
case Direction.Up:
|
|
9592
|
-
vy = -speedValue * this.speedScalar;
|
|
9593
|
-
break;
|
|
9594
|
-
case Direction.Down:
|
|
9595
|
-
vy = speedValue * this.speedScalar;
|
|
9596
|
-
break;
|
|
9597
|
-
}
|
|
9598
|
-
entity.setVelocity({
|
|
9599
|
-
x: vx,
|
|
9600
|
-
y: vy
|
|
9601
|
-
});
|
|
9760
|
+
const speed = player.speed() * this.speedScalar;
|
|
9761
|
+
this.physic.moveEntity(entity, direction, speed);
|
|
9602
9762
|
entity.wakeUp();
|
|
9603
9763
|
return true;
|
|
9604
9764
|
}
|
|
@@ -9667,8 +9827,7 @@ var RpgCommonMap = class {
|
|
|
9667
9827
|
if (!entity) return [];
|
|
9668
9828
|
const collider = createCollider(entity);
|
|
9669
9829
|
if (!collider) return [];
|
|
9670
|
-
const
|
|
9671
|
-
const expandedAABB = entityAABB.expand(1);
|
|
9830
|
+
const expandedAABB = collider.getBounds().expand(1);
|
|
9672
9831
|
const nearby = this.physic.queryAABB(expandedAABB);
|
|
9673
9832
|
const collisions = [];
|
|
9674
9833
|
for (const other of nearby) {
|
|
@@ -9676,10 +9835,46 @@ var RpgCommonMap = class {
|
|
|
9676
9835
|
const otherCollider = createCollider(other);
|
|
9677
9836
|
if (!otherCollider) continue;
|
|
9678
9837
|
const otherAABB = otherCollider.getBounds();
|
|
9679
|
-
if (
|
|
9838
|
+
if (expandedAABB.intersects(otherAABB)) collisions.push(other.uuid);
|
|
9839
|
+
}
|
|
9840
|
+
return collisions;
|
|
9841
|
+
}
|
|
9842
|
+
/**
|
|
9843
|
+
* Get entities inside the action area directly in front of an entity.
|
|
9844
|
+
*
|
|
9845
|
+
* This is intentionally separate from physical collisions: a player often
|
|
9846
|
+
* presses the action key while blocked just before touching an NPC, so the
|
|
9847
|
+
* physics solver may leave a tiny gap even though gameplay expects an
|
|
9848
|
+
* interaction.
|
|
9849
|
+
*/
|
|
9850
|
+
getInteractionCollisions(id, direction, range = DEFAULT_INTERACTION_RANGE) {
|
|
9851
|
+
const entity = this.physic.getEntityByUUID(id);
|
|
9852
|
+
if (!entity) return [];
|
|
9853
|
+
const collider = createCollider(entity);
|
|
9854
|
+
if (!collider) return [];
|
|
9855
|
+
const entityAABB = collider.getBounds();
|
|
9856
|
+
const interactionAABB = this.getInteractionAABB(entityAABB, direction, range);
|
|
9857
|
+
const nearby = this.physic.queryAABB(interactionAABB);
|
|
9858
|
+
const collisions = [];
|
|
9859
|
+
for (const other of nearby) {
|
|
9860
|
+
if (other.uuid === id) continue;
|
|
9861
|
+
const otherCollider = createCollider(other);
|
|
9862
|
+
if (!otherCollider) continue;
|
|
9863
|
+
if (interactionAABB.intersects(otherCollider.getBounds())) collisions.push(other.uuid);
|
|
9680
9864
|
}
|
|
9681
9865
|
return collisions;
|
|
9682
9866
|
}
|
|
9867
|
+
getInteractionAABB(bounds, direction, range = DEFAULT_INTERACTION_RANGE) {
|
|
9868
|
+
const distance = Number.isFinite(range) ? Math.max(0, range) : DEFAULT_INTERACTION_RANGE;
|
|
9869
|
+
const sidePadding = distance > 0 ? Math.min(INTERACTION_SIDE_PADDING, distance / 2) : 0;
|
|
9870
|
+
switch (direction) {
|
|
9871
|
+
case Direction.Up: return new AABB(bounds.minX - sidePadding, bounds.minY - distance, bounds.maxX + sidePadding, bounds.minY + COLLISION_PROXIMITY_MARGIN);
|
|
9872
|
+
case Direction.Down: return new AABB(bounds.minX - sidePadding, bounds.maxY - COLLISION_PROXIMITY_MARGIN, bounds.maxX + sidePadding, bounds.maxY + distance);
|
|
9873
|
+
case Direction.Left: return new AABB(bounds.minX - distance, bounds.minY - sidePadding, bounds.minX + COLLISION_PROXIMITY_MARGIN, bounds.maxY + sidePadding);
|
|
9874
|
+
case Direction.Right: return new AABB(bounds.maxX - COLLISION_PROXIMITY_MARGIN, bounds.minY - sidePadding, bounds.maxX + distance, bounds.maxY + sidePadding);
|
|
9875
|
+
default: return bounds.expand(distance);
|
|
9876
|
+
}
|
|
9877
|
+
}
|
|
9683
9878
|
/**
|
|
9684
9879
|
* Get physics body (entity) for an id
|
|
9685
9880
|
* @protected
|
|
@@ -9739,6 +9934,7 @@ var RpgCommonMap = class {
|
|
|
9739
9934
|
}
|
|
9740
9935
|
entity.position.set(centerX, centerY);
|
|
9741
9936
|
entity.notifyPositionChange();
|
|
9937
|
+
this.physic.updateEntity(entity);
|
|
9742
9938
|
return entity;
|
|
9743
9939
|
}
|
|
9744
9940
|
/**
|
|
@@ -9750,7 +9946,6 @@ var RpgCommonMap = class {
|
|
|
9750
9946
|
* @private
|
|
9751
9947
|
*/
|
|
9752
9948
|
addZone(id, options) {
|
|
9753
|
-
const zoneManager = this.physic.getZoneManager();
|
|
9754
9949
|
if (this.physic.getEntityByUUID(id)) throw new Error(`Zone with id ${id} already exists as entity`);
|
|
9755
9950
|
const radius = options.radius;
|
|
9756
9951
|
if (typeof radius !== "number" || radius <= 0) throw new Error("Zone radius must be a positive number");
|
|
@@ -9759,15 +9954,13 @@ var RpgCommonMap = class {
|
|
|
9759
9954
|
attachedEntity = this.physic.getEntityByUUID(options.linkedTo);
|
|
9760
9955
|
if (!attachedEntity) throw new Error(`Cannot link zone to unknown entity ${options.linkedTo}`);
|
|
9761
9956
|
}
|
|
9762
|
-
const
|
|
9763
|
-
|
|
9764
|
-
callbacks._onExitString = void 0;
|
|
9765
|
-
const zoneId = attachedEntity ? zoneManager.createAttachedZone(attachedEntity, {
|
|
9957
|
+
const zoneId = this.physic.createSensor(id, attachedEntity ? {
|
|
9958
|
+
entity: attachedEntity,
|
|
9766
9959
|
radius,
|
|
9767
9960
|
angle: options.angle ?? 360,
|
|
9768
9961
|
direction: options.direction ?? "down",
|
|
9769
9962
|
limitedByWalls: options.limitedByWalls ?? false
|
|
9770
|
-
}
|
|
9963
|
+
} : {
|
|
9771
9964
|
position: {
|
|
9772
9965
|
x: options.x ?? 0,
|
|
9773
9966
|
y: options.y ?? 0
|
|
@@ -9776,7 +9969,7 @@ var RpgCommonMap = class {
|
|
|
9776
9969
|
angle: options.angle ?? 360,
|
|
9777
9970
|
direction: options.direction ?? "down",
|
|
9778
9971
|
limitedByWalls: options.limitedByWalls ?? false
|
|
9779
|
-
}
|
|
9972
|
+
});
|
|
9780
9973
|
this._zoneIdMap = this._zoneIdMap || /* @__PURE__ */ new Map();
|
|
9781
9974
|
this._zoneIdMap.set(id, zoneId);
|
|
9782
9975
|
return id;
|
|
@@ -11041,6 +11234,6 @@ function provideServerModules(modules) {
|
|
|
11041
11234
|
});
|
|
11042
11235
|
}
|
|
11043
11236
|
//#endregion
|
|
11044
|
-
export {
|
|
11237
|
+
export { Skill as $, isArray as A, ProjectileMovement as B, SDEF as C, linkedSignal as Ct, arrayFlat as D, combineLatest as Dt, PrebuiltGui as E, finalize as Et, ModulesToken as F, Knockback as G, PathFollow as H, RpgModule as I, Entity as J, IceMovement as K, WorldMapsManager as L, isInstanceOf as M, isString as N, arrayUniq as O, BehaviorSubject as Ot, random as P, RpgCommonPlayer as Q, RpgCommonMap as R, PDEF as S, isSignal as St, PerlinNoise2D as T, untracked as Tt, Oscillate as U, ProjectileType as V, LinearRepulsion as W, RpgShape as X, Vector2 as Y, Direction as Z, ATK as _, computed as _t, isMapUpdateAuthorized as a, createStatesSnapshotDeep as at, MAXHP as b, isComputed as bt, updateMap as c, id as ct, context$1 as d, sync as dt, Item as et, inject as f, syncClass as ft, AGI as g, ObjectSubject as gt, injector as h, ArraySubject as ht, createMapUpdateHeaders as i, createStatesSnapshot as it, isFunction as j, capitalize as k, createErrorClass as kt, context as l, load as lt, inject$1 as m, users as mt, MAP_UPDATE_TOKEN_ENV as n, __decorateMetadata as nt, readMapUpdateToken as o, generateShortUUID as ot, setInject as p, type as pt, Dash as q, MAP_UPDATE_TOKEN_HEADER as r, DELETE_TOKEN as rt, resolveMapUpdateToken as s, getByPath as st, provideServerModules as t, __decorate as tt, clearInject as u, persist as ut, DEX as v, effect as vt, STR as w, signal as wt, MAXSP as x, isObjectSubject as xt, INT as y, isArraySubject as yt, SeekAvoid as z };
|
|
11045
11238
|
|
|
11046
|
-
//# sourceMappingURL=module-
|
|
11239
|
+
//# sourceMappingURL=module-Dy124Jyk.js.map
|