Adding a tile
How-to guide. For the art rules of each tile family, read Tile assets.
Use these steps to add built-in tile art that ships with the app. You draw an SVG file, register it in the tile catalog, update the palette test, and check the art in the tile preview page.
A GM who loads an image at runtime does not need these steps.
TilePalette.addCustom registers that image without a code change.
Prerequisites
- A clone with
pnpm installdone. python3, to serve the preview page.
1. Read the family rules
Open Tile assets and read the section for your tile family. The families are terrain variants, road pieces, river pieces, coast pieces, dock pieces, POI markers, town pieces, interior pieces, and furnishings.
Each family has its own rule for the background and the edges. A tile that breaks the rule shows a visible line against its neighbors on the map.
2. Draw the SVG
- Use a
viewBox="0 0 64 64"tile box. - Keep each decorative detail inset from the edges, unless the family rules allow a motif that crosses an edge.
- Define a repeated element once in
<defs>, and stamp it with<use href="#id" transform=...>. The canvasdrawImagemethod draws these elements correctly. - Save the file at the path that the catalog expects for its family:
| Family | Path | Example |
|---|---|---|
| Terrain variant | assets/tiles/<type>/<type>-<n>.svg |
assets/tiles/grass/grass-4.svg |
| Road piece | assets/tiles/road/road-<kind>.svg |
assets/tiles/road/road-corner-ne.svg |
| River piece | assets/tiles/river/river-<kind>.svg |
assets/tiles/river/river-ford-h.svg |
| Coast piece | assets/tiles/coast/coast-<kind>.svg |
assets/tiles/coast/coast-inner-sw.svg |
| Dock piece | assets/tiles/dock/dock-<kind>.svg |
assets/tiles/dock/dock-quay-n.svg |
| POI marker | assets/tiles/<marker>/<marker>.svg |
assets/tiles/tavern/tavern.svg |
| Town building | assets/tiles/town/<building>.svg |
assets/tiles/town/bakery.svg |
| Town wall piece | assets/tiles/town/town-<kind>.svg |
assets/tiles/town/town-gate-h.svg |
| Interior piece or furnishing | assets/tiles/interior/interior-<kind>.svg |
assets/tiles/interior/interior-chest.svg |
3. Register the tile
Warning: A new terrain variant or floor variant changes the look of saved maps. A save stores a cell whose variant matches the position hash as its family name alone. The hash then picks from the new count, so most cells of that family in every saved map draw a different variant.
A tile exists for the app only when a registry table names it. Add the tile to the table for its family:
| Family | Table | File | What to add |
|---|---|---|---|
| Terrain | VARIANT_COUNTS |
src/map/TileCatalog.js |
Raise the count for the type |
| Interior floor | FLOOR_VARIANT_COUNTS and INTERIOR_KINDS |
src/map/TileCatalog.js and src/map/TileKinds.js |
Raise the count for the floor family, and add the kind with the meaning floor |
| Road | ROAD_KINDS |
src/map/TileCatalog.js |
The kind name |
| River | RIVER_KINDS |
src/map/TileCatalog.js |
The kind name |
| Coast | COAST_KINDS |
src/map/TileCatalog.js |
The kind name |
| Dock | DOCK_KINDS |
src/map/TileCatalog.js |
The kind name |
| POI marker | MARKER_TYPES |
src/map/TileCatalog.js |
The marker name |
| Town building | TOWN_BUILDINGS |
src/map/TileCatalog.js |
The building name |
| Town wall | TOWN_WALL_KINDS |
src/map/TileKinds.js |
The kind name and its rule meaning |
| Interior | INTERIOR_KINDS |
src/map/TileKinds.js |
The kind name and its rule meaning |
| Furnishing | FURNISHING_KINDS |
src/map/TileKinds.js |
The kind name and its rule meaning |
An interior piece, a furnishing, or a town wall piece needs a rule meaning.
The valid meanings are wall, obstacle, door, stairs-up,
stairs-down, floor, and plain. The rest of the app reads the meaning
through tileKind(tile). A piece that is not in these three tables has
the meaning plain, so the party can walk across it.
4. Update the tests
- Open
tests/TilePalette.test.js. - Update the count or the name list in the test for your family. For example, a new road piece raises the expected road count from 15 to 16.
- If the tile has a rule meaning, add an assertion to
tests/TileKinds.test.js. -
Run the two files:
node --test tests/TilePalette.test.js tests/TileKinds.test.js
The test “every built-in entry points at a file that exists” fails if the registered path has no file. If it fails, compare the path in the error with the path table in step 2.
5. Add the tile to the preview page
tests/tile-preview.html lists its tiles by hand. Add your tile to the
list for its family. For example, a new POI marker goes into the marker
list, and a terrain type with more than 3 variants goes into VARIANTS.
6. Look at the tile
-
From the project root, serve the files:
python3 -m http.server 8934 - Open
http://localhost:8934/tests/tile-preview.html. - Find the new tile, and check these points:
- The background matches the family rule.
- No detail touches an edge, unless the family allows it.
- The tile joins its neighbors with no visible line.
- If the tile is a connector, check that its path lines up with a straight piece and with a corner piece at the shared edge.
- Stop the server with Ctrl+C.
See Testing a change for the other browser checks.