# A city district (world.district) Readable offline: `gripforge docs world-district`, or the MCP tool `gripforge_docs` (topic `world-district`). ## What it is for `world.district` builds the world of an open-world city game. The district is a square grid of `cells` by `cells` tiles of `tile` metres. Every `avenueEvery` row and column is a road; the cells between roads form blocks. Each road cell takes the tile that matches its neighbours (straight, four-way, T or curve) and is turned into place. Each block gets a sidewalk ring and lots along its streets, filled from the buildings you bind, facing the street. Interior lots stay open (courtyards) or are filled. It provides the ground and building colliders (`world.ground`), the road tile centres, the sidewalk nodes and the lane graph (`world.roads`), and the named zones (`world.zones`). Vehicle and character kits stand on it, traffic and pedestrian kits follow its lanes and sidewalks. Without bound assets it still plays, with flat slabs and procedural boxes. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "world.district" } gripforge_gamekit_configure { project_id: "gkp_…", id: "world.district", config: { "cells": 17, "avenueEvery": 4, "seed": 3, "buildingHeight": [8, 30] } } ``` The kit requires `game.engine`, `physics.rigidbody` and `physics.port`. Bind the four road tiles and any number of buildings to the asset slots below. ## First example With `cells` 13, `tile` 8 and `avenueEvery` 4 and the same `seed`, the layout is always the same; another seed changes the lots. The district is 104 metres wide with four road lines. It has 3 by 3 blocks of 3 by 3 lots (81 lots, one interior lot per block left open), 36 sidewalk pieces and the three default zones. ## Settings and defaults | Setting | Type | Default | Meaning | |---|---|---|---| | `layout` | `tiles` or `blocks` | `tiles` | road tiles, or the grid of square blocks (below) | | `cells` | integer 5 to 97 | `13` | tiles per side (an odd number keeps a central block) | | `tile` | number 4 to 16 | `8` | tile edge in metres; road tiles and lots share it | | `avenueEvery` | integer 2 to 8 | `4` | a road every N cells; blocks are N-1 cells wide | | `buildingHeight` | [min, max] | `[6, 18]` | metres, for the procedural boxes when no building is bound | | `seed` | integer | `1` | deterministic layout seed | | `sidewalks` | boolean | `true` | a sidewalk ring per block | | `courtyards` | boolean | `true` | leave the interior lots of a block open | | `profile` | `grid` or `coastal_city` | `grid` | web: regional land use, parks, coast dressing, neighbourhood zones | | `appearance` | `asset` or `realistic` | `asset` | web: shared street surfaces, markings, sky, street furniture and skyline | | `streetscape` | object | none | web: `seed`, `buildingDistance`, `vegetationDistance`, `decorDistance`, `weathering`, `wetness` (all six) | | `streamEntities` | boolean | `false` | web: with `world.streaming`, materialise nearby instances and colliders only | | `edge` | object | see below | the edge of the world | The blocks layout (`layout` set to `blocks`) builds a `blocks` by `blocks` grid of square blocks instead: | Setting | Default | Meaning | |---|---|---| | `blocks` | `3` | blocks per side (1 to 8) | | `blockSize` | `40` | block edge in metres (20 to 120) | | `roadWidth` | `8` | road width in metres (4 to 20) | It uses `buildingHeight`, `seed` and `sidewalks` too, draws box buildings, keeps the default zones and spawn, and is built on the web and Rust hosts; the engine wrappers build the tiles. The tile settings do not apply to it. The edge of the world, `edge`: `apron` (default 40) is the ground in metres that goes on beyond the streets, `barrier` (`rail`, `wall` or `none`, default `rail`) holds vehicles and characters on the edge, `height` (default 1.2) is its height in metres, and `recoverBelow` (default 12) is how far under the ground, or beyond the apron, an actor can be before it is put back on the nearest road. ## Asset slots | Slot | Many | Without a binding | |---|---|---| | `road_straight`, `road_intersection`, `road_t`, `road_curve` | no | flat slab | | `district_building` | yes | procedural boxes | | `district_tree`, `district_park_tree`, `district_skyline` | no, yes, no | procedural, in the realistic appearance | | `lot_fill` | no | flat lot | | `city_block` | yes | blocks layout: procedural boxes | | `ground_texture`, `skybox` | no | flat grey, gradient sky | Tiles are square, `tile` metres wide, origin at the centre, +Y up. The straight tile runs along Z, the T keeps its straight along Z with the branch towards +X, the curve links -Z to +X; the kit applies the yaw. Buildings have their origin at the ground centre and their front on -Z; they are fitted to the lot and turned towards their street. ## Services, events, variables - `world.district` (service): `layout()` answers `{ cells, tile, extent, profile, areas, tiles, sidewalks, sidewalkNodes, lots, blocks, lanes, zones, spawn }`. - `world.roads` (service): `nearestSidewalk(pos)`, `randomWalk(from, rng)` and `lanes()`, the lanes as `{ from, to, width }`. - `world.ground` (service): static ground and building colliders, with `groundHeight(x, z)`. - `world.ready` (event): `{ blocks, extent, bounds, zones, tiles, buildings }`, once colliders and zones exist; spawn-time kits may place entities. It is sent again when a different district is rebuilt. - `world.recovered` (event): `{ entity, cause, x, y, z }`, an actor lost under the ground (`fell`) or beyond the apron (`outside`) was put back. - `world.district.area` (variable): the neighbourhood the player stands in (coastal profile); empty in the grid profile. - `zones` (collection): `{ id, shape (sphere or box), center, radius or size, tags }`. The defaults are `z_parking`, `z_downtown` (tag `police`) and `z_garage`; a zone with a default id replaces it. ## Hooks and modding - `district.build` `{ scene, cells, tile, avenueEvery, seed, profile, sidewalks, courtyards }`: tiles layout, asked before a build (at the start, when a scene asks for another district, when a load rebuilds it). A tap may change `seed`, `profile`, `sidewalks` and `courtyards`; the grid is read only. Not cancellable. - `city_block.build` `{ scene, blocks, blockSize, roadWidth, seed, sidewalks }`: the same for the blocks layout. A tap may change `blocks`, `seed` and `sidewalks`. - `world.recover` `{ entity, cause, x, y, z }`: asked before an actor is put back. A tap may change `x`, `y`, `z` (its feet); cancelled, the game handles it. ```json { "id": "harbour_town", "hook": "district.build", "match": { "scene": "harbour" }, "set": { "profile": "coastal_city" } } { "id": "back_to_garage", "hook": "world.recover", "match": { "cause": "outside" }, "set": { "x": 0, "y": 0, "z": 12 } } ``` A scene whose world names `world.district` plays the district of its own `world.config` over the kit config, and rebuilds when it differs. A mod may override the layout and look settings (all but `streamEntities`) and add, patch or remove `zones`. ## Engines The tiles layout is built by the web player and the native player, and by the Godot, Unity and Unreal wrappers with the same layout, roads, lanes and zones. The realistic appearance, the coastal profile, the streetscape and entity streaming are web only; the engines keep the asset look and the eager layout. Hooks run through the Rust kit logic. ## When it does not do what you expect - Flat slabs and grey boxes: no road tile or building is bound to its slot. - The streetscape or the realistic look is missing in an engine: those settings are web only. - `streamEntities` has no effect: `world.streaming` is not installed. - A car drove out of the map and came back: the edge safety net, see `edge` and the `world.recover` hook. - The tile settings are ignored: `layout` is `blocks`. ## Limits One district per scene, built from a regular grid. It does not simulate traffic or pedestrians; it gives them lanes and sidewalks. ## Versions Kit 1.10.0. 1.9.0 added the edge of the world, 1.10.0 the blocks layout and the `city_block.build` hook; `district.build` came with 1.7.0. The services `world.ground`, `world.roads` and `world.zones` are at 1.0.0.