@antha/entity-2d 0.16.0 → 0.18.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/README.md +3 -3
- package/dist/{entity-suite.d.ts → antha-entity-2d.mod.d.ts} +46 -68
- package/dist/antha-entity-2d.mod.js +101 -0
- package/dist/entity.d.ts +46 -4
- package/dist/entity.js +48 -3
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/package.json +10 -7
- package/dist/antha-entity.mod.d.ts +0 -36
- package/dist/antha-entity.mod.js +0 -60
- package/dist/entity-suite.js +0 -79
package/README.md
CHANGED
|
@@ -15,13 +15,13 @@ npm i @antha/entity-2d
|
|
|
15
15
|
```TypeScript
|
|
16
16
|
import {AnthaEngine, defineAnthaMod} from '@antha/engine';
|
|
17
17
|
import {createAnthaGraphics2dMod} from '@antha/graphics-2d';
|
|
18
|
-
import {type AnthaEntity2dModState,
|
|
18
|
+
import {type AnthaEntity2dModState, createAnthaEntity2dSuite} from '@antha/entity-2d';
|
|
19
19
|
|
|
20
20
|
type GameState = AnthaEntity2dModState<{
|
|
21
21
|
hasCreatedScoreEntity: boolean;
|
|
22
22
|
}>;
|
|
23
23
|
|
|
24
|
-
const {defineLogicEntity,
|
|
24
|
+
const {defineLogicEntity, updateEntitiesMod} = createAnthaEntity2dSuite<{
|
|
25
25
|
hasCreatedScoreEntity: boolean;
|
|
26
26
|
}>();
|
|
27
27
|
|
|
@@ -38,7 +38,7 @@ const engine = new AnthaEngine<GameState>({
|
|
|
38
38
|
},
|
|
39
39
|
mods: [
|
|
40
40
|
createAnthaGraphics2dMod(),
|
|
41
|
-
|
|
41
|
+
updateEntitiesMod,
|
|
42
42
|
defineAnthaMod<GameState>({
|
|
43
43
|
modName: 'game-logic',
|
|
44
44
|
async execute({state}) {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type AnthaAssetModState } from '@antha/asset';
|
|
2
|
+
import { type ModTrigger } from '@antha/engine';
|
|
3
|
+
import { type AnthaGraphics2dModState } from '@antha/graphics-2d';
|
|
4
|
+
import { type AnyObject, type Constructor, type PartialWithUndefined } from '@augment-vir/common';
|
|
2
5
|
import { type Shape } from 'object-shape-tester';
|
|
3
|
-
import { BaseEntity2d,
|
|
6
|
+
import { BaseEntity2d, EntityStore2d, ViewEntity2d, type BaseEntityAssetDefinitions, type EntityCollisionDefinition, type EntityStore2dConstructorParams, type ParamsMap, type StaticEntity2dParts } from './entity.js';
|
|
4
7
|
/**
|
|
5
|
-
* Params for both
|
|
8
|
+
* Params for both `defineEntity` and `defineLogicEntity`.
|
|
6
9
|
*
|
|
7
10
|
* @category Internal
|
|
8
11
|
*/
|
|
@@ -92,37 +95,6 @@ export type DefineEntity2dArgs<ParamsShape extends Shape | undefined, EntityAsse
|
|
|
92
95
|
paramsMap?: ParamsMap<NoInfer<ParamsShape> extends Shape ? NoInfer<ParamsShape>['runtimeType'] : undefined> | undefined;
|
|
93
96
|
assets?: EntityAssets;
|
|
94
97
|
};
|
|
95
|
-
/**
|
|
96
|
-
* Static members of both view and logic entity constructors.
|
|
97
|
-
*
|
|
98
|
-
* @category Internal
|
|
99
|
-
*/
|
|
100
|
-
export type StaticEntity2dParts<State extends AnyObject = any, ParamsShape extends Shape | undefined = any, EntityAssets extends BaseEntityAssetDefinitions | undefined = any> = {
|
|
101
|
-
/** Entity classes this entity observes collisions with. Omit to observe none. */
|
|
102
|
-
collidesWith: EntityCollisionDefinition | undefined;
|
|
103
|
-
/** Cached entity classes this entity observes collisions with. */
|
|
104
|
-
collidesWithSet: ReadonlySet<Entity2dConstructor>;
|
|
105
|
-
/**
|
|
106
|
-
* This key is used for deserialization of entities to track which class needs to be
|
|
107
|
-
* constructed. You cannot have duplicate keys loaded at the same time.
|
|
108
|
-
*
|
|
109
|
-
* This is used instead of inferring the entity key from the class name so that you can still
|
|
110
|
-
* minify your class names without making debugging nigh impossible (you'll still know which
|
|
111
|
-
* entities are being serialized and deserialized even if your class names are minified).
|
|
112
|
-
*/
|
|
113
|
-
entityKey: string;
|
|
114
|
-
/** Shape definition of this entity's parameters. */
|
|
115
|
-
paramsShape: ParamsShape;
|
|
116
|
-
/**
|
|
117
|
-
* Defines which properties from {@link BaseEntity2d.params} will be mapped to hitbox and/or view
|
|
118
|
-
* properties.
|
|
119
|
-
*/
|
|
120
|
-
paramsMap: IsAny<ParamsShape> extends true ? any : ParamsMap<ParamsShape extends Shape ? ParamsShape['runtimeType'] : undefined>;
|
|
121
|
-
/** Parses the serialized params generated by {@link BaseEntity2d.serialize}. */
|
|
122
|
-
deserialize(serialized: string | undefined): AnyObject | undefined;
|
|
123
|
-
assets: MappedEntityAssets<EntityAssets>;
|
|
124
|
-
ConstructorArgsType: Entity2dConstructorParams<State, ParamsShape extends Shape ? ParamsShape['runtimeType'] : undefined>;
|
|
125
|
-
};
|
|
126
98
|
/**
|
|
127
99
|
* ========================
|
|
128
100
|
*
|
|
@@ -145,7 +117,7 @@ export type DefinedViewEntity2dInstance<State extends AnyObject, ParamsShape ext
|
|
|
145
117
|
*/
|
|
146
118
|
export type DefinedViewEntity2dConstructor<State extends AnyObject, ParamsShape extends Shape | undefined, EntityAssets extends BaseEntityAssetDefinitions | undefined> = Constructor<DefinedViewEntity2dInstance<State, ParamsShape, EntityAssets>, ConstructorParameters<typeof ViewEntity2d<State, ParamsShape extends Shape ? ParamsShape['runtimeType'] : undefined>>> & StaticEntity2dParts<State, ParamsShape>;
|
|
147
119
|
/**
|
|
148
|
-
* Type for
|
|
120
|
+
* Type for `defineEntity`.
|
|
149
121
|
*
|
|
150
122
|
* @category Internal
|
|
151
123
|
*/
|
|
@@ -173,56 +145,62 @@ export type DefinedLogicEntity2dInstance<State extends AnyObject, ParamsShape ex
|
|
|
173
145
|
*/
|
|
174
146
|
export type DefinedLogicEntity2dConstructor<State extends AnyObject, ParamsShape extends Shape | undefined> = Constructor<DefinedLogicEntity2dInstance<State, ParamsShape>, ConstructorParameters<typeof BaseEntity2d<State, ParamsShape extends Shape ? ParamsShape['runtimeType'] : undefined>>> & StaticEntity2dParts<State, ParamsShape>;
|
|
175
147
|
/**
|
|
176
|
-
* Type for `
|
|
148
|
+
* Type for `defineLogicEntity`.
|
|
177
149
|
*
|
|
178
150
|
* @category Internal
|
|
179
151
|
*/
|
|
180
152
|
export type DefineLogicEntity2d<State extends AnyObject> = <const ParamsShape extends Shape | undefined, const EntityAssets extends BaseEntityAssetDefinitions | undefined>(params: DefineEntity2dArgs<ParamsShape, EntityAssets>) => DefinedLogicEntity2dConstructor<NoInfer<State>, NoInfer<ParamsShape>>;
|
|
181
153
|
/**
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* # Entity Suite
|
|
154
|
+
* State for {@link createAnthaEntity2dSuite}.
|
|
185
155
|
*
|
|
186
|
-
*
|
|
156
|
+
* @category Internal
|
|
187
157
|
*/
|
|
158
|
+
export type AnthaEntity2dModState<State extends AnyObject = AnyObject> = {
|
|
159
|
+
entityStore: EntityStore2d<Partial<AnthaEntity2dModState<State>>>;
|
|
160
|
+
/** If true, entity updates and collision checks are skipped. */
|
|
161
|
+
disableEntityUpdate: boolean;
|
|
162
|
+
/** If true, entity renders are skipped. */
|
|
163
|
+
disableEntityRender: boolean;
|
|
164
|
+
/** If `true`, hit boxes are visually rendered for debugging purposes. */
|
|
165
|
+
showHitboxDebug: boolean;
|
|
166
|
+
} & State & AnthaGraphics2dModState & AnthaAssetModState;
|
|
188
167
|
/**
|
|
189
|
-
*
|
|
168
|
+
* Options for {@link createAnthaEntity2dSuite}.
|
|
190
169
|
*
|
|
191
170
|
* @category Internal
|
|
192
171
|
*/
|
|
193
|
-
export type
|
|
172
|
+
export type AnthaEntity2dModOptions = PartialWithUndefined<EntityStore2dConstructorParams & {
|
|
173
|
+
debug: boolean;
|
|
194
174
|
/**
|
|
195
|
-
* The
|
|
196
|
-
*
|
|
197
|
-
* All defined entities will also have a reference to this store so they can add additional
|
|
198
|
-
* entities by themselves.
|
|
175
|
+
* The entity update mod's trigger. If left undefined, enabled entity updates will run on
|
|
176
|
+
* every engine tick.
|
|
199
177
|
*/
|
|
200
|
-
|
|
178
|
+
updateTrigger: ModTrigger;
|
|
201
179
|
/**
|
|
202
|
-
*
|
|
203
|
-
*
|
|
180
|
+
* If `true`, the `updateEntitiesMod` does not update entity data and collision state.
|
|
181
|
+
*
|
|
182
|
+
* @default false
|
|
204
183
|
*/
|
|
205
|
-
|
|
206
|
-
/** Define an entity that doesn't have an attached view. These are likely to be rare. */
|
|
207
|
-
defineLogicEntity: DefineLogicEntity2d<State>;
|
|
184
|
+
disableEntityUpdate: boolean;
|
|
208
185
|
/**
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* instantiated.
|
|
186
|
+
* If `true`, the `updateEntitiesMod` does not update transient entity render state.
|
|
187
|
+
*
|
|
188
|
+
* @default false
|
|
213
189
|
*/
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* This is the starting point of the @game-vir/entity package. Call this to produce the function
|
|
218
|
-
* needed to define new entities and the store needed to add entity instances.
|
|
219
|
-
*
|
|
220
|
-
* @category Main
|
|
221
|
-
*/
|
|
222
|
-
export declare function defineEntitySuite2d<State extends AnyObject>(): EntitySuite2d<State>;
|
|
190
|
+
disableEntityRender: boolean;
|
|
191
|
+
}>;
|
|
223
192
|
/**
|
|
224
|
-
*
|
|
193
|
+
* Creates an entity update mod and entity factories.
|
|
225
194
|
*
|
|
226
|
-
* @category
|
|
195
|
+
* @category Pre-built Mods
|
|
227
196
|
*/
|
|
228
|
-
export declare function
|
|
197
|
+
export declare function createAnthaEntity2dSuite<ExtraState extends AnyObject>(options?: Readonly<AnthaEntity2dModOptions>): {
|
|
198
|
+
/**
|
|
199
|
+
* Updates entity data an/or transient render state, depending on the options you provided
|
|
200
|
+
* to `createAnthaEntity2dSuite`.
|
|
201
|
+
*/
|
|
202
|
+
updateEntitiesMod: import("@antha/engine").AnthaMod<NoInfer<AnthaEntity2dModState<ExtraState>>>;
|
|
203
|
+
defineEntity: DefineViewEntity2d<AnthaEntity2dModState<ExtraState>>;
|
|
204
|
+
defineLogicEntity: DefineLogicEntity2d<AnthaEntity2dModState<ExtraState>>;
|
|
205
|
+
entityKeys: Set<string>;
|
|
206
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { defineAnthaMod, SkipExecution } from '@antha/engine';
|
|
2
|
+
import { assertWrap } from '@augment-vir/assert';
|
|
3
|
+
import { getObjectTypedEntries, mergeDefinedProperties, } from '@augment-vir/common';
|
|
4
|
+
import { html } from 'element-vir';
|
|
5
|
+
import { BaseEntity2d, EntityStore2d, reverseParamsMap, ViewEntity2d, } from './entity.js';
|
|
6
|
+
function ensureEntityStore({ state, options, }) {
|
|
7
|
+
if (!state.pixi?.pixiApplication || !state.assetLoader) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
if (!state.entityStore) {
|
|
11
|
+
state.entityStore = new EntityStore2d(mergeDefinedProperties({
|
|
12
|
+
pixi: state.pixi.pixiApplication,
|
|
13
|
+
state,
|
|
14
|
+
assetLoader: state.assetLoader,
|
|
15
|
+
}, options));
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Creates an entity update mod and entity factories.
|
|
21
|
+
*
|
|
22
|
+
* @category Pre-built Mods
|
|
23
|
+
*/
|
|
24
|
+
export function createAnthaEntity2dSuite(options = {}) {
|
|
25
|
+
const entityKeys = new Set();
|
|
26
|
+
function createDefiner(entityParent) {
|
|
27
|
+
return (params) => {
|
|
28
|
+
if (params.assets) {
|
|
29
|
+
getObjectTypedEntries(params.assets).forEach(([key, rawAsset,]) => {
|
|
30
|
+
rawAsset.assetName = [
|
|
31
|
+
params.key,
|
|
32
|
+
key,
|
|
33
|
+
].join(':');
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return defineEntity(entityParent, params);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function defineEntity(entityParent, { collidesWith, key, paramsShape, paramsMap, assets }) {
|
|
40
|
+
if (entityKeys.has(key)) {
|
|
41
|
+
throw new Error(`Entity key '${key}' has already been attached to an entity class.`);
|
|
42
|
+
}
|
|
43
|
+
entityKeys.add(key);
|
|
44
|
+
const classWrapper = {
|
|
45
|
+
// @ts-expect-error: abstract methods are intentionally not implemented here
|
|
46
|
+
[key]: class extends entityParent {
|
|
47
|
+
static collidesWith = collidesWith;
|
|
48
|
+
static collidesWithSet = new Set(collidesWith?.collidesWithOtherEntities);
|
|
49
|
+
static entityKey = key;
|
|
50
|
+
static paramsShape = paramsShape;
|
|
51
|
+
static assets = assets || {};
|
|
52
|
+
static paramsMap = paramsMap;
|
|
53
|
+
static reverseParamsMap = reverseParamsMap(paramsMap);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
return assertWrap.isDefined(classWrapper[key]);
|
|
57
|
+
}
|
|
58
|
+
const updateEntitiesMod = defineAnthaMod({
|
|
59
|
+
modName: 'antha-entity-2d-update',
|
|
60
|
+
initState: {
|
|
61
|
+
showHitboxDebug: !!options.debug,
|
|
62
|
+
disableEntityRender: !!options.disableEntityRender,
|
|
63
|
+
disableEntityUpdate: !!options.disableEntityUpdate,
|
|
64
|
+
},
|
|
65
|
+
cleanup({ state }) {
|
|
66
|
+
state.entityStore?.destroy();
|
|
67
|
+
},
|
|
68
|
+
trigger: options.updateTrigger,
|
|
69
|
+
async execute(executeParams) {
|
|
70
|
+
if (!ensureEntityStore({
|
|
71
|
+
options,
|
|
72
|
+
state: executeParams.state,
|
|
73
|
+
})) {
|
|
74
|
+
return SkipExecution;
|
|
75
|
+
}
|
|
76
|
+
else if (executeParams.state.entityStore) {
|
|
77
|
+
if (!executeParams.state.disableEntityUpdate) {
|
|
78
|
+
await executeParams.state.entityStore.updateAllEntities(executeParams);
|
|
79
|
+
}
|
|
80
|
+
if (!executeParams.state.disableEntityRender) {
|
|
81
|
+
await executeParams.state.entityStore.renderAllEntities(executeParams);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return executeParams.state.showHitboxDebug
|
|
85
|
+
? html `
|
|
86
|
+
<canvas class="hitbox-debug-canvas"></canvas>
|
|
87
|
+
`
|
|
88
|
+
: undefined;
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
/**
|
|
93
|
+
* Updates entity data an/or transient render state, depending on the options you provided
|
|
94
|
+
* to `createAnthaEntity2dSuite`.
|
|
95
|
+
*/
|
|
96
|
+
updateEntitiesMod,
|
|
97
|
+
defineEntity: createDefiner(ViewEntity2d),
|
|
98
|
+
defineLogicEntity: createDefiner(BaseEntity2d),
|
|
99
|
+
entityKeys,
|
|
100
|
+
};
|
|
101
|
+
}
|
package/dist/entity.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { type Asset, type AssetLoader, type AssetValue } from '@antha/asset';
|
|
2
2
|
import { type ModExecuteParams } from '@antha/engine';
|
|
3
3
|
import { type PixiApplication } from '@antha/graphics-2d';
|
|
4
|
-
import { ConstructorInstanceMap, type AbstractConstructor, type AnyObject, type Constructor, type EmptyObject, type ExtractKeysWithMatchingValues, type IsEqual, type IsNever, type MaybePromise, type PartialWithUndefined, type WritableKeysOf } from '@augment-vir/common';
|
|
4
|
+
import { ConstructorInstanceMap, type AbstractConstructor, type AnyObject, type Constructor, type EmptyObject, type ExtractKeysWithMatchingValues, type IsAny, type IsEqual, type IsNever, type MaybePromise, type PartialWithUndefined, type WritableKeysOf } from '@augment-vir/common';
|
|
5
5
|
import { System as HitboxSystem, type Response as Collision, type Body as Hitbox } from 'detect-collisions';
|
|
6
6
|
import { type Shape } from 'object-shape-tester';
|
|
7
7
|
import { type Container, type ViewContainer } from 'pixi.js';
|
|
8
8
|
import { GenericListenTarget } from 'typed-event-target';
|
|
9
|
-
import { type StaticEntity2dParts } from './entity-suite.js';
|
|
10
9
|
export { System as HitboxSystem, type Response as Collision } from 'detect-collisions';
|
|
11
10
|
/**
|
|
12
11
|
* Definition of entity assets used when defining an entity.
|
|
@@ -74,6 +73,37 @@ export type EntityStore2dConstructorParams<State extends AnyObject = any> = {
|
|
|
74
73
|
* @category Internal
|
|
75
74
|
*/
|
|
76
75
|
export type Entity2dConstructor = Constructor<BaseEntity2d> & StaticEntity2dParts<any, any, BaseEntityAssetDefinitions>;
|
|
76
|
+
/**
|
|
77
|
+
* Static members of both view and logic entity constructors.
|
|
78
|
+
*
|
|
79
|
+
* @category Internal
|
|
80
|
+
*/
|
|
81
|
+
export type StaticEntity2dParts<State extends AnyObject = any, ParamsShape extends Shape | undefined = any, EntityAssets extends BaseEntityAssetDefinitions | undefined = any> = {
|
|
82
|
+
/** Entity classes this entity observes collisions with. Omit to observe none. */
|
|
83
|
+
collidesWith: EntityCollisionDefinition | undefined;
|
|
84
|
+
/** Cached entity classes this entity observes collisions with. */
|
|
85
|
+
collidesWithSet: ReadonlySet<Entity2dConstructor>;
|
|
86
|
+
/**
|
|
87
|
+
* This key is used for deserialization of entities to track which class needs to be
|
|
88
|
+
* constructed. You cannot have duplicate keys loaded at the same time.
|
|
89
|
+
*
|
|
90
|
+
* This is used instead of inferring the entity key from the class name so that you can still
|
|
91
|
+
* minify your class names without making debugging nigh impossible (you'll still know which
|
|
92
|
+
* entities are being serialized and deserialized even if your class names are minified).
|
|
93
|
+
*/
|
|
94
|
+
entityKey: string;
|
|
95
|
+
/** Shape definition of this entity's parameters. */
|
|
96
|
+
paramsShape: ParamsShape;
|
|
97
|
+
/**
|
|
98
|
+
* Defines which properties from {@link BaseEntity2d.params} will be mapped to hitbox and/or view
|
|
99
|
+
* properties.
|
|
100
|
+
*/
|
|
101
|
+
paramsMap: IsAny<ParamsShape> extends true ? any : ParamsMap<ParamsShape extends Shape ? ParamsShape['runtimeType'] : undefined>;
|
|
102
|
+
/** Parses the serialized params generated by {@link BaseEntity2d.serialize}. */
|
|
103
|
+
deserialize(serialized: string | undefined): AnyObject | undefined;
|
|
104
|
+
assets: MappedEntityAssets<EntityAssets>;
|
|
105
|
+
ConstructorArgsType: Entity2dConstructorParams<State, ParamsShape extends Shape ? ParamsShape['runtimeType'] : undefined>;
|
|
106
|
+
};
|
|
77
107
|
/**
|
|
78
108
|
* Defines which entity classes this entity observes collisions with.
|
|
79
109
|
*
|
|
@@ -139,10 +169,10 @@ export declare class EntityStore2d<State extends AnyObject = any> {
|
|
|
139
169
|
* Runs `.update()` on all current entities and runs collision detection for all hitboxes. If
|
|
140
170
|
* any entities get marked as destroyed during their update, then they will be removed from the
|
|
141
171
|
* set of entities.
|
|
142
|
-
*
|
|
143
|
-
* @returns All detected hitbox collisions (if any).
|
|
144
172
|
*/
|
|
145
173
|
updateAllEntities(updateParams: Readonly<ModExecuteParams<NoInfer<State>>>): Promise<void>;
|
|
174
|
+
/** Runs presentation-only updates for every entity without simulating collisions. */
|
|
175
|
+
renderAllEntities(renderParams: Readonly<ModExecuteParams<NoInfer<State>>>): Promise<void>;
|
|
146
176
|
/** Get all current instances of the given entity class constructor. */
|
|
147
177
|
getEntities<T>(entityClassConstructor: AbstractConstructor<T> | Constructor<T>): Set<T>;
|
|
148
178
|
/** Remove an entity from the store. */
|
|
@@ -299,6 +329,12 @@ export declare const position2dParamsMap: {
|
|
|
299
329
|
* @category Internal
|
|
300
330
|
*/
|
|
301
331
|
export type ReverseParamsMap = Record<string, Partial<Record<'hitbox' | 'view', string[]>>>;
|
|
332
|
+
/**
|
|
333
|
+
* Converts {@link ParamsMap} to {@link ReverseParamsMap}.
|
|
334
|
+
*
|
|
335
|
+
* @category Internal
|
|
336
|
+
*/
|
|
337
|
+
export declare function reverseParamsMap(paramsMap: ParamsMap | undefined): ReverseParamsMap | undefined;
|
|
302
338
|
/**
|
|
303
339
|
* Base entity class, types, and functionality.
|
|
304
340
|
*
|
|
@@ -357,6 +393,12 @@ export declare abstract class BaseEntity2d<State extends AnyObject = any, Params
|
|
|
357
393
|
* entity definition classes.
|
|
358
394
|
*/
|
|
359
395
|
abstract update(updateParams: Readonly<ModExecuteParams<NoInfer<State>>>): MaybePromise<void>;
|
|
396
|
+
/**
|
|
397
|
+
* This is meant to be used to update transient render state without changing the authoritative
|
|
398
|
+
* entity state or logic. This is optional. `updateEntitiesMod` calls it (if it exists) after
|
|
399
|
+
* each entity data update unless `disableEntityRender` is `true`.
|
|
400
|
+
*/
|
|
401
|
+
render(_renderParams: Readonly<ModExecuteParams<NoInfer<State>>>): MaybePromise<void>;
|
|
360
402
|
/** Called after construction to perform async initialization (e.g. creating views). */
|
|
361
403
|
initInstance(): MaybePromise<void>;
|
|
362
404
|
/** The game's current state. */
|
package/dist/entity.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assert, check } from '@augment-vir/assert';
|
|
2
|
-
import { ConstructorInstanceMap, getObjectTypedEntries, makeWritable, mapObjectValues, } from '@augment-vir/common';
|
|
2
|
+
import { ConstructorInstanceMap, getObjectTypedEntries, getOrSet, makeWritable, mapObjectValues, } from '@augment-vir/common';
|
|
3
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';
|
|
@@ -174,8 +174,6 @@ export class EntityStore2d {
|
|
|
174
174
|
* Runs `.update()` on all current entities and runs collision detection for all hitboxes. If
|
|
175
175
|
* any entities get marked as destroyed during their update, then they will be removed from the
|
|
176
176
|
* set of entities.
|
|
177
|
-
*
|
|
178
|
-
* @returns All detected hitbox collisions (if any).
|
|
179
177
|
*/
|
|
180
178
|
async updateAllEntities(updateParams) {
|
|
181
179
|
if (this.isDestroyed) {
|
|
@@ -229,6 +227,18 @@ export class EntityStore2d {
|
|
|
229
227
|
});
|
|
230
228
|
await Promise.all(collisionPromises);
|
|
231
229
|
}
|
|
230
|
+
/** Runs presentation-only updates for every entity without simulating collisions. */
|
|
231
|
+
async renderAllEntities(renderParams) {
|
|
232
|
+
if (this.isDestroyed) {
|
|
233
|
+
throw new Error('Cannot operate on a destroyed entity store.');
|
|
234
|
+
}
|
|
235
|
+
for (const entity of this.currentEntityInstances) {
|
|
236
|
+
if (entity.isDestroyed) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
await entity.render(renderParams);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
232
242
|
/** Get all current instances of the given entity class constructor. */
|
|
233
243
|
getEntities(entityClassConstructor) {
|
|
234
244
|
return this.entityInstanceMap.getInstances(entityClassConstructor);
|
|
@@ -361,6 +371,34 @@ export const position2dParamsMap = {
|
|
|
361
371
|
y: true,
|
|
362
372
|
},
|
|
363
373
|
};
|
|
374
|
+
/**
|
|
375
|
+
* Converts {@link ParamsMap} to {@link ReverseParamsMap}.
|
|
376
|
+
*
|
|
377
|
+
* @category Internal
|
|
378
|
+
*/
|
|
379
|
+
export function reverseParamsMap(paramsMap) {
|
|
380
|
+
if (!paramsMap) {
|
|
381
|
+
return undefined;
|
|
382
|
+
}
|
|
383
|
+
const reverseParamsMap = {};
|
|
384
|
+
getObjectTypedEntries(paramsMap).forEach(([topKey, mappings,]) => {
|
|
385
|
+
getObjectTypedEntries(mappings).forEach(([mapToKey, mapFromKey,]) => {
|
|
386
|
+
if (!mapFromKey) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const paramMapping = getOrSet(reverseParamsMap, check.isString(mapFromKey) ? mapFromKey : mapToKey, () => {
|
|
390
|
+
return {};
|
|
391
|
+
});
|
|
392
|
+
const mapToArray = getOrSet(paramMapping, topKey, () => {
|
|
393
|
+
return [];
|
|
394
|
+
});
|
|
395
|
+
if (!mapToArray.includes(mapToKey)) {
|
|
396
|
+
mapToArray.push(mapToKey);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
return reverseParamsMap;
|
|
401
|
+
}
|
|
364
402
|
/**
|
|
365
403
|
* Base entity class, types, and functionality.
|
|
366
404
|
*
|
|
@@ -438,6 +476,13 @@ export class BaseEntity2d {
|
|
|
438
476
|
entityAssets: assets,
|
|
439
477
|
});
|
|
440
478
|
}
|
|
479
|
+
/**
|
|
480
|
+
* This is meant to be used to update transient render state without changing the authoritative
|
|
481
|
+
* entity state or logic. This is optional. `updateEntitiesMod` calls it (if it exists) after
|
|
482
|
+
* each entity data update unless `disableEntityRender` is `true`.
|
|
483
|
+
*/
|
|
484
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
485
|
+
render(_renderParams) { }
|
|
441
486
|
/** Called after construction to perform async initialization (e.g. creating views). */
|
|
442
487
|
initInstance() {
|
|
443
488
|
return;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antha/entity-2d",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "An Antha mod for handling 2D entities.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vir",
|
|
@@ -39,8 +39,11 @@
|
|
|
39
39
|
"@augment-vir/common": "^32.3.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@antha/
|
|
43
|
-
"@antha/
|
|
42
|
+
"@antha/asset": "^0.18.0",
|
|
43
|
+
"@antha/audio": "^0.18.0",
|
|
44
|
+
"@antha/engine": "^0.18.0",
|
|
45
|
+
"@antha/graphics-2d": "^0.18.0",
|
|
46
|
+
"@antha/web-test-runner-plugin-pixi": "^0.18.0",
|
|
44
47
|
"@augment-vir/test": "^32.3.0",
|
|
45
48
|
"@web/dev-server-esbuild": "^2.0.0",
|
|
46
49
|
"@web/test-runner": "^1.0.0",
|
|
@@ -52,10 +55,10 @@
|
|
|
52
55
|
"typed-event-target": "^4.3.3"
|
|
53
56
|
},
|
|
54
57
|
"peerDependencies": {
|
|
55
|
-
"@antha/asset": "^0.
|
|
56
|
-
"@antha/audio": "^0.
|
|
57
|
-
"@antha/engine": "^0.
|
|
58
|
-
"@antha/graphics-2d": "^0.
|
|
58
|
+
"@antha/asset": "^0.18.0",
|
|
59
|
+
"@antha/audio": "^0.18.0",
|
|
60
|
+
"@antha/engine": "^0.18.0",
|
|
61
|
+
"@antha/graphics-2d": "^0.18.0",
|
|
59
62
|
"detect-collisions": "^10",
|
|
60
63
|
"element-vir": "^27",
|
|
61
64
|
"object-shape-tester": "^6",
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { type AnthaAssetModState } from '@antha/asset';
|
|
2
|
-
import { type AnthaMod } from '@antha/engine';
|
|
3
|
-
import { type AnthaGraphics2dModState } from '@antha/graphics-2d';
|
|
4
|
-
import { type AnyObject, type PartialWithUndefined } from '@augment-vir/common';
|
|
5
|
-
import { type EntityStore2d, type EntityStore2dConstructorParams } from './entity.js';
|
|
6
|
-
/**
|
|
7
|
-
* State for {@link createAnthaEntityMod2d}.
|
|
8
|
-
*
|
|
9
|
-
* @category Internal
|
|
10
|
-
*/
|
|
11
|
-
export type AnthaEntity2dModState<State extends AnyObject = AnyObject> = {
|
|
12
|
-
entityStore: EntityStore2d<Partial<AnthaEntity2dModState<State>>>;
|
|
13
|
-
/** If true, entity updates and collision checks are skipped. */
|
|
14
|
-
disableEntityUpdates: boolean;
|
|
15
|
-
/** If `true`, hit boxes are visually rendered for debugging purposes. */
|
|
16
|
-
debugHitboxes: boolean;
|
|
17
|
-
} & State & AnthaGraphics2dModState & AnthaAssetModState;
|
|
18
|
-
/**
|
|
19
|
-
* Options for {@link createAnthaEntityMod2d}.
|
|
20
|
-
*
|
|
21
|
-
* @category Internal
|
|
22
|
-
*/
|
|
23
|
-
export type AnthaEntity2dModOptions = PartialWithUndefined<EntityStore2dConstructorParams & {
|
|
24
|
-
debug: boolean;
|
|
25
|
-
}>;
|
|
26
|
-
/**
|
|
27
|
-
* A mod for rendering entities and handling collisions between them.
|
|
28
|
-
*
|
|
29
|
-
* @category Pre-built Mods
|
|
30
|
-
*/
|
|
31
|
-
export declare function createAnthaEntityMod2d<ExtraState extends AnyObject>(options?: Readonly<AnthaEntity2dModOptions>): {
|
|
32
|
-
defineEntity: import("./entity-suite.js").DefineViewEntity2d<AnthaEntity2dModState<ExtraState>>;
|
|
33
|
-
defineLogicEntity: import("./entity-suite.js").DefineLogicEntity2d<AnthaEntity2dModState<ExtraState>>;
|
|
34
|
-
entityKeys: Set<string>;
|
|
35
|
-
mod: AnthaMod<AnthaEntity2dModState<ExtraState>>;
|
|
36
|
-
};
|
package/dist/antha-entity.mod.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { anthaAssetModName, AssetLoader } from '@antha/asset';
|
|
2
|
-
import { defineAnthaMod, SkipExecution } from '@antha/engine';
|
|
3
|
-
import { mergeDefinedProperties, } from '@augment-vir/common';
|
|
4
|
-
import { html } from 'element-vir';
|
|
5
|
-
import { defineEntitySuite2d } from './entity-suite.js';
|
|
6
|
-
/**
|
|
7
|
-
* A mod for rendering entities and handling collisions between them.
|
|
8
|
-
*
|
|
9
|
-
* @category Pre-built Mods
|
|
10
|
-
*/
|
|
11
|
-
export function createAnthaEntityMod2d(options = {}) {
|
|
12
|
-
const { EntityStore, ...entitySuite } = defineEntitySuite2d();
|
|
13
|
-
const mod = defineAnthaMod({
|
|
14
|
-
modName: 'antha-entity-2d',
|
|
15
|
-
initState: {
|
|
16
|
-
debugHitboxes: !!options.debug,
|
|
17
|
-
},
|
|
18
|
-
cleanup({ state }) {
|
|
19
|
-
state.entityStore?.destroy();
|
|
20
|
-
},
|
|
21
|
-
async execute(executeParams) {
|
|
22
|
-
/**
|
|
23
|
-
* If we don't have a mod that is expected to create the asset loader, then we create
|
|
24
|
-
* one ourself.
|
|
25
|
-
*/
|
|
26
|
-
if (!executeParams.state.assetLoader &&
|
|
27
|
-
!executeParams.engine.currentMods.some((mod) => mod.modName === anthaAssetModName)) {
|
|
28
|
-
executeParams.state.assetLoader = new AssetLoader();
|
|
29
|
-
}
|
|
30
|
-
const pixiApplication = executeParams.state.pixi?.pixiApplication;
|
|
31
|
-
if (!pixiApplication) {
|
|
32
|
-
return SkipExecution;
|
|
33
|
-
}
|
|
34
|
-
if (executeParams.state.entityStore) {
|
|
35
|
-
if (!executeParams.state.disableEntityUpdates) {
|
|
36
|
-
await executeParams.state.entityStore.updateAllEntities(executeParams);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
else if (executeParams.state.assetLoader) {
|
|
40
|
-
executeParams.state.entityStore = new EntityStore(mergeDefinedProperties({
|
|
41
|
-
pixi: pixiApplication,
|
|
42
|
-
state: executeParams.state,
|
|
43
|
-
assetLoader: executeParams.state.assetLoader,
|
|
44
|
-
}, options));
|
|
45
|
-
}
|
|
46
|
-
if (executeParams.state.debugHitboxes) {
|
|
47
|
-
return html `
|
|
48
|
-
<canvas class="hitbox-debug-canvas"></canvas>
|
|
49
|
-
`;
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
return undefined;
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
return {
|
|
57
|
-
mod,
|
|
58
|
-
...entitySuite,
|
|
59
|
-
};
|
|
60
|
-
}
|
package/dist/entity-suite.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { check } from '@augment-vir/assert';
|
|
2
|
-
import { getObjectTypedEntries, getOrSet, } from '@augment-vir/common';
|
|
3
|
-
import { BaseEntity2d, EntityStore2d, ViewEntity2d, } from './entity.js';
|
|
4
|
-
/**
|
|
5
|
-
* This is the starting point of the @game-vir/entity package. Call this to produce the function
|
|
6
|
-
* needed to define new entities and the store needed to add entity instances.
|
|
7
|
-
*
|
|
8
|
-
* @category Main
|
|
9
|
-
*/
|
|
10
|
-
export function defineEntitySuite2d() {
|
|
11
|
-
const entityKeys = new Set();
|
|
12
|
-
function createDefiner(entityParent) {
|
|
13
|
-
return (params) => {
|
|
14
|
-
if (params.assets) {
|
|
15
|
-
getObjectTypedEntries(params.assets).forEach(([key, rawAsset,]) => {
|
|
16
|
-
rawAsset.assetName = [
|
|
17
|
-
params.key,
|
|
18
|
-
key,
|
|
19
|
-
].join(':');
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
return defineEntity(entityParent, params);
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function defineEntity(entityParent, { collidesWith, key, paramsShape, paramsMap, assets }) {
|
|
26
|
-
if (entityKeys.has(key)) {
|
|
27
|
-
throw new Error(`Entity key '${key}' has already been attached to an entity class.`);
|
|
28
|
-
}
|
|
29
|
-
entityKeys.add(key);
|
|
30
|
-
const classWrapper = {
|
|
31
|
-
// @ts-expect-error: abstract methods are intentionally not implemented here
|
|
32
|
-
[key]: class extends entityParent {
|
|
33
|
-
static collidesWith = collidesWith;
|
|
34
|
-
static collidesWithSet = new Set(collidesWith?.collidesWithOtherEntities);
|
|
35
|
-
static entityKey = key;
|
|
36
|
-
static paramsShape = paramsShape;
|
|
37
|
-
static assets = assets || {};
|
|
38
|
-
static paramsMap = paramsMap;
|
|
39
|
-
static reverseParamsMap = reverseParamsMap(paramsMap);
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
43
|
-
return classWrapper[key];
|
|
44
|
-
}
|
|
45
|
-
return {
|
|
46
|
-
EntityStore: EntityStore2d,
|
|
47
|
-
defineEntity: createDefiner(ViewEntity2d),
|
|
48
|
-
defineLogicEntity: createDefiner(BaseEntity2d),
|
|
49
|
-
entityKeys,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Converts {@link ParamsMap} to {@link ReverseParamsMap}.
|
|
54
|
-
*
|
|
55
|
-
* @category Internal
|
|
56
|
-
*/
|
|
57
|
-
export function reverseParamsMap(paramsMap) {
|
|
58
|
-
if (!paramsMap) {
|
|
59
|
-
return undefined;
|
|
60
|
-
}
|
|
61
|
-
const reverseParamsMap = {};
|
|
62
|
-
getObjectTypedEntries(paramsMap).forEach(([topKey, mappings,]) => {
|
|
63
|
-
getObjectTypedEntries(mappings).forEach(([mapToKey, mapFromKey,]) => {
|
|
64
|
-
if (!mapFromKey) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
const paramMapping = getOrSet(reverseParamsMap, check.isString(mapFromKey) ? mapFromKey : mapToKey, () => {
|
|
68
|
-
return {};
|
|
69
|
-
});
|
|
70
|
-
const mapToArray = getOrSet(paramMapping, topKey, () => {
|
|
71
|
-
return [];
|
|
72
|
-
});
|
|
73
|
-
if (!mapToArray.includes(mapToKey)) {
|
|
74
|
-
mapToArray.push(mapToKey);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
return reverseParamsMap;
|
|
79
|
-
}
|