# Mod configuration guide

**Audience:** game hosts + coding agents  
**Law:** [EVERYTHING-IS-A-MOD.md](./EVERYTHING-IS-A-MOD.md)  
**Catalog (machine):** [/agents/mod-catalog.json](../agents/mod-catalog.json)

---

## 1. Universal conventions

Every modern ModTrick mod should support:

| Surface | Purpose |
|---------|---------|
| `createX(scene, options)` | GENESIS — options fully documented in `mod.json` → `config` |
| `handle.getConfig()` | Snapshot of live tunable fields |
| `handle.configure(partial)` / `setConfig` | Hot-reload tunables without reloading the module |
| `handle.enabled` / `setEnabled(bool)` | Soft disable (often hides group) |
| Host inject | When loaded via **ModRuntime**, `camera`, `renderer`, `domElement`, `THREE`, `bus` are filled automatically if omitted |

Internal keys (never required by authors): `__runtime`, `__manifest`.

---

## 2. Loading with configuration

```js
const runtime = new ModRuntime({ scene, camera, renderer, THREE });

// Global defaults for every load of this id
runtime.setDefaults("threejs-collectibles", { count: 20, value: 2 });

await runtime.loadManifestUrl(`${BASE}/mods/threejs-third-person/mod.json`, {
  baseUrl: `${BASE}/mods/threejs-third-person/`,
  options: {
    // camera + domElement auto-injected from host if omitted
    target: hero.group,
    speed: 9,
    distance: 8,
    bounds: { minX: -40, maxX: 40, minZ: -40, maxZ: 40 },
  },
});

// Hot reconfigure
runtime.configure("threejs-collectibles", { collectDistance: 2, respawn: true });
console.log(runtime.getConfig("threejs-collectibles"));
console.log(runtime.list());
```

Direct import (no runtime):

```js
import { injectHost } from "/mods/_shared/mod-kit.js";
// or just pass camera/domElement explicitly
const orbit = createOrbitCamera(scene, { camera, domElement: renderer.domElement, damping: 0.2 });
orbit.configure?.({ autoRotate: true });
```

---

## 3. Shared bus contracts

Mods publish/consume fields on `runtime.bus` (or a frame-context bus):

| Field | Producers | Consumers |
|-------|-----------|-----------|
| `playerPos` / `playerYaw` | third-person, fps, character | collectibles, minimap, rain, checkpoint, portal, npc |
| `character` | third-person / character | portal, checkpoint.respawn |
| `sunDir` / `sunColor` / `fogHints` | sky | ocean, light-rig |
| `score` / `collected` / `remaining` | collectibles | quest, hud |
| `health` / `dead` | health | hud, game over |
| `currency` / `currencyDelta` | currency / collectibles | shops |
| `inventory` | inventory | UI |
| `quests` | quest | UI |
| `timer` | timer | hud |
| `timeOfDay` | day-cycle | sky (driven) |
| `sfx` / `toast` / `dialogue` | any | audio-bus, toast, dialogue-box |
| `fps` | debug-stats | hud |
| `weather` | weather-rain | — |

---

## 4. Config schema in `mod.json`

Preferred shape (agents should read this):

```json
{
  "config": {
    "presets": ["gems", "coins"],
    "fields": {
      "count": { "type": "number", "default": 12, "min": 1, "max": 200 },
      "color": { "type": "color", "default": "0x3dd6c6" },
      "respawn": { "type": "boolean", "default": false }
    },
    "needsHost": ["camera", "domElement"],
    "hot": ["count", "color", "respawn"]
  }
}
```

- **needsHost** — required for GENESIS unless ModRuntime injects them  
- **hot** — safe to change via `configure()` without rebuild  
- **presets** — named option packs  

---

## 5. Agent subskill: configure don’t rewrite

When the user asks to “tweak gems / slower camera / more rain”:

1. Prefer `runtime.configure(id, partial)` or `handle.configure(partial)`.  
2. Do **not** fork the mod source for tunables.  
3. Only edit the entry module when adding a **new capability** (then update ASEC SHADOW/META).  
4. Document new fields in `mod.json` `config.fields` + `AGENT.md`.  

---

## 6. Presets cheat sheet

| Mod | Presets |
|-----|---------|
| collectibles | `gems`, `coins`, `dense`, `sparse` |
| particles | `fire`, `smoke`, `sparks`, `magic`, `embers`, `snow` |

```js
gems.configure({ preset: "coins" });
fx.configure({ preset: "snow", count: 800 }); // particles
```
