@whatever-engine/api 0.1.1 → 0.2.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.
package/README.md CHANGED
@@ -5,14 +5,14 @@ TypeScript scripting API for Whatever Engine mods. Abstracts the NDJSON IPC prot
5
5
  ## Usage
6
6
 
7
7
  ```ts
8
- import { Engine, Window, Scene, Assets, File, Mods, Message, Console } from "@whatever-engine/api";
8
+ import { Engine, Window, File, Scene, Entity, Mods, Message, Console } from "@whatever-engine/api";
9
9
 
10
10
  Engine.on("init", ({ mod_id }) => {
11
11
  Engine.log("info", `loaded as ${mod_id}`);
12
12
  });
13
13
 
14
- Engine.on("frame", ({ delta_seconds }) => {
15
- // per-frame logic
14
+ Engine.on("tick", async ({ delta_seconds }) => {
15
+ // game logic — engine waits for this Promise before advancing
16
16
  });
17
17
  ```
18
18
 
@@ -22,22 +22,17 @@ No install step needed — Bun resolves the package automatically for any script
22
22
 
23
23
  ### `Engine`
24
24
 
25
- - `Engine.on(event, handler)` — subscribe to an engine event (`init`, `exit`, `frame`, `input`, `asset_response`, `mod_message`)
25
+ - `Engine.on(event, handler)` — subscribe to an engine event (`init`, `exit`, `tick`, `mod_message`)
26
26
  - `Engine.log(level, message)` — log through the engine logger (`"info"`, `"warn"`, `"error"`)
27
+ - `Engine.setTickRate(ticks_per_second)` — override the game tick rate at runtime
28
+ - `Engine.setFpsCap(fps | null)` — set FPS cap; `null` = uncapped
29
+ - `Engine.setVsync(enabled)` — enable or disable vertical sync
27
30
 
28
31
  ### `Window`
29
32
 
30
33
  - `Window.setTitle(title)` — change the window title
31
-
32
- ### `Scene`
33
-
34
- - `Scene.spawnSprite(entity_id, texture, position, scale?)` — spawn a textured sprite
35
- - `Scene.moveEntity(entity_id, position)` — move an entity to a new world-space position
36
- - `Scene.destroyEntity(entity_id)` — remove an entity from the scene
37
-
38
- ### `Assets`
39
-
40
- - `Assets.request(request_id, path)` — request raw asset bytes from the VFS; result arrives as an `asset_response` event
34
+ - `Window.setSize(width, height)` — request a new inner size in physical pixels (no-op in fullscreen modes)
35
+ - `Window.setMode(mode)` — set display mode: `"windowed"`, `"borderless"`, or `"fullscreen"`
41
36
 
42
37
  ### `File`
43
38
 
@@ -47,6 +42,52 @@ Sandboxed per-mod file I/O. Paths must not contain `..`.
47
42
  - `File.read(path)` → `Promise<string>`
48
43
  - `File.delete(path)` → `Promise<void>`
49
44
 
