@antha/entity-2d 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entity-suite.d.ts +7 -1
- package/dist/entity-suite.js +4 -2
- package/dist/entity.d.ts +16 -3
- package/dist/entity.js +135 -5
- package/package.json +6 -6
package/dist/entity-suite.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type AnyObject, type Constructor, type IsAny } from '@augment-vir/common';
|
|
2
2
|
import { type Shape } from 'object-shape-tester';
|
|
3
|
-
import { BaseEntity2d, type BaseEntityAssetDefinitions, type Entity2dConstructorParams, EntityStore2d, type EntityStore2dConstructorParams, type MappedEntityAssets, type ParamsMap, type ReverseParamsMap, ViewEntity2d } from './entity.js';
|
|
3
|
+
import { BaseEntity2d, type BaseEntityAssetDefinitions, type Entity2dConstructor, type Entity2dConstructorParams, EntityStore2d, type EntityStore2dConstructorParams, type MappedEntityAssets, type ParamsMap, type ReverseParamsMap, ViewEntity2d } from './entity.js';
|
|
4
4
|
/**
|
|
5
5
|
* Params for both {@link EntitySuite2d.defineEntity} and {@link EntitySuite2d.defineLogicEntity}.
|
|
6
6
|
*
|
|
7
7
|
* @category Internal
|
|
8
8
|
*/
|
|
9
9
|
export type DefineEntity2dArgs<ParamsShape extends Shape<AnyObject> | undefined, EntityAssets extends BaseEntityAssetDefinitions | undefined> = {
|
|
10
|
+
/** Entity classes this entity observes collisions with. Omit to observe none. */
|
|
11
|
+
collidesWith?: ReadonlyArray<Entity2dConstructor> | undefined;
|
|
10
12
|
/**
|
|
11
13
|
* This key is used for deserialization of entities to track which class needs to be
|
|
12
14
|
* constructed. Do not use duplicate key strings across multiple entity classes.
|
|
@@ -96,6 +98,10 @@ export type DefineEntity2dArgs<ParamsShape extends Shape<AnyObject> | undefined,
|
|
|
96
98
|
* @category Internal
|
|
97
99
|
*/
|
|
98
100
|
export type StaticEntity2dParts<State extends AnyObject = any, ParamsShape extends Shape<AnyObject> | undefined = any, EntityAssets extends BaseEntityAssetDefinitions | undefined = any> = {
|
|
101
|
+
/** Entity classes this entity observes collisions with. Omit to observe none. */
|
|
102
|
+
collidesWith: ReadonlyArray<Entity2dConstructor> | undefined;
|
|
103
|
+
/** Cached entity classes this entity observes collisions with. */
|
|
104
|
+
collidesWithSet: ReadonlySet<Entity2dConstructor> | undefined;
|
|
99
105
|
/**
|
|
100
106
|
* This key is used for deserialization of entities to track which class needs to be
|
|
101
107
|
* constructed. You cannot have duplicate keys loaded at the same time.
|
package/dist/entity-suite.js
CHANGED
|
@@ -13,7 +13,7 @@ export function defineEntitySuite2d() {
|
|
|
13
13
|
return (params) => {
|
|
14
14
|
if (params.assets) {
|
|
15
15
|
getObjectTypedEntries(params.assets).forEach(([key, rawAsset,]) => {
|
|
16
|
-
rawAsset.
|
|
16
|
+
rawAsset.assetName = [
|
|
17
17
|
params.key,
|
|
18
18
|
key,
|
|
19
19
|
].join(':');
|
|
@@ -22,7 +22,7 @@ export function defineEntitySuite2d() {
|
|
|
22
22
|
return defineEntity(entityParent, params);
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
function defineEntity(entityParent, { key, paramsShape, paramsMap, assets }) {
|
|
25
|
+
function defineEntity(entityParent, { collidesWith, key, paramsShape, paramsMap, assets }) {
|
|
26
26
|
if (entityKeys.has(key)) {
|
|
27
27
|
throw new Error(`Entity key '${key}' has already been attached to an entity class.`);
|
|
28
28
|
}
|
|
@@ -30,6 +30,8 @@ export function defineEntitySuite2d() {
|
|
|
30
30
|
const classWrapper = {
|
|
31
31
|
// @ts-expect-error: abstract methods are intentionally not implemented here
|
|
32
32
|
[key]: class extends entityParent {
|
|
33
|
+
static collidesWith = collidesWith;
|
|
34
|
+
static collidesWithSet = new Set(collidesWith);
|
|
33
35
|
static entityKey = key;
|
|
34
36
|
static paramsShape = paramsShape;
|
|
35
37
|
static assets = assets;
|
package/dist/entity.d.ts
CHANGED
|
@@ -13,8 +13,8 @@ export { System as HitboxSystem, type Response as Collision } from 'detect-colli
|
|
|
13
13
|
* @category Internal
|
|
14
14
|
*/
|
|
15
15
|
export type BaseEntityAssetDefinitions = Record<string, Omit<Asset,
|
|
16
|
-
/**
|
|
17
|
-
'
|
|
16
|
+
/** Asset names are derived from the entity and asset keys. */
|
|
17
|
+
'assetName'>>;
|
|
18
18
|
/**
|
|
19
19
|
* A record of `Asset` entries.
|
|
20
20
|
*
|
|
@@ -28,7 +28,7 @@ export type BaseEntityAssets = Record<string, Asset>;
|
|
|
28
28
|
*/
|
|
29
29
|
export type MappedEntityAssets<Definitions extends BaseEntityAssetDefinitions | undefined> = Definitions extends undefined ? EmptyObject : {
|
|
30
30
|
[Key in keyof Definitions]: Definitions[Key] & {
|
|
31
|
-
|
|
31
|
+
assetName: string;
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
/**
|
|
@@ -73,6 +73,13 @@ export type EntityStore2dConstructorParams<State extends AnyObject = any> = {
|
|
|
73
73
|
* @category Internal
|
|
74
74
|
*/
|
|
75
75
|
export type Entity2dConstructor = Constructor<BaseEntity2d> & StaticEntity2dParts;
|
|
76
|
+
/** A collision system that skips pairs no entity observes before running SAT collision checks. */
|
|
77
|
+
export declare class EntityHitboxSystem extends HitboxSystem {
|
|
78
|
+
protected checkedHitboxPairs: WeakMap<Hitbox, WeakSet<Hitbox>> | undefined;
|
|
79
|
+
checkAll(...[callback, response,]: Parameters<HitboxSystem['checkAll']>): any;
|
|
80
|
+
checkOne(...[hitbox, callback, response,]: Parameters<HitboxSystem['checkOne']>): boolean;
|
|
81
|
+
checkCollision(...hitboxes: Parameters<HitboxSystem['checkCollision']>): boolean;
|
|
82
|
+
}
|
|
76
83
|
/**
|
|
77
84
|
* The top level storage class of all entities. Add entities with {@link EntityStore2d.addEntity}.
|
|
78
85
|
*
|
|
@@ -299,6 +306,10 @@ export declare abstract class BaseEntity2d<State extends AnyObject = any, Params
|
|
|
299
306
|
* constructed. You cannot have duplicate keys loaded at the same time.
|
|
300
307
|
*/
|
|
301
308
|
static readonly entityKey: string;
|
|
309
|
+
/** Entity classes this entity observes collisions with. Omit to observe none. */
|
|
310
|
+
static readonly collidesWith: ReadonlyArray<Entity2dConstructor> | undefined;
|
|
311
|
+
/** Cached entity classes this entity observes collisions with. */
|
|
312
|
+
static readonly collidesWithSet: ReadonlySet<Entity2dConstructor> | undefined;
|
|
302
313
|
/** Shape definition of this entity's parameters. */
|
|
303
314
|
static readonly paramsShape: Shape<AnyObject> | undefined;
|
|
304
315
|
static readonly assets: MappedEntityAssets<BaseEntityAssetDefinitions | undefined> | undefined;
|
|
@@ -321,6 +332,8 @@ export declare abstract class BaseEntity2d<State extends AnyObject = any, Params
|
|
|
321
332
|
dispatch(event: EntityEvent | EntityDestroyEvent): void;
|
|
322
333
|
/** If true, this entity should no longer be used or operated upon. */
|
|
323
334
|
readonly isDestroyed: boolean;
|
|
335
|
+
/** Static definition used to construct this entity. */
|
|
336
|
+
readonly entityDefinition: Entity2dConstructor;
|
|
324
337
|
protected readonly abortController: AbortController;
|
|
325
338
|
/** An `AbortSignal` that triggers when the entity is destroyed. */
|
|
326
339
|
readonly abortSignal: AbortSignal;
|
package/dist/entity.js
CHANGED
|
@@ -1,10 +1,120 @@
|
|
|
1
1
|
import { assert, check } from '@augment-vir/assert';
|
|
2
2
|
import { ConstructorInstanceMap, getObjectTypedEntries, makeWritable, mapObjectValues, } from '@augment-vir/common';
|
|
3
|
-
import { System as HitboxSystem, } from 'detect-collisions';
|
|
3
|
+
import { System as HitboxSystem, Response, } from 'detect-collisions';
|
|
4
4
|
import { assertValidShape, defineShape } from 'object-shape-tester';
|
|
5
5
|
import { ParticleContainer } from 'pixi.js';
|
|
6
6
|
import { defineTypedCustomEvent, GenericListenTarget } from 'typed-event-target';
|
|
7
7
|
export { System as HitboxSystem } from 'detect-collisions';
|
|
8
|
+
function extractEntityFromHitbox(hitbox) {
|
|
9
|
+
return hitbox.userData instanceof BaseEntity2d ? hitbox.userData : undefined;
|
|
10
|
+
}
|
|
11
|
+
function hasCollisionTargets(entity) {
|
|
12
|
+
return ((entity.entityDefinition.collidesWithSet?.size ??
|
|
13
|
+
entity.entityDefinition.collidesWith?.length ??
|
|
14
|
+
0) > 0);
|
|
15
|
+
}
|
|
16
|
+
function doesEntityCollideWith({ entity, otherEntity, }) {
|
|
17
|
+
return (entity.entityDefinition.collidesWithSet?.has(otherEntity.entityDefinition) ??
|
|
18
|
+
entity.entityDefinition.collidesWith?.includes(otherEntity.entityDefinition) ??
|
|
19
|
+
false);
|
|
20
|
+
}
|
|
21
|
+
function shouldCheckEntityCollision({ firstEntity, secondEntity, }) {
|
|
22
|
+
return (doesEntityCollideWith({
|
|
23
|
+
entity: firstEntity,
|
|
24
|
+
otherEntity: secondEntity,
|
|
25
|
+
}) ||
|
|
26
|
+
doesEntityCollideWith({
|
|
27
|
+
entity: secondEntity,
|
|
28
|
+
otherEntity: firstEntity,
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
function shouldNotifyEntityOfCollision({ entity, otherEntity, }) {
|
|
32
|
+
return doesEntityCollideWith({
|
|
33
|
+
entity,
|
|
34
|
+
otherEntity,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function createReversedCollision(collision) {
|
|
38
|
+
const reversedCollision = new Response();
|
|
39
|
+
reversedCollision.a = collision.b;
|
|
40
|
+
reversedCollision.aInB = collision.bInA;
|
|
41
|
+
reversedCollision.b = collision.a;
|
|
42
|
+
reversedCollision.bInA = collision.aInB;
|
|
43
|
+
reversedCollision.overlap = collision.overlap;
|
|
44
|
+
reversedCollision.overlapN.x = -collision.overlapN.x;
|
|
45
|
+
reversedCollision.overlapN.y = -collision.overlapN.y;
|
|
46
|
+
reversedCollision.overlapV.x = -collision.overlapV.x;
|
|
47
|
+
reversedCollision.overlapV.y = -collision.overlapV.y;
|
|
48
|
+
return reversedCollision;
|
|
49
|
+
}
|
|
50
|
+
function hasAlreadyCheckedHitboxPair({ checkedHitboxPairs, firstHitbox, secondHitbox, }) {
|
|
51
|
+
return (checkedHitboxPairs.get(firstHitbox)?.has(secondHitbox) ||
|
|
52
|
+
checkedHitboxPairs.get(secondHitbox)?.has(firstHitbox));
|
|
53
|
+
}
|
|
54
|
+
function markHitboxPairAsChecked({ checkedHitboxPairs, firstHitbox, secondHitbox, }) {
|
|
55
|
+
const firstHitboxPairs = checkedHitboxPairs.get(firstHitbox);
|
|
56
|
+
if (firstHitboxPairs) {
|
|
57
|
+
firstHitboxPairs.add(secondHitbox);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
checkedHitboxPairs.set(firstHitbox, new WeakSet([secondHitbox]));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** A collision system that skips pairs no entity observes before running SAT collision checks. */
|
|
64
|
+
export class EntityHitboxSystem extends HitboxSystem {
|
|
65
|
+
checkedHitboxPairs;
|
|
66
|
+
checkAll(...[callback, response,]) {
|
|
67
|
+
const previousCheckedHitboxPairs = this.checkedHitboxPairs;
|
|
68
|
+
this.checkedHitboxPairs = new WeakMap();
|
|
69
|
+
try {
|
|
70
|
+
return this.all().some((hitbox) => {
|
|
71
|
+
const entity = extractEntityFromHitbox(hitbox);
|
|
72
|
+
return ((!entity || hasCollisionTargets(entity)) &&
|
|
73
|
+
this.checkOne(hitbox, callback, response));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
this.checkedHitboxPairs = previousCheckedHitboxPairs;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
checkOne(...[hitbox, callback, response,]) {
|
|
81
|
+
const entity = extractEntityFromHitbox(hitbox);
|
|
82
|
+
if (entity && !hasCollisionTargets(entity)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return super.checkOne(hitbox, callback, response);
|
|
86
|
+
}
|
|
87
|
+
checkCollision(...hitboxes) {
|
|
88
|
+
const [firstHitbox, secondHitbox,] = hitboxes;
|
|
89
|
+
const checkedHitboxPairs = this.checkedHitboxPairs;
|
|
90
|
+
if (checkedHitboxPairs &&
|
|
91
|
+
hasAlreadyCheckedHitboxPair({
|
|
92
|
+
checkedHitboxPairs,
|
|
93
|
+
firstHitbox,
|
|
94
|
+
secondHitbox,
|
|
95
|
+
})) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (checkedHitboxPairs) {
|
|
99
|
+
markHitboxPairAsChecked({
|
|
100
|
+
checkedHitboxPairs,
|
|
101
|
+
firstHitbox,
|
|
102
|
+
secondHitbox,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const firstEntity = extractEntityFromHitbox(firstHitbox);
|
|
106
|
+
const secondEntity = extractEntityFromHitbox(secondHitbox);
|
|
107
|
+
if (firstEntity &&
|
|
108
|
+
secondEntity &&
|
|
109
|
+
!shouldCheckEntityCollision({
|
|
110
|
+
firstEntity,
|
|
111
|
+
secondEntity,
|
|
112
|
+
})) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return super.checkCollision(...hitboxes);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
8
118
|
/**
|
|
9
119
|
* The top level storage class of all entities. Add entities with {@link EntityStore2d.addEntity}.
|
|
10
120
|
*
|
|
@@ -36,7 +146,7 @@ export class EntityStore2d {
|
|
|
36
146
|
constructor(args) {
|
|
37
147
|
this.pixi = args.pixi;
|
|
38
148
|
this.assetLoader = args.assetLoader;
|
|
39
|
-
this.hitboxSystem = args.customHitboxSystem || new
|
|
149
|
+
this.hitboxSystem = args.customHitboxSystem || new EntityHitboxSystem();
|
|
40
150
|
this.state = args.state;
|
|
41
151
|
if (args.preregisteredEntities) {
|
|
42
152
|
this.registerEntities({
|
|
@@ -111,9 +221,22 @@ export class EntityStore2d {
|
|
|
111
221
|
secondaryEntity.isDestroyed) {
|
|
112
222
|
return;
|
|
113
223
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
224
|
+
function trackCollisionResult(result) {
|
|
225
|
+
if (result instanceof Promise) {
|
|
226
|
+
collisionPromises.push(result);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (shouldNotifyEntityOfCollision({
|
|
230
|
+
entity: primaryEntity,
|
|
231
|
+
otherEntity: secondaryEntity,
|
|
232
|
+
})) {
|
|
233
|
+
trackCollisionResult(primaryEntity.collide(secondaryEntity, response));
|
|
234
|
+
}
|
|
235
|
+
if (shouldNotifyEntityOfCollision({
|
|
236
|
+
entity: secondaryEntity,
|
|
237
|
+
otherEntity: primaryEntity,
|
|
238
|
+
})) {
|
|
239
|
+
trackCollisionResult(secondaryEntity.collide(primaryEntity, createReversedCollision(response)));
|
|
117
240
|
}
|
|
118
241
|
});
|
|
119
242
|
await Promise.all(collisionPromises);
|
|
@@ -254,6 +377,10 @@ export class BaseEntity2d {
|
|
|
254
377
|
* constructed. You cannot have duplicate keys loaded at the same time.
|
|
255
378
|
*/
|
|
256
379
|
static entityKey = 'BaseEntity';
|
|
380
|
+
/** Entity classes this entity observes collisions with. Omit to observe none. */
|
|
381
|
+
static collidesWith;
|
|
382
|
+
/** Cached entity classes this entity observes collisions with. */
|
|
383
|
+
static collidesWithSet;
|
|
257
384
|
/** Shape definition of this entity's parameters. */
|
|
258
385
|
static paramsShape;
|
|
259
386
|
static assets;
|
|
@@ -288,6 +415,8 @@ export class BaseEntity2d {
|
|
|
288
415
|
}
|
|
289
416
|
/** If true, this entity should no longer be used or operated upon. */
|
|
290
417
|
isDestroyed = false;
|
|
418
|
+
/** Static definition used to construct this entity. */
|
|
419
|
+
entityDefinition;
|
|
291
420
|
abortController = new AbortController();
|
|
292
421
|
/** An `AbortSignal` that triggers when the entity is destroyed. */
|
|
293
422
|
abortSignal = this.abortController.signal;
|
|
@@ -302,6 +431,7 @@ export class BaseEntity2d {
|
|
|
302
431
|
hitboxSystem;
|
|
303
432
|
getAsset;
|
|
304
433
|
constructor(args) {
|
|
434
|
+
this.entityDefinition = this.constructor;
|
|
305
435
|
this.entityStore = args.entityStore;
|
|
306
436
|
this.params = args.params;
|
|
307
437
|
this.pixi = args.pixi;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antha/entity-2d",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "An Antha mod for handling 2D entities.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vir",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"@augment-vir/common": "^32.2.3"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@antha/engine": "^0.
|
|
43
|
-
"@antha/web-test-runner-plugin-pixi": "^0.
|
|
42
|
+
"@antha/engine": "^0.5.0",
|
|
43
|
+
"@antha/web-test-runner-plugin-pixi": "^0.5.0",
|
|
44
44
|
"@augment-vir/test": "^32.2.3",
|
|
45
45
|
"@web/dev-server-esbuild": "^2.0.0",
|
|
46
46
|
"@web/test-runner": "^1.0.0",
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"typed-event-target": "^4.3.3"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"@antha/asset": "^0.
|
|
56
|
-
"@antha/engine": "^0.
|
|
57
|
-
"@antha/graphics-2d": "^0.
|
|
55
|
+
"@antha/asset": "^0.5.0",
|
|
56
|
+
"@antha/engine": "^0.5.0",
|
|
57
|
+
"@antha/graphics-2d": "^0.5.0",
|
|
58
58
|
"detect-collisions": ">=10",
|
|
59
59
|
"element-vir": ">=26",
|
|
60
60
|
"object-shape-tester": ">=6",
|