# Heaven City — asset developer handoff

## Runtime and source

Edit `src/assets/<id>.json` in this repository. The app fetches `manifest.json` from this local service or Cloudflare and loads each hashed geometry definition on demand. See README.md for local setup, publishing, cache headers, and connecting the web app and API. The base map is a single cream colour; roads, buildings, animals, trees, and props are explicitly placed assets.

## Required world rules

| Property | Required behavior |
| --- | --- |
| Footprint | The default is **1 × 1** logical plots, independent of art dimensions. Declare explicit integer sizes up to 8 × 8 for static assets. Sample Plaza is 2 × 2; Sample Warehouse is 3 × 4 and becomes 4 × 3 at 90°/270°. Starter homes use `hut_improved` with a 1 × 1 footprint. |
| Visual/animation envelope | **3 × 3** plots, centred on the 1 × 1 home plot. This does not enlarge the static collision footprint. |
| Headroom | Reserve **1 plot of vertical headroom** in preview/art framing. Preview containers fit the complete model and ground rather than cutting off roofs. |
| Anchor | Ground centre; local model Z = 0 is the ground. Do not bake world position or screen-space offsets into the asset. |
| Projection | 2:1 diamond: one logical tile projects to **128 × 64 px** at 100% zoom. `screenX=(worldX-worldY)*64`, `screenY=(worldX+worldY)*32`. |
| Rotation | 0, 90, 180, 270 degrees on the ground plane; author all four sides. Do not bake an isometric camera into geometry. |
| Roads | Roads are 1 × 1 placeable terrain assets with the same move/rotate/variant/delete tools. The base world is one uniform cream colour with an optional plot grid. Roads appear only as explicitly placed assets; there are no automatic road lanes or alternating tile colours. Other assets cannot overlap a road asset. |
| Placement | Whole plot coordinates, bounded to ±100,000. No overlaps with other footprints, follower homes, or reserved roaming areas. The base world has no hidden road reservations; only placed assets reserve space. |
| Roaming | Opt-in per placed animal. Keep its **1 × 1** body footprint inside a clear **3 × 3** area centred on home. The entire area is reserved and must avoid roads and other reservations. |
| Movement | Animal perimeter path in world coordinates at **0.25 plots/second**, 40-second loop; return home at loop end. Return to the home plot for selection/editing and when preview is disabled; pause while placing; disable movement for reduced motion. |
| Depth | Sort by current world X + Y, including roaming offset; do not put every animal above every building. |
| Ownership | Asset JSON describes appearance/capabilities. Database map instances own position, rotation, chosen variant, and whether roaming is enabled. |

A 3 × 3 source image or art board does **not** mean nine occupied plots. Keep art size, collision footprint, roaming reservation, and rendering scale separate. Do not add hidden padding to `size` to fake the footprint.

## Author one asset

Use `asset.schema.json` and copy `assets/goat.*.json`, `assets/tree.*.json`, or `assets/road.*.json` from the pack. Every definition has:

- `schemaVersion: 2`, stable `id` (`snake_case`), `name`, and `category` (`building`, `animal`, `nature`, `prop`, `terrain`).
- `footprint: { "x": 1, "y": 1 }`.
- `size`: author-space width (`w`), depth (`d`), and height (`h`). These are model units, not plots.
- `tile`: square author-space base dimensions are preferred; the runtime fits their projection into one plot.
- `view.zoom`: preview preference, capped by the available card dimensions.
- `palette`: named, six-digit hex colours.
- `variants`: at least one named variant; each overrides palette colours and may replace `parts`.
- `parts`: reusable boxes (`t: "b"`) or pyramids (`t: "p"`). Each part has local `x,y,z`, positive `w,d,h`, a palette key `c`, and optional top colour key `ct`.
- `animation`: capabilities and bounds. Use `canRoam: true` only for animals; other assets are static. This is declarative movement metadata, not arbitrary code.

The hut builder is a legacy optional builder with additional `foundation`, `roof`, `chimney`, and `decor` data. New assets should normally use `parts` so a custom runtime builder is unnecessary. Complex skeletal animation, GLB models, sprite atlases, sound, and arbitrary JavaScript are not supported by this renderer; agree a new schema/renderer revision before delivering them.

## Tiny example (a static tree)

```json
{
  "schemaVersion": 2,
  "id": "tree_round",
  "name": "Round Tree",
  "category": "nature",
  "view": { "zoom": 1 },
  "tile": { "w": 100, "d": 100, "h": 6 },
  "size": { "w": 60, "d": 60, "h": 90 },
  "footprint": { "x": 1, "y": 1 },
  "palette": { "trunk": "#A2673B", "leaf": "#659943" },
  "variants": { "green": {}, "autumn": { "leaf": "#F4AD35" } },
  "animation": {
    "canRoam": false,
    "bounds": { "x": 3, "y": 3 },
    "headroomPlots": 1,
    "speedPlotsPerSecond": 0.25,
    "path": "perimeter",
    "collision": "reserve-area"
  },
  "parts": [
    { "t": "b", "x": 25, "y": 25, "z": 0, "w": 10, "d": 10, "h": 40, "c": "trunk" },
    { "t": "p", "x": 0, "y": 0, "z": 30, "w": 60, "d": 60, "h": 60, "c": "leaf" }
  ]
}
```

## Deliver and integrate

Run `npm run build` in this repository, verify the model in the editor, and publish `dist/` using the README instructions. Share the manifest URL. For new asset IDs, the app developer syncs the API capability registry from that URL, and rebuilds the API. No database asset/category catalog is used. Never put world coordinates, account data, secrets, or scripts in asset definitions.
