-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathWorld.d.ts
More file actions
290 lines (246 loc) · 11 KB
/
World.d.ts
File metadata and controls
290 lines (246 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
import { AnyComponent, ComponentBundle, ComponentCtor, DynamicBundle, InferComponents } from "./component";
/**
* Nominal type to describe an entity with an expressed set of associated components.
* @typeParam T - Bundle of component values
*/
type Entity1<T extends ComponentBundle> = T;
type Entity2<T extends ComponentBundle> = T[number];
type Permutation<T extends unknown[], Prev extends unknown[] = []> = T extends [infer First, ...infer Rest]
? [First, ...Permutation<[...Prev, ...Rest]>] | (Rest extends [] ? never : Permutation<Rest, [...Prev, First]>)
: [];
type Entity3<T extends ComponentBundle> = Permutation<T>;
declare const withTag: unique symbol;
export type With<T extends ComponentBundle> = Permutation<T>;
/**
* @hidden
* @deprecated
*/
declare const archetype: unique symbol;
type Entity5<T extends ComponentBundle, S = T[number]> = [S] extends [any] ? S[] : never;
export type Entity<T = ComponentBundle> = number & {
[archetype]: T;
};
export type GenericOfEntity<T> = T extends Entity<infer a> ? a : never;
/**
* AnyEntity is a plain number, and can be used as such or be casted back and forth, however it upholds a type contract that prevents accidental misuse by enforcing
* developers to think about what they really wanted to use.
*/
export type AnyEntity = Entity;
type Equals<A1, A2> = (<A>() => A extends A2 ? 1 : 0) extends <A>() => A extends A1 ? 1 : 0 ? 1 : 0;
type Includes<T, V> = T extends [infer F, ...infer R] ? (Equals<F, V> extends 1 ? true : Includes<R, V>) : false;
type IncludesAll<T extends ReadonlyArray<unknown>, S extends ReadonlyArray<unknown>> = Equals<
{ [P in keyof S]: Includes<T, S[P]> }[number],
true
> extends 1
? true
: false;
type NullableArray<A extends Array<unknown>> = Partial<A> extends Array<unknown> ? Partial<A> : never;
/**
* @class World
*
* A World contains entities which have components.
* The World is queryable and can be used to get entities with a specific set of components.
* Entities are simply ever-increasing integers.
*/
export interface World extends IterableFunction<LuaTuple<[AnyEntity, Map<ComponentCtor, AnyComponent>]>> {}
export class World {
public constructor();
/**
* Spawns a new entity in the world with the given components.
* @param component_bundle - The component values to spawn the entity with.
* @return The new entity ID.
*/
public spawn<T extends ComponentBundle>(...component_bundle: T): Entity<T>;
/**
* Spawns a new entity in the world with a specific entity ID and given components.
* The next ID generated from [World:spawn] will be increased as needed to never collide with a manually specified ID.
* @param id - The entity ID to spawn with.
* @param component_bundle - The component values to spawn the entity with.
* @see {@link id Entity}
*/
public spawnAt<T extends ComponentBundle>(id: number, ...component_bundle: T): Entity<T>;
/**
* Replaces a given entity by ID with an entirely new set of components.
* Equivalent to removing all components from an entity, and then adding these ones.
* @param id - The entity ID
* @param component_bundle {@link ComponentBundle ComponentBundle} - The component values to spawn the entity with.
* @see {@link id AnyEntity}
*/
public replace<T extends ComponentBundle>(id: AnyEntity, ...component_bundle: T): Entity<T>;
/**
* Despawns a given entity by ID, removing it and all its components from the world entirely.
* @param id - The entity ID
* @see {@link id AnyEntity}
*/
public despawn(id: AnyEntity): void;
/**
* Removes all entities from the world.
* @remarks
* Removing entities in this is not reported by {@link queryChanged queryChanged}
*/
public clear(): void;
/**
* Checks if the given entity ID is currently spawned in this world.
*
* @param id number - The entity ID
* @returns boolean - `true` if the entity exists
* @see {@link AnyEntity AnyEntity}
*/
public contains(id: AnyEntity): boolean;
/**
* Gets a specific component from a specific entity in this world.
*
* @param entity - The entity ID
* @param only - The component to fetch
* @returns Returns the component values in the same order they were passed to.
* @remarks
* Component value returned is nullable if it isn't associated with the entity (in real-time).
*/
public get<T extends ComponentCtor>(entity: AnyEntity, only: T): ReturnType<T> | undefined;
/**
* Gets multiple components from a specific entity in this world.
*
* @param entity - The entity ID
* @param bundle - The components to fetch
* @returns Returns the component values in the same order they were passed to.
* @remarks
* Component values returned are nullable if the components used to search for aren't associated with the entity (in real-time).
*/
public get<a extends Entity, T extends ComponentCtor>(
entity: a,
only: T,
): Includes<a extends Entity<infer A> ? A : never, ReturnType<T>> extends true
? ReturnType<T>
: ReturnType<T> | undefined;
public get<T extends DynamicBundle>(entity: AnyEntity, ...bundle: T): LuaTuple<NullableArray<InferComponents<T>>>;
public get<T extends DynamicBundle>(entity: Entity<T>, ...bundle: T): LuaTuple<InferComponents<T>>;
public get<a, T extends DynamicBundle>(
entity: a,
...bundle: T
): LuaTuple<a extends Entity<InferComponents<T>> ? InferComponents<T> : NullableArray<InferComponents<T>>>;
/**
* Performs a query against the entities in this World. Returns a [QueryResult](/api/QueryResult), which iterates over
* the results of the query.
* Order of iteration is not guaranteed.
* @param dynamic_bundle
* @returns QueryResult - See {@link QueryResult QueryResult}
*/
public query<T extends DynamicBundle, a extends InferComponents<T>>(...dynamic_bundle: T): QueryResult<a>;
public queryChanged<C extends ComponentCtor>(
mt: C,
): IterableFunction<
LuaTuple<[Entity<[ReturnType<C>]>, { new: ReturnType<C> | undefined; old: ReturnType<C> | undefined }]>
>;
public insert(id: AnyEntity, ...dynamic_bundle: ComponentBundle): void;
public remove<T extends DynamicBundle>(id: AnyEntity, ...dynamic_bundle: T): LuaTuple<InferComponents<T>>;
public size(): number;
public optimizeQueries(): void;
}
type Query<T extends ComponentBundle> = IterableFunction<LuaTuple<[Entity<T>, ...T]>>;
/**
* @class QueryResult
*
* A result from the {@link query World.query} function.
*
* @remarks
* Calling the table or the `next` method allows iteration over the results. Once all results have returned, the
* QueryResult is exhausted and is no longer useful
*/
type QueryResult<T extends ComponentBundle> = Query<T> & {
/**
* Returns an iterator that will skip any entities that also have the given components.
*
* @remarks
* This is essentially equivalent to querying normally, using `World:get` to check if a component is present,
* and using Lua's `continue` keyword to skip this iteration (though, using `:without` is faster).
*
* This means that you should avoid queries that return a very large amount of results only to filter them down
* to a few with `:without`. If you can, always prefer adding components and making your query more specific.
*
* @param ...components - The component types to filter against.
* @returns IterableFunction<LuaTuple<[Entity<ComponentBundle>, ...ComponentBundle]>> - Iterator of entity ID followed by the requested component values
*
* ```ts
* for (const [id] of world.query(Target).without(Model)) {
* // Do something
* }
* ```
*/
without: (this: Query<T>, ...components: DynamicBundle) => QueryResult<T>;
/**
* Returns the next set of values from the query result. Once all results have been returned, the
* QueryResult is exhausted and is no longer useful.
*
* @remarks
* This function is equivalent to calling the QueryResult as a function. When used in a for loop, this is implicitly
* done by the language itself.
*
* ```ts
* // Using world.query in this position will make Lua invoke the table as a function. This is conventional.
* for (const [id, enemy, charge, model] of world.query(Enemy, Charge, Model)) {
* // Do something
* }
* ```
*
* If you wanted to iterate over the QueryResult without a for loop, it's recommended that you call `next` directly
* instead of calling the QueryResult as a function.
* ```lua
* const [id, enemy, charge, model] = world.query(Enemy, Charge, Model).next()
* const [id, enemy, charge, model] = world.query(Enemy, Charge, Model)() -- Possible, but unconventional
* ```
* @returns A LuaTuple of an entity followed with queried components
* @See {@link Entity Entity}
*/
next: (this: Query<T>) => LuaTuple<[Entity<T>, ...T] | [undefined, ...{ [k in keyof T]: undefined }]>;
/**
* Creates a "snapshot" of this query, draining this QueryResult and returning a list containing all of its results.
*
* By default, iterating over a QueryResult happens in "real time": it iterates over the actual data in the ECS, so
* changes that occur during the iteration will affect future results.
*
* By contrast, `QueryResult:snapshot()` creates a list of all of the results of this query at the moment it is called,
* so changes made while iterating over the result of `QueryResult:snapshot` do not affect future results of the
* iteration.
* Of course, this comes with a cost: we must allocate a new list and iterate over everything returned from the
* QueryResult in advance, so using this method is slower than iterating over a QueryResult directly.
* The table returned from this method has a custom `__iter` method, which lets you use it as you would use QueryResult
* directly:
* ```ts
* for (const [entityId, health, player] of world.query(Health, Player).snapshot()) {
*
* }
* However, the table itself is just a list of sub-tables structured like `[entityId, component1, component2, ...etc]`
* @returns ReadonlyArray<[Entity<ComponentBundle>, ...ComponentBundle]>
```
However, the table itself is just a list of sub-tables structured like `{entityId, component1, component2, ...etc}`.
*/
snapshot: (this: Query<T>) => ReadonlyArray<[Entity<T>, ...T]>;
/**
* Creates a View of the query and does all of the iterator tasks at once at an amortized cost.
This is used for many repeated random access to an entity. If you only need to iterate, just use a query.
```ts
const inflicting = world.query(Damage, Hitting, Player).view()
for (const [_, source] of world.query(DamagedBy)) {
const damage = inflicting.get(source.from)
}
for (const [, damage] of world.query(Damage).view()) {} -- You can still iterate views if you want!
```
@returns View See [View](/api/View) docs.
*/
view: (this: Query<T>) => View<T>;
};
type View<T extends ComponentBundle> = Query<T> & {
get<E>(
entity: E,
): T extends [AnyComponent]
? E extends Entity<T>
? T[0]
: T[0] | undefined
: LuaTuple<E extends Entity<T> ? T : NullableArray<T>>;
contains: (this: View<T>, id: AnyEntity) => boolean;
};
export type FilterOut<T extends Array<unknown>, F> = T extends [infer L, ...infer R]
? [L] extends [F]
? [...FilterOut<R, F>]
: [L, ...FilterOut<R, F>]
: [];