@antha/entity-2d 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,436 @@
1
+ import { type Asset, type AssetBulkLoaderLoadOptions, type AssetLoader, type AssetValue } from '@antha/asset';
2
+ import { type PixiApplication } from '@antha/graphics-2d';
3
+ import { ConstructorInstanceMap, type AnyObject, type ExtractKeysWithMatchingValues, type MaybePromise, type PartialWithUndefined } from '@augment-vir/common';
4
+ import { System as HitboxSystem, type Response as Collision, type Body as Hitbox } from 'detect-collisions';
5
+ import { type Shape } from 'object-shape-tester';
6
+ import { type Container, type ViewContainer } from 'pixi.js';
7
+ import { type AbstractConstructor, type Constructor, type EmptyObject, type IsEqual, type IsNever, type WritableKeysOf } from 'type-fest';
8
+ import { GenericListenTarget } from 'typed-event-target';
9
+ import { type StaticEntity2dParts } from './entity-suite.js';
10
+ export { System as HitboxSystem, type Response as Collision } from 'detect-collisions';
11
+ /**
12
+ * Definition of entity assets used when defining an entity.
13
+ *
14
+ * @category Internal
15
+ */
16
+ export type BaseEntityAssetDefinitions = Record<string, Omit<Asset,
17
+ /** Name will be the value's key in this object. */
18
+ 'name'>>;
19
+ /**
20
+ * A record of `Asset` entries.
21
+ *
22
+ * @category Internal
23
+ */
24
+ export type BaseEntityAssets = Record<string, Asset>;
25
+ /**
26
+ * Maps {@link BaseEntityAssetDefinitions} into full `Asset` instances.
27
+ *
28
+ * @category Internal
29
+ */
30
+ export type MappedEntityAssets<Definitions extends BaseEntityAssetDefinitions | undefined> = Definitions extends undefined ? EmptyObject : {
31
+ [Key in keyof Definitions]: Definitions[Key] & {
32
+ name: string;
33
+ };
34
+ };
35
+ /**
36
+ * Parameters for {@link EntityStore2d.addEntity}. Flattens itself to an empty array if there are no
37
+ * entity constructor params.
38
+ *
39
+ * @category Internal
40
+ */
41
+ export type AddEntity2dParams<ThisConstructor extends Entity2dConstructor> = ThisConstructor extends {
42
+ ConstructorArgsType: infer Args extends Entity2dConstructorParams<any, any>;
43
+ } ? Args['params'] extends undefined ? [] : [Args['params']] : ['ERROR: invalid entity constructor'];
44
+ /**
45
+ * Parameters for the constructor of {@link EntityStore2d}.
46
+ *
47
+ * @category Internal
48
+ */
49
+ export type EntityStore2dConstructorParams<State extends AnyObject = any> = {
50
+ /**
51
+ * A PixiJS [`Application`](https://pixijs.download/release/docs/app.Application.html) instance
52
+ * from the [`pixi.js`](https://www.npmjs.com/package/pixi.js) package.
53
+ */
54
+ pixi: PixiApplication;
55
+ state: State;
56
+ assetLoader: AssetLoader;
57
+ } & PartialWithUndefined<{
58
+ /**
59
+ * A `System` instance from the
60
+ * [`detect-collisions`](https://www.npmjs.com/package/detect-collisions) package. If this
61
+ * property is omitted or `undefined`, the {@link EntityStore2d} instance will create its own.
62
+ */
63
+ customHitboxSystem?: HitboxSystem | undefined;
64
+ /**
65
+ * An array of all entity constructors that will be pre-registered with this
66
+ * {@link EntityStore2d} instance.
67
+ */
68
+ preregisteredEntities: ReadonlyArray<Entity2dConstructor>;
69
+ }>;
70
+ /**
71
+ * A constructor that creates an Entity, as well as the static entity data that should be attached
72
+ * to it.
73
+ *
74
+ * @category Internal
75
+ */
76
+ export type Entity2dConstructor = Constructor<BaseEntity2d> & StaticEntity2dParts;
77
+ /**
78
+ * The top level storage class of all entities. Add entities with {@link EntityStore2d.addEntity}.
79
+ *
80
+ * @category Internal
81
+ */
82
+ export declare class EntityStore2d<State extends AnyObject = any> {
83
+ /**
84
+ * All current child entities.
85
+ *
86
+ * Instead of modifying this set, use {@link EntityStore2d.addEntity} or
87
+ * {@link EntityStore2d.removeEntity}. If you must manually modify this set directly, you'll also
88
+ * need to modify {@link EntityStore2d.entityInstanceMap}.
89
+ */
90
+ readonly currentEntityInstances: Set<BaseEntity2d<any, any, any>>;
91
+ /** If true, this entity store should no longer be used or operated upon. */
92
+ readonly isDestroyed: boolean;
93
+ /** An internal mapping of all entity constructors to their current instances. */
94
+ readonly entityInstanceMap: ConstructorInstanceMap;
95
+ /** Original pixi app. */
96
+ readonly pixi: PixiApplication;
97
+ /** Collision detection system. */
98
+ readonly hitboxSystem: HitboxSystem;
99
+ /** A map of all entity keys to their registered Entity constructors. */
100
+ entityKeyConstructorMap: Record<string, Entity2dConstructor>;
101
+ /** Listen target for events emitted from any child entities. */
102
+ listenTarget: GenericListenTarget;
103
+ readonly state: State;
104
+ readonly assetLoader: AssetLoader;
105
+ constructor(args: Readonly<EntityStore2dConstructorParams>);
106
+ /**
107
+ * Load all the assets for all the given entities. Without this, assets will be loaded on demand
108
+ * only.
109
+ */
110
+ loadEntityAssets({ entities, otherAssets, }: Readonly<{
111
+ entities: ReadonlyArray<Entity2dConstructor>;
112
+ otherAssets?: ReadonlyArray<Readonly<Asset>> | undefined;
113
+ }>, options?: Readonly<AssetBulkLoaderLoadOptions> | undefined): Promise<readonly unknown[]>;
114
+ /**
115
+ * Register a set of entities so that they can be deserialized (for example, when transferring
116
+ * game state in across the network for multilayer).
117
+ */
118
+ registerEntities({ clearPreviousRegistrations, entities, }: Readonly<{
119
+ entities: ReadonlyArray<Entity2dConstructor>;
120
+ } & PartialWithUndefined<{
121
+ /** If set to true, all previous registrations will be removed. */
122
+ clearPreviousRegistrations: boolean;
123
+ }>>): void;
124
+ /**
125
+ * Runs `.update()` on all current entities and runs collision detection for all hitboxes. If
126
+ * any entities get marked as destroyed during their update, then they will be removed from the
127
+ * set of entities.
128
+ *
129
+ * @returns All detected hitbox collisions (if any).
130
+ */
131
+ updateAllEntities(updateParams: Readonly<EntityUpdateParams>): Promise<void>;
132
+ /** Get all current instances of the given entity class constructor. */
133
+ getEntities<T>(entityClassConstructor: AbstractConstructor<T> | Constructor<T>): Set<T>;
134
+ /** Remove an entity from the store. */
135
+ removeEntity(entity: BaseEntity2d): void;
136
+ /**
137
+ * Create an entity instance by finding the registered constructor with the given `entityKey`
138
+ * and then deserializing and passing the given `serializedParams` to that constructor.
139
+ */
140
+ deserializeEntity(entityKey: string, serializedParams: string | undefined): Promise<BaseEntity2d>;
141
+ /** Create a new instance of the given entity class and add it to this entity store. */
142
+ addEntity<const NewEntityConstructor extends Entity2dConstructor>(entityClass: NewEntityConstructor, ...params: AddEntity2dParams<NoInfer<NewEntityConstructor>>): Promise<InstanceType<NewEntityConstructor>>;
143
+ /** Destroys the entity store and all entities contained inside it. */
144
+ destroy(): void;
145
+ }
146
+ /**
147
+ * Shape definition for {@link EntityPositionParams}.
148
+ *
149
+ * @category Util
150
+ */
151
+ export declare const entityPositionParamsShape: Shape<{
152
+ x: number;
153
+ y: number;
154
+ }>;
155
+ /**
156
+ * Base entity serialization. All entities should at least include these properties.
157
+ *
158
+ * @category Internal
159
+ */
160
+ export type EntityPositionParams = typeof entityPositionParamsShape.runtimeType;
161
+ /**
162
+ * Parameters for an entity's constructor.
163
+ *
164
+ * @category Internal
165
+ */
166
+ export type Entity2dConstructorParams<State extends AnyObject = any, Params extends Record<string, any> | undefined = undefined> = (IsNever<Extract<Params, undefined | null>> extends true ? {
167
+ params: Params;
168
+ } : {
169
+ params?: Params;
170
+ }) & {
171
+ state: State;
172
+ paramsMap?: ParamsMap<NoInfer<Params>> | undefined;
173
+ entityStore: EntityStore2d<State>;
174
+ pixi: PixiApplication;
175
+ hitboxSystem: HitboxSystem;
176
+ };
177
+ /**
178
+ * Finds all keys from `Params` that match the value at the given `Key` in `OriginalObject`.
179
+ *
180
+ * @category Internal
181
+ */
182
+ export type MatchingKeys<Key extends PropertyKey, Params extends Record<string, any> | undefined, OriginalObject extends AnyObject> = Params extends Record<string, any> ? ExtractKeysWithMatchingValues<Params, Extract<OriginalObject, Record<Key, any>>[Key]> : never;
183
+ /**
184
+ * An object that controls which entity parameter projects get mapped to view and hitbox properties.
185
+ * Values can be:
186
+ *
187
+ * - `true`: indicates that the property is mapped directly from params to that view or hitbox object.
188
+ * - Omitted: the property is not mapped at all.
189
+ * - A string: specifies the params key that this view or hitbox property is mapped to.
190
+ */
191
+ export type ParamsMap<Params extends Record<string, any> | undefined = AnyObject> = IsEqual<Params, undefined> extends true ? undefined : PartialWithUndefined<{
192
+ view: Partial<{
193
+ [Key in WritableKeysOf<ViewContainer> as IsNever<MatchingKeys<Key, Params, ViewContainer>> extends true ? never : Key]: (Key extends keyof Params ? Params[Key] extends ViewContainer[Key] ? true : never : never) | MatchingKeys<Key, Params, ViewContainer>;
194
+ }>;
195
+ hitbox: Partial<{
196
+ [Key in WritableKeysOf<Hitbox> as IsNever<MatchingKeys<Key, Params, Hitbox>> extends true ? never : Key]: (Key extends keyof Params ? Params[Key] extends Extract<Hitbox, Record<Key, any>>[Key] ? true : never : never) | MatchingKeys<Key, Params, Hitbox>;
197
+ }>;
198
+ }>;
199
+ declare const EntityEvent_base: (new (eventInitDict: {
200
+ bubbles?: boolean;
201
+ cancelable?: boolean;
202
+ composed?: boolean;
203
+ detail: {
204
+ data?: any;
205
+ entityInstance: BaseEntity2d;
206
+ };
207
+ }) => import("typed-event-target").TypedCustomEvent<{
208
+ data?: any;
209
+ entityInstance: BaseEntity2d;
210
+ }, "antha-entity-event">) & Pick<{
211
+ new (type: string, eventInitDict?: EventInit): Event;
212
+ prototype: Event;
213
+ readonly NONE: 0;
214
+ readonly CAPTURING_PHASE: 1;
215
+ readonly AT_TARGET: 2;
216
+ readonly BUBBLING_PHASE: 3;
217
+ }, "prototype" | "NONE" | "CAPTURING_PHASE" | "AT_TARGET" | "BUBBLING_PHASE"> & Pick<import("typed-event-target").TypedCustomEvent<{
218
+ data?: any;
219
+ entityInstance: BaseEntity2d;
220
+ }, "antha-entity-event">, "type">;
221
+ /**
222
+ * The bse of all entity-specific events.
223
+ *
224
+ * @category Internal
225
+ */
226
+ export declare class EntityEvent<const Data = any> extends EntityEvent_base {
227
+ readonly detail: Readonly<undefined | void extends Data ? {
228
+ entityInstance: BaseEntity2d;
229
+ data?: never;
230
+ } : {
231
+ entityInstance: BaseEntity2d;
232
+ data: Data;
233
+ }>;
234
+ constructor(detail: Readonly<undefined | void extends Data ? {
235
+ entityInstance: BaseEntity2d;
236
+ data?: never;
237
+ } : {
238
+ entityInstance: BaseEntity2d;
239
+ data: Data;
240
+ }>);
241
+ }
242
+ /**
243
+ * Event emitted by all entities when they are destroyed.
244
+ *
245
+ * @category Internal
246
+ */
247
+ export declare class EntityDestroyEvent extends EntityEvent<void> {
248
+ }
249
+ /**
250
+ * Default params shape for x, y position coordinates.
251
+ *
252
+ * Use with {@link position2dParamsMap}, or something similar.
253
+ *
254
+ * @category Internal
255
+ */
256
+ export declare const position2dParamsShape: Shape<{
257
+ x: number;
258
+ y: number;
259
+ }>;
260
+ /**
261
+ * Default value for the optional {@link ParamsMap}. This maps the top level params of `x` and `y` to
262
+ * both `x` and `y` in the hitbox and view.
263
+ *
264
+ * Use with {@link position2dParamsShape}, or something similar.
265
+ *
266
+ * @category Internal
267
+ */
268
+ export declare const position2dParamsMap: {
269
+ readonly hitbox: {
270
+ x: true;
271
+ y: true;
272
+ };
273
+ readonly view: {
274
+ x: true;
275
+ y: true;
276
+ };
277
+ };
278
+ /**
279
+ * Type for {@link BaseEntity2d.reverseParamsMap}.
280
+ *
281
+ * @category Internal
282
+ */
283
+ export type ReverseParamsMap = Record<string, Partial<Record<'hitbox' | 'view', string[]>>>;
284
+ /**
285
+ * The parameters given to entity update methods.
286
+ *
287
+ * @category Internal
288
+ */
289
+ export type EntityUpdateParams = {
290
+ msSinceLastUpdate: number;
291
+ };
292
+ /**
293
+ * Base entity class, types, and functionality.
294
+ *
295
+ * @category Internal
296
+ */
297
+ export declare abstract class BaseEntity2d<State extends AnyObject = any, Params extends Record<string, any> | undefined = any, EntityAssets extends BaseEntityAssetDefinitions | undefined = any> {
298
+ /**
299
+ * This key is used for deserialization of entities to track which class needs to be
300
+ * constructed. You cannot have duplicate keys loaded at the same time.
301
+ */
302
+ static readonly entityKey: string;
303
+ /** Shape definition of this entity's parameters. */
304
+ static readonly paramsShape: Shape<AnyObject> | undefined;
305
+ static readonly assets: MappedEntityAssets<BaseEntityAssetDefinitions | undefined> | undefined;
306
+ /**
307
+ * Defines which properties from {@link BaseEntity2d.params} will be mapped to hitbox and/or view
308
+ * properties.
309
+ */
310
+ static readonly paramsMap: ParamsMap | undefined;
311
+ /**
312
+ * A mapping from params properties to hitbox or view properties, making it easy to map params
313
+ * values.
314
+ */
315
+ static readonly reverseParamsMap: ReverseParamsMap | undefined;
316
+ /** Parses the serialized params generated by {@link BaseEntity2d.serialize}. */
317
+ static deserialize(serialized: string | undefined): AnyObject | undefined;
318
+ /**
319
+ * Dispatch an event. This will be dispatched through this entity's entity store (so listen for
320
+ * the event on the store).
321
+ */
322
+ dispatch(event: EntityEvent | EntityDestroyEvent): void;
323
+ /** If true, this entity should no longer be used or operated upon. */
324
+ readonly isDestroyed: boolean;
325
+ private readonly abortController;
326
+ /** An `AbortSignal` that triggers when the entity is destroyed. */
327
+ readonly abortSignal: AbortSignal;
328
+ hitbox: Hitbox<this> | undefined;
329
+ /** The entity store to add all entities to. */
330
+ readonly entityStore: EntityStore2d<State>;
331
+ /** Writable entity params. These should be serializable. */
332
+ readonly params: Params;
333
+ /** Original pixi app. */
334
+ readonly pixi: PixiApplication;
335
+ /** Collision detection system. */
336
+ readonly hitboxSystem: HitboxSystem;
337
+ getAsset: EntityAssetAccessor<MappedEntityAssets<EntityAssets>>;
338
+ constructor(args: Readonly<Entity2dConstructorParams<NoInfer<State>, NoInfer<Params>>>);
339
+ /**
340
+ * Called every game tick. Run all entity updates in here. This should be overridden in all
341
+ * entity definition classes.
342
+ */
343
+ abstract update(updateParams: Readonly<EntityUpdateParams>): MaybePromise<void>;
344
+ /** Called after construction to perform async initialization (e.g. creating views). */
345
+ initInstance(): MaybePromise<void>;
346
+ /** The game's current state. */
347
+ state: State;
348
+ /** Add a new entity to the entity store. */
349
+ addEntity<const NewEntityConstructor extends Entity2dConstructor>(entityClass: NewEntityConstructor, ...params: AddEntity2dParams<NoInfer<NewEntityConstructor>>): Promise<InstanceType<NewEntityConstructor>>;
350
+ /** Marks the entity for destruction in the next entity store update. */
351
+ destroy(): void;
352
+ /**
353
+ * Immediately destroy the current entity, stop its updates.
354
+ *
355
+ * This is probably not what you want to use! See {@link BaseEntity2d.destroy} instead.
356
+ */
357
+ immediatelyDestroy(): void;
358
+ /**
359
+ * This method is call whenever this entity's hitbox (if it has one) collides with another
360
+ * hitbox. It will be called for each individual collision. Override this to do something about
361
+ * it.
362
+ */
363
+ collide(otherEntity: BaseEntity2d, collision: Readonly<Collision>): MaybePromise<void>;
364
+ /**
365
+ * Serialize the entity params for sharing across the network (for multiplayer play). By default
366
+ * this simply calls `JSON.stringify` on `this.params`. This method must be overridden if your
367
+ * entity has params that are not JSON compatible.
368
+ */
369
+ serialize(): string | undefined;
370
+ }
371
+ /**
372
+ * Output of {@link ViewEntity2d.createView}.
373
+ *
374
+ * @category Internal
375
+ */
376
+ export type ViewCreation2d = {
377
+ /**
378
+ * A view for rendering. Create with, for example, [`new
379
+ * AnimatedSprite`](https://pixijs.download/release/docs/scene.AnimatedSprite.html) or [`new
380
+ * Graphics`](https://pixijs.download/release/docs/scene.Graphics.html), etc. imported from the
381
+ * [`pixi.js`](https://www.npmjs.com/package/pixi.js) package.
382
+ */
383
+ view?: Container | undefined;
384
+ /**
385
+ * A Body instance for hitbox collision detection. Create one with, for example,
386
+ * `this.hitboxSystem.createBox()` or import directly from the
387
+ * [`detect-collisions`](https://www.npmjs.com/package/detect-collisions) package, like with
388
+ * [`new Circle`](https://prozi.github.io/detect-collisions/classes/Circle.html#constructor).
389
+ *
390
+ * This property optional, if a hitbox is not provided, collision detection will not be
391
+ * calculated for this entity.
392
+ */
393
+ hitbox?: Hitbox | undefined;
394
+ };
395
+ /**
396
+ * Creates async accessors for all entity assets.
397
+ *
398
+ * @category Internal
399
+ */
400
+ export type EntityAssetAccessor<EntityAssets extends BaseEntityAssetDefinitions | undefined> = EntityAssets extends undefined ? EmptyObject : {
401
+ [Key in keyof EntityAssets]: () => Promise<AssetValue<NonNullable<EntityAssets>[Key]>>;
402
+ };
403
+ /**
404
+ * Base view entity class, types, and functionality.
405
+ *
406
+ * @category Internal
407
+ */
408
+ export declare abstract class ViewEntity2d<State extends AnyObject = any, Params extends Record<string, any> | undefined = any, EntityAssets extends BaseEntityAssetDefinitions | undefined = any> extends BaseEntity2d<State, Params, EntityAssets> {
409
+ /** The entity's PixiJS view. */
410
+ view: Container;
411
+ /** Creates the view and hitbox, adds the view to the stage, and sets up the params proxy. */
412
+ initInstance(): Promise<void>;
413
+ constructor(args: Readonly<Entity2dConstructorParams<NoInfer<State>, NoInfer<Params>>>);
414
+ private wrapParamsInProxy;
415
+ /**
416
+ * Creates the entity's PixiJS view. This will be called on entity construction and added to the
417
+ * PixiJS application stage.
418
+ */
419
+ abstract createView(): MaybePromise<ViewCreation2d>;
420
+ /** Detects if the current entity is still within the bounds of the render canvas. */
421
+ isInBounds(options?: PartialWithUndefined<{
422
+ /**
423
+ * If `true`, the entire entity's bounds must be within the canvas's bounds. If `false`,
424
+ * any portion of the entity being within the canvas bounds is counted.
425
+ *
426
+ * @default false
427
+ */
428
+ entirely?: boolean;
429
+ }>): boolean;
430
+ /**
431
+ * Immediately destroy the current entity, stop its updates, and remove it from the view.
432
+ *
433
+ * This is probably not what you want to use! See {@link BaseEntity2d.destroy} instead.
434
+ */
435
+ immediatelyDestroy(): void;
436
+ }