# Engine Public API (on demand)

Open only when the short `agent-skill` card is insufficient for a named symbol or host bridge.

## Public API

### Values

`Game`, `Scene`, `GameObject`, `Component`, `System`, `Transform`, `resource`, `resourceLoader`, `decorators`, `IDEProp`, `componentObserver`, `LOAD_EVENT`, `RESOURCE_TYPE`, `OBSERVER_TYPE`, `LOAD_SCENE_MODE`, `RESOURCE_TYPE_STRATEGY`, `normalizeResourceSrc`, `normalizeSpritesheetData`, `version`, `COMBOS_GAME_PLUGIN_INIT_SUCCESS`, `COMBOS_GAME_READY`, `COMBOS_GAME_SET_PLAYING`, `COMBOS_GAME_STATE_CHANGED`, `postParentPluginInitSuccess`, `postParentGameReady`, `postParentGameState`, `parseSetPlayingMessage`, `DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES`, `isAllowedMessageOrigin`, `mergeAllowedMessageOrigins`.

### Types

`GameParams`, `PluginStruct`, `TransformParams`, `ComponentChanged`, `UpdateParams`, `ComponentParams`, `ObserverInfo`, `PureObserverInfo`, `ResourceBase`, `SystemConstructor`, `CombosGamePluginInitSuccessMessage`, `CombosGameReadyMessage`, `CombosGameStateChangedMessage`, `CombosGameSetPlayingMessage`.

### `GameParams`


| Field                          | Type                     | Default | Notes                                    |
| ------------------------------ | ------------------------ | ------- | ---------------------------------------- |
| `systems`                      | `System[]`               | `[]`    | Bootstrapped async in registration order |
| `frameRate`                    | `number`                 | `60`    |                                          |
| `autoStart`                    | `boolean`                | `true`  |                                          |
| `needScene`                    | `boolean`                | `true`  | Auto-creates `Scene('scene')`            |
| `onSystemsBootstrapComplete`   | `(game, error?) => void` | —       | After all systems init                   |
| `pluginInitNotifyTargetOrigin` | `string`                 | `'*'`   | Outbound postMessage target (init / ready / state) |
| `allowedMessageOrigins`        | `string[]`               | defaults | Inbound origins allowed to send `set-playing` (merged with defaults; `['*']` = any) |


When the game runs inside an iframe (`window.parent !== window`), each `Game.addSystem` call posts to the parent after that system's `init` completes:

```typescript
{
  type: 'combos-game:plugin-init-success',
  systemName: string,       // System.systemName
  engineVersion: string,    // @combos-fun/engine build version
  packageName?: string,     // npm name, injected at plugin build
  packageVersion?: string,  // semver from plugin package.json, injected at plugin build
}
```

Official `@combos-fun/plugin-*` packages get `packageName` / `packageVersion` automatically via `scripts/build-package.mjs` (no hand-written static fields). Host pages can gate tooling on specific systems or versions using this payload. Types: `CombosGamePluginInitSuccessMessage`, helper `postParentPluginInitSuccess` in `bootstrapMessages.ts`.

### Host lifecycle protocol (preload → hold → play)

Core `Game` speaks a `postMessage` protocol with the embedding page so a host APP can preload, hold, then start the game on user intent. All outbound messages go to `pluginInitNotifyTargetOrigin`; the inbound command is origin-checked against `allowedMessageOrigins`.

**Outbound (iframe → parent):**

| Type | When | Payload |
| ---- | ---- | ------- |
| `combos-game:ready` | Bootstrap finished (systems `init`/`awake`, optional scene load & start). Sent once even with `autoStart:false`. | `{ engineVersion, error? }` |
| `combos-game:state-changed` | After `start` / `pause` / `resume` | `{ playing, started }` |

**Inbound (parent → iframe):**

| Type | Effect | Payload |
| ---- | ------ | ------- |
| `combos-game:set-playing` | `true` → cold `start()` on first play, else `resume()`; `false` → `pause()` | `{ playing: boolean }` |

Recommended flow: create the game with `autoStart:false`, wait for `combos-game:ready`, keep the game held at frame 0 (host shows a cover/loading overlay), then post `{ type: 'combos-game:set-playing', playing: true }` when the user taps play. `game.setPlaying(playing)` is the programmatic equivalent. Helpers: `postParentGameReady`, `postParentGameState`, `parseSetPlayingMessage`.

> Note: play/pause is owned by the engine core here, **not** by `plugin-development-tool` (which now only handles pick mode + mute).


### `Game` methods

`addSystem(system)`, `removeSystem(system | class | string)` (calls `system.destroy()` internally), `getSystem(class | string)`, `loadScene({ scene, mode?, params? })` (`LOAD_SCENE_MODE.SINGLE | MULTI_CANVAS`), `start()`, `pause()`, `resume()`, `setPlaying(playing)` (host bridge: cold-start-or-resume / pause), `destroy()`.

### `resource` singleton


| Method                                                     | Notes                     |
| ---------------------------------------------------------- | ------------------------- |
| `addResource(resources[])`                                 | Register (no load)        |
| `preload()`                                                | Load all `preload: true`  |
| `loadConfig(resources[])`                                  | `addResource` + `preload` |
| `loadSingle(resource): Promise`                            | Add + load one            |
| `getResource(name): Promise`                               | Get loaded                |
| `destroy(name): Promise`                                   | Destroy one               |
| `registerResourceType(type, value?)`                       | Custom type               |
| `registerInstance(type, cb)` / `registerDestroy(type, cb)` | Factory / destructor      |


Fields: `timeout` (6000ms), `resourcesMap`, `progress`.

> ⚠️ `addResource` only registers; it does **not** kick off network load. Components that reference an unloaded `resource` (e.g. `Img({ resource: 'logo' })`) silently render nothing — the canvas stays blank with no console error. Always pair it with `preload()` (and gate `new Game(...)` behind `resource.once(LOAD_EVENT.COMPLETE, ...)`), or skip the pair entirely and use `loadConfig(resources[])` which does both in one call.