45
+ ### `Entity`
46
+
47
+ A live entity in the scene. Returned by `Scene.createEntity`, `Scene.listEntities`, `Scene.query`, and `Scene.spawnSprite`. For built-in component types the data parameter/return is automatically typed; for custom component types a generic `T` can be supplied.
48
+
49
+ - `entity.id` — opaque entity ID string
50
+ - `entity.destroy()` — fire-and-forget
51
+ - `entity.setComponent(component_type, data)` — fire-and-forget; typed for built-in components
52
+ - `entity.removeComponent(component_type)` — fire-and-forget
53
+ - `entity.getComponent(component_type)` → `Promise<T | null>` — typed for built-in components
54
+ - `entity.move(position)` → `Promise<void>` — update `core:transform` position, preserve rotation/scale
55
+
56
+ ### `Scene`
57
+
58
+ Entity and component management. Methods that accept a raw `entity_id` string are provided for cases where only an ID is available; prefer `Entity` methods when possible.
59
+
60
+ - `Scene.createEntity()` → `Promise<Entity>`
61
+ - `Scene.destroyEntity(entity_id)` — fire-and-forget
62
+ - `Scene.listEntities()` → `Promise<Entity[]>`
63
+ - `Scene.setComponent(entity_id, component_type, data)` — fire-and-forget; typed for built-in components
64
+ - `Scene.removeComponent(entity_id, component_type)` — fire-and-forget
65
+ - `Scene.getComponent(entity_id, component_type)` → `Promise<T | null>` — typed for built-in components
66
+ - `Scene.query(component_types)` → `Promise<QueryResult[]>` — each row has `entity: Entity` and `components`
67
+ - `Scene.spawnSprite(texture, position, scale?)` → `Promise<Entity>` — convenience
68
+ - `Scene.moveEntity(entity_id, position)` → `Promise<void>` — convenience
69
+
70
+ Built-in component types: `core:transform`, `core:sprite_renderer`. `getComponent` for built-in types returns a **live class instance** with methods — not a plain object.
71
+
72
+ ### `BuiltInComponents.Transform`
73
+
74
+ Methods (all setters chainable, return `this`):
75
+
76
+ - `getX/Y/Z()`, `setX/Y/Z(v)` — individual position components
77
+ - `getPosition()` → `[x, y, z]`, `setPosition(x, y, z)` — full position
78
+ - `getScaleX/Y/Z()`, `setScaleX/Y/Z(v)` — individual scale components
79
+ - `getScale()` → `[x, y, z]`, `setScale(x, y, z)`, `setScaleUniform(s)` — full scale
80
+ - `getRotation()` → `[x, y, z, w]`, `setRotation(x, y, z, w)` — raw quaternion
81
+ - `getEulerRadians/Degrees()` → `[rx, ry, rz]` — intrinsic XYZ decomposition
82
+ - `setEulerRadians/Degrees(rx, ry, rz)` — set from intrinsic XYZ Euler angles
83
+ - `rotateX/Y/Z(degrees)` — incremental world-space rotation around axis
84
+ - `distance(other)` → `number` — Euclidean distance between positions
85
+
86
+ ### `BuiltInComponents.SpriteRenderer`
87
+
88
+ - `getTexture()`, `setTexture(path)` — VFS texture path
89
+ - `getZIndex()`, `setZIndex(z)` — draw order
90
+
50
91
  ### `Mods`
51
92
 
52
93
  - `Mods.list()` → `Promise<ModManifest[]>` — all loaded mods in load order
@@ -0,0 +1,390 @@
1
+ // Generated by dts-bundle-generator v9.5.1
2
+
3
+ /** Base interface for all components. */
4
+ export interface Component {
5
+ id: string;
6
+ }
7
+ /** Arbitrary JSON-serializable value used for inter-mod messages and components. */
8
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
9
+ [key: string]: JsonValue;
10
+ };
11
+ /** Metadata about a loaded mod, mirroring mod.toml. */
12
+ export type ModManifest = {
13
+ id: string;
14
+ name: string;
15
+ version: string;
16
+ description: string;
17
+ authors: string[];
18
+ license: string;
19
+ dependencies: Record<string, string>;
20
+ load_order: {
21
+ after: string[];
22
+ before: string[];
23
+ };
24
+ script?: {
25
+ entry: string;
26
+ runtime: string;
27
+ };
28
+ };
29
+ /** Payload types for each public event name. */
30
+ export type EventPayloads = {
31
+ /** Fired once after the engine has initialised and the window is ready. */
32
+ init: {
33
+ mod_id: string;
34
+ engine_version: string;
35
+ };
36
+ /** Fired when the game is shutting down. The process exits after all handlers return. */
37
+ exit: {
38
+ exit_code: number;
39
+ };
40
+ /**
41
+ * Fired every game tick. All async handlers are awaited before the engine advances.
42
+ * Subscribe by calling `Engine.on("tick", handler)`.
43
+ */
44
+ tick: {
45
+ tick_number: number;
46
+ delta_seconds: number;
47
+ keys_pressed: string[];
48
+ mouse_delta: [
49
+ number,
50
+ number
51
+ ];
52
+ };
53
+ /**
54
+ * Fired when another mod sends this mod a message via `Message.send`.
55
+ * If `request_id` is present the sender is awaiting a reply — call `Message.reply(request_id, data)`.
56
+ * Not fired for replies that arrive via the `Message.send(id, msg, timeout)` overload.
57
+ */
58
+ mod_message: {
59
+ source_mod_id: string;
60
+ message: JsonValue;
61
+ request_id?: string;
62
+ };
63
+ };
64
+ export type EventName = keyof EventPayloads;
65
+ /** One row returned by `Scene.query`. */
66
+ export type QueryResult<E> = {
67
+ entity: E;
68
+ components: Record<string, JsonValue>;
69
+ };
70
+ /** Core engine events and logging. */
71
+ export declare const Engine: {
72
+ /**
73
+ * Subscribe to an engine event. The handler is called each time the event fires.
74
+ * For `tick`, async handlers are awaited before the engine advances the simulation.
75
+ * Registering a handler also sends a `Subscribe` message to the engine automatically
76
+ * (except `mod_message`, which the engine routes unconditionally).
77
+ */
78
+ on<E extends EventName>(event: E, handler: (payload: EventPayloads[E]) => void | Promise<void>): void;
79
+ /** Log a message via the engine logger. Output includes timestamp and mod ID. */
80
+ log(level: "info" | "warn" | "error", message: string): void;
81
+ /** Override the game tick rate. Takes effect immediately. */
82
+ setTickRate(ticks_per_second: number): void;
83
+ /** Set the FPS cap. Pass `null` to remove the cap (uncapped). */
84
+ setFpsCap(fps: number | null): void;
85
+ /** Enable or disable vertical sync. Takes effect immediately. */
86
+ setVsync(enabled: boolean): void;
87
+ };
88
+ export type WindowMode = "windowed" | "borderless" | "fullscreen";
89
+ /** Window management. */
90
+ export declare const Window: {
91
+ /** Set the title of the active window. */
92
+ setTitle(title: string): void;
93
+ /** Request a new inner window size in physical pixels. Has no effect in fullscreen modes. */
94
+ setSize(width: number, height: number): void;
95
+ /**
96
+ * Set the window display mode.
97
+ * - `"windowed"` — normal window
98
+ * - `"borderless"` — borderless fullscreen (windowed fullscreen)
99
+ * - `"fullscreen"` — exclusive fullscreen using the monitor's preferred video mode;
100
+ * falls back to borderless if no exclusive mode is available
101
+ */
102
+ setMode(mode: WindowMode): void;
103
+ };
104
+ /** Sandboxed per-mod file I/O. Paths must not contain `..`. */
105
+ declare const File$1: {
106
+ /** Write a UTF-8 string to a sandboxed file for this mod. */
107
+ write(path: string, data: string): Promise<void>;
108
+ /** Read a sandboxed file for this mod and return its contents as a UTF-8 string. */
109
+ read(path: string): Promise<string>;
110
+ /** Delete a sandboxed file for this mod. */
111
+ delete(path: string): Promise<void>;
112
+ };
113
+ export declare namespace BuiltInComponents {
114
+ const TRANSFORM_ID = "core:transform";
115
+ const SPRITE_RENDERER_ID = "core:sprite_renderer";
116
+ /** Transform component. Returned by `getComponent("core:transform")` as a live class instance. */
117
+ class Transform implements Component {
118
+ readonly id = "core:transform";
119
+ position: [
120
+ number,
121
+ number,
122
+ number
123
+ ];
124
+ rotation: [
125
+ number,
126
+ number,
127
+ number,
128
+ number
129
+ ];
130
+ scale: [
131
+ number,
132
+ number,
133
+ number
134
+ ];
135
+ constructor(init?: {
136
+ position?: [
137
+ number,
138
+ number,
139
+ number
140
+ ];
141
+ rotation?: [
142
+ number,
143
+ number,
144
+ number,
145
+ number
146
+ ];
147
+ scale?: [
148
+ number,
149
+ number,
150
+ number
151
+ ];
152
+ });
153
+ getX(): number;
154
+ setX(x: number): this;
155
+ addX(x: number): this;
156
+ getY(): number;
157
+ setY(y: number): this;
158
+ addY(y: number): this;
159
+ getZ(): number;
160
+ setZ(z: number): this;
161
+ addZ(z: number): this;
162
+ /** Returns a copy of the position as [x, y, z]. */
163
+ getPosition(): [
164
+ number,
165
+ number,
166
+ number
167
+ ];
168
+ /** Set all three position components at once. Chainable. */
169
+ setPosition(x: number, y: number, z: number): this;
170
+ getScaleX(): number;
171
+ setScaleX(x: number): this;
172
+ getScaleY(): number;
173
+ setScaleY(y: number): this;
174
+ getScaleZ(): number;
175
+ setScaleZ(z: number): this;
176
+ /** Returns a copy of the scale tuple. */
177
+ getScale(): [
178
+ number,
179
+ number,
180
+ number
181
+ ];
182
+ /** Set all three scale components. Chainable. */
183
+ setScale(x: number, y: number, z: number): this;
184
+ /** Set all three scale components to the same value. Chainable. */
185
+ setScaleUniform(s: number): this;
186
+ /** Euclidean distance between this transform's position and another's. */
187
+ distance(other: Transform): number;
188
+ /** Returns a copy of the raw quaternion as [x, y, z, w]. */
189
+ getRotation(): [
190
+ number,
191
+ number,
192
+ number,
193
+ number
194
+ ];
195
+ /** Set the raw quaternion directly. Chainable. */
196
+ setRotation(x: number, y: number, z: number, w: number): this;
197
+ /**
198
+ * Decompose the current quaternion into intrinsic XYZ Euler angles in radians.
199
+ * Returns [rx, ry, rz] — pitch around X, then yaw around Y, then roll around Z.
200
+ * Near gimbal-lock (ry ≈ ±90°) rx and rz may be unstable.
201
+ */
202
+ getEulerRadians(): [
203
+ number,
204
+ number,
205
+ number
206
+ ];
207
+ /**
208
+ * Set rotation from intrinsic XYZ Euler angles in radians (equivalent to extrinsic ZYX,
209
+ * i.e. q = Qz·Qy·Qx). Consistent with `getEulerRadians`. Chainable.
210
+ */
211
+ setEulerRadians(rx: number, ry: number, rz: number): this;
212
+ /** Decompose the current quaternion into intrinsic XYZ Euler angles in degrees. */
213
+ getEulerDegrees(): [
214
+ number,
215
+ number,
216
+ number
217
+ ];
218
+ /** Set rotation from intrinsic XYZ Euler angles in degrees. Chainable. */
219
+ setEulerDegrees(x: number, y: number, z: number): this;
220
+ /** Rotate by `degrees` around the world X axis. Chainable. */
221
+ rotateX(degrees: number): this;
222
+ /** Rotate by `degrees` around the world Y axis. Chainable. */
223
+ rotateY(degrees: number): this;
224
+ /** Rotate by `degrees` around the world Z axis. Chainable. */
225
+ rotateZ(degrees: number): this;
226
+ private _leftMultiply;
227
+ }
228
+ /** SpriteRenderer component. Returned by `getComponent("core:sprite_renderer")` as a live class instance. */
229
+ class SpriteRenderer implements Component {
230
+ readonly id = "core:sprite_renderer";
231
+ texture: string;
232
+ z_index: number;
233
+ constructor(init?: {
234
+ texture?: string;
235
+ z_index?: number;
236
+ });
237
+ getTexture(): string;
238
+ setTexture(texture: string): this;
239
+ getZIndex(): number;
240
+ setZIndex(z: number): this;
241
+ }
242
+ }
243
+ export interface _ComponentRegistry {
244
+ [BuiltInComponents.TRANSFORM_ID]: BuiltInComponents.Transform;
245
+ [BuiltInComponents.SPRITE_RENDERER_ID]: BuiltInComponents.SpriteRenderer;
246
+ }
247
+ export interface _ComponentSetRegistry {
248
+ [BuiltInComponents.TRANSFORM_ID]: {
249
+ position: [
250
+ number,
251
+ number,
252
+ number
253
+ ];
254
+ rotation: [
255
+ number,
256
+ number,
257
+ number,
258
+ number
259
+ ];
260
+ scale: [
261
+ number,
262
+ number,
263
+ number
264
+ ];
265
+ };
266
+ [BuiltInComponents.SPRITE_RENDERER_ID]: {
267
+ texture: string;
268
+ z_index: number;
269
+ };
270
+ }
271
+ declare function _setComponentImpl<K extends keyof _ComponentSetRegistry>(entity_id: string, component_type: K, data: _ComponentSetRegistry[K]): void;
272
+ declare function _setComponentImpl(entity_id: string, component_type: string, data: JsonValue): void;
273
+ declare function _getComponentImpl<K extends keyof _ComponentRegistry>(entity_id: string, component_type: K): Promise<_ComponentRegistry[K] | null>;
274
+ declare function _getComponentImpl<T extends Component = Component>(entity_id: string, component_type: string): Promise<T | null>;
275
+ /** A live entity in the scene. Wraps an entity ID and provides component access. */
276
+ export declare class Entity {
277
+ readonly id: string;
278
+ constructor(id: string);
279
+ /** Destroy this entity and all its components. Fire-and-forget. */
280
+ destroy(): void;
281
+ /** Set a component on this entity. Fire-and-forget. */
282
+ setComponent<K extends keyof _ComponentSetRegistry>(component_type: K, data: _ComponentSetRegistry[K]): void;
283
+ setComponent(component_type: string, data: JsonValue): void;
284
+ /** Remove a component from this entity. Fire-and-forget. */
285
+ removeComponent(component_type: string): void;
286
+ /** Get a component's data. Returns `null` if the component is not set. */
287
+ getComponent<K extends keyof _ComponentRegistry>(component_type: K): Promise<_ComponentRegistry[K] | null>;
288
+ getComponent<T extends Component = Component>(component_type: string): Promise<T | null>;
289
+ /**
290
+ * Convenience: update the `position` field of this entity's `core:transform`
291
+ * while preserving existing rotation and scale.
292
+ */
293
+ move(position: [
294
+ number,
295
+ number,
296
+ number
297
+ ]): Promise<void>;
298
+ }
299
+ /** Entity and component management. */
300
+ export declare const Scene: {
301
+ /** Create a new entity and return it. */
302
+ createEntity(): Promise<Entity>;
303
+ /** Destroy an entity and all its components. Fire-and-forget. */
304
+ destroyEntity(entity_id: string): void;
305
+ /** Return all living entities. */
306
+ listEntities(): Promise<Entity[]>;
307
+ /** Set a component on an entity by ID. Fire-and-forget. */
308
+ setComponent: typeof _setComponentImpl;
309
+ /** Remove a component from an entity. Fire-and-forget. */
310
+ removeComponent(entity_id: string, component_type: string): void;
311
+ /** Get a component's data by entity ID. Returns `null` if the component is not set. */
312
+ getComponent: typeof _getComponentImpl;
313
+ /** Query all entities that have every listed component type. */
314
+ query(component_types: string[]): Promise<QueryResult<Entity>[]>;
315
+ /**
316
+ * Convenience: create an entity and attach `core:transform` + `core:sprite_renderer`.
317
+ * The sprite becomes visible as soon as both components are set.
318
+ */
319
+ spawnSprite(texture: string, position: [
320
+ number,
321
+ number,
322
+ number
323
+ ], scale?: [
324
+ number,
325
+ number,
326
+ number
327
+ ]): Promise<Entity>;
328
+ /**
329
+ * Convenience: move an entity by updating the `position` field of its `core:transform`
330
+ * while preserving existing rotation and scale.
331
+ */
332
+ moveEntity(entity_id: string, position: [
333
+ number,
334
+ number,
335
+ number
336
+ ]): Promise<void>;
337
+ };
338
+ /** Query information about loaded mods. */
339
+ export declare const Mods: {
340
+ /** Returns the manifests of all currently loaded mods in load order. */
341
+ list(): Promise<ModManifest[]>;
342
+ /** Returns the manifest for a specific mod by ID. Rejects if the mod is not loaded. */
343
+ get(id: string): Promise<ModManifest>;
344
+ };
345
+ export interface _MessageNamespace {
346
+ /** Send a fire-and-forget message to another mod. */
347
+ sendAndForget<T extends JsonValue>(id: string, message: T): void;
348
+ /**
349
+ * Send a message to another mod and wait for a reply.
350
+ * The receiving mod's handler must return a non-null value within `timeout` ms.
351
+ * Rejects with a timeout error if no reply arrives in time.
352
+ */
353
+ send<T extends JsonValue, U extends JsonValue>(id: string, message: T, timeout: number): Promise<U>;
354
+ /**
355
+ * Register a handler for incoming mod messages.
356
+ * Return a `JsonValue` to reply (only meaningful when the sender used `send`); return `null` to ignore.
357
+ * The `request_id` in the payload is an opaque engine token — do not inspect or store it.
358
+ */
359
+ registerMessageHandler(handler: (payload: EventPayloads["mod_message"]) => JsonValue | null): void;
360
+ }
361
+ /** Inter-mod communication. */
362
+ export declare const Message: _MessageNamespace;
363
+ /** Public arg type for Console.register(). */
364
+ export type ArgType = "string" | "int" | "float" | "bool";
365
+ /** Argument specification for a command. */
366
+ export type ArgSpec = {
367
+ name: string;
368
+ type: ArgType;
369
+ required?: boolean;
370
+ description?: string;
371
+ };
372
+ /** A command or subcommand specification. */
373
+ export type CommandSpec = {
374
+ name: string;
375
+ description?: string;
376
+ subcommands?: CommandSpec[];
377
+ args?: ArgSpec[];
378
+ handler?: (args: Record<string, string | number | boolean>) => string | string[] | Promise<string | string[]>;
379
+ };
380
+ /** Register a command that users can invoke from the developer console. */
381
+ declare const Console$1: {
382
+ register(spec: CommandSpec): void;
383
+ };
384
+
385
+ export {
386
+ Console$1 as Console,
387
+ File$1 as File,
388
+ };
389
+
390
+ export {};