Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ jobs:
- name: Publish Windows application
shell: pwsh
run: ./scripts/publish.ps1
- name: Build a two-section scene with the published command line
shell: pwsh
run: |
./artifacts/app/simplepzmap.exe build examples/CiScene/ci-scene.json --out work/ci/ci.pzworld.json --export work/ci/ci.zip --strict
if ($LASTEXITCODE -ne 0) { throw 'simplepzmap build --strict failed.' }
./artifacts/app/simplepzmap.exe validate work/ci/ci.pzworld.json --source examples/CiScene/ci-source.geojson
if ($LASTEXITCODE -ne 0) { throw 'simplepzmap validate --source failed.' }
- name: Install isolated browser check dependencies
shell: pwsh
run: |
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ History is bounded/sparse, so large actions can remove older entries. A world re
4. Create a **new game** using the exported map. Existing saves are not migrated.
5. Enable overlapping example mods separately, or relocate and re-export them.

Exports include Build 42 `mod.info`, `common/`, native `.lotheader/.lotpack/chunkdata_*.bin`, room/building definitions, spawn points, zones, world map XML, editable project, attribution and installation instructions. Name/seed determine the mod ID; changing either creates a different ID. Origin defaults to native cell 100,100.
Exports include Build 42 `mod.info`, `common/`, native `.lotheader/.lotpack/chunkdata_*.bin`, room/building definitions, spawn points, zones, biome maps (`maps/biomemap_X_Y.png`, which Build 42 needs for foraging zones; squares with buildings, roads, fences, decorations or spawns use the featureless `dirt` biome so WorldGen never replaces authored vegetation there with its own jumbo trees), a 256×256 `thumb.png`, world map XML, editable project, attribution and installation instructions. Generated bedrooms, kitchens, bathrooms and living rooms are furnished with TileZed furniture standing against their walls (map format 14); the distributed housing templates keep their single legacy object per room. Imported streets are dressed with Build 42 street props: a mailbox at the curb in front of each house with a garbage bin beside it, a stop sign on every approach of a lesser road to a junction, a concrete wheel stop in each 3×5 parking stall, one to three yard props (chopping block, rain barrel, composter) behind each village house with no pavement nearby, and a scarecrow on farmland (parking zones snap to whole stalls in the direction the cars face). Like the vanilla maps, `spawnpoints.lua` starts players inside houses (the living room, kitchen or bedroom of every building with a bedroom, up to 256), gives all 25 Build 42 professions a key, and adds workplaces such as a police office or a restaurant kitchen for matching professions. Authored spawns are used only when the map has no house. Name/seed determine the mod ID; changing either creates a different ID. Origin defaults to native cell 100,100.

## Build from source

Expand Down
8 changes: 6 additions & 2 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

`simplepzmap` builds, checks and exports maps without the browser editor. It uses the same importer, validators and native exporter as the application, so a scene built here opens unchanged in the editor.

Run it from the repository with `dotnet run --project src/SimplePZMapmaker.Cli -- <command> …`, or build it once with `dotnet build src/SimplePZMapmaker.Cli -c Release` and call `simplepzmap.exe`.
The release package ships it as `artifacts/app/simplepzmap.exe`, next to the editor and with the same bundled runtime. From the repository, run `dotnet run --project src/SimplePZMapmaker.Cli -- <command> …`, or build it once with `dotnet build src/SimplePZMapmaker.Cli -c Release` and call `simplepzmap.exe`. CI builds [examples/CiScene](../examples/CiScene/README.md), a two-section scene with a shared building and a seam-crossing fence, with `build --strict` and checks it with `validate --source`.

Without `--game`, generated street dressing and vanilla vehicle distributions are accepted from built-in lists of Build 42 tiles and names. Tags that select game resources, such as `roof:colour`, `pz:wall` or `pz:style=tilezed:…`, still need `--game`.

| Command | What it does |
|---|---|
Expand All @@ -12,7 +14,9 @@ Run it from the repository with `dotnet run --project src/SimplePZMapmaker.Cli -
| `export <file> --out mod.zip` | Writes the Build 42 mod of a map or world. |
| `tiles <tileset.json> --out tiles.zip` | Builds a tile mod (`.pack` + `.tiles`) from PNG frames. |
| `probe <mod.zip> --game <path>` | Loads the exported lots with the installed game's own classes (headers, chunks, rooms, every tile sprite; stairs with `--expect-stairs`). Needs the repository's `scripts/` folder and Java from the game. |
| `server-check <mod.zip> --game <path>` | Starts the installed Build 42 dedicated server (`zombie.network.GameServer`, `-nosteam`) in a temporary cache folder with the mod and a test-only Lua probe. The probe asks the server's own metagrid for buildings at room tiles taken from the archive's authoring source, plus one empty control tile; the server is then stopped with `quit`. Your `Zomboid` folder is not touched. |
| `conflicts <mod.zip\|map\|world> --game <path>` | Lists installed maps that already use one of the map's native cells: the game's own maps (challenge maps excluded), Steam Workshop items next to the installation and the local mods folder (`--mods-dir`), all read only. It fails when the base game is hit. `build --game` runs the same check, and `--strict` then fails on it. |
| `serve <mod.zip> --game <path>` | Starts a dedicated server for playtesting in `work/playtest-server/<name>` (`--server-dir`), never in your `Zomboid` folder: the map loads next to Muldraugh with its spawn regions, the window prints the address and admin login, and `quit` stops it. `--fresh` starts a new world, `--port` sets the port (16261). See the [playtest checklist](PLAYTEST.md). |
| `server-check <mod.zip> --game <path>` | Starts the installed Build 42 dedicated server (`zombie.network.GameServer`, `-nosteam`) in a temporary cache folder with the mod and a test-only Lua probe. The probe asks the server's own metagrid for buildings at room tiles taken from the archive's authoring source, plus one empty control tile, and loads the map's spawn regions as the game does (profession keys, spawn points and how many lie in a building); the server is then stopped with `quit`. Your `Zomboid` folder is not touched. |

Common options:

Expand Down
64 changes: 64 additions & 0 deletions docs/PLAYTEST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Playtest checklist

The automatic checks already cover a lot:

- `build --game` confirms that every tile exists in the game and checks for cell conflicts.
- `probe` loads the lots with the game's own classes.
- `server-check` has the dedicated server find buildings, rooms and spawn regions.

What they cannot see happens only in a client: drawn sprites, chunks streamed around a player, foraging and WorldGen. This list covers that part. Play one character per map and tick each line.

## Start a server for the map

```bash
simplepzmap build scene.json --out work/world.pzworld.json --export work/map.zip --strict --game "<Build 42 folder>"
```

```bash
simplepzmap serve work/map.zip --game "<Build 42 folder>"
```

`serve` installs the mod into `work/playtest-server/<archive name>/mods` and starts the dedicated server there. It never touches your `Zomboid` folder. The map loads next to Muldraugh, KY with the map's own spawn regions. The world is kept between runs; `--fresh` starts a new one, and `--port` changes the port from 16261.

The window prints:

- the address to join (`127.0.0.1:16261`);
- the admin login;
- the path of the installed mod. The client needs the same mod in its `Zomboid\mods` folder: copy it there yourself, or export with `--install`.

The server runs without Steam (`-nosteam`), so start the game with the launch option `-nosteam`. Type `quit` in the server window to save and stop.

## Arrival

- [ ] The map appears in the server's spawn selection with its `thumb.png`.
- [ ] A new character starts inside a house: living room, kitchen or bedroom, never outside or in a wall.
- [ ] A doctor, a police officer or a chef can start at a matching workplace when the map has one.

## Buildings

- [ ] Walls close at every corner. A square with plain north and west walls shows one corner piece, and outer corners have the SE pillar.
- [ ] Doors and windows of a `pz:wall` set or a `tilezed:` template use that set's own pieces, including the irregular ones such as the diner.
- [ ] Furniture stands against the walls and faces into the room: beds, dressers, kitchen counters, stove, fridge, sink, toilet, bath, sofa and TV. None of it blocks a doorway or the stairs.
- [ ] The stove, fridge and sink work, and containers hold loot that fits the room name (kitchen, bathroom, bakery, barn).
- [ ] Room names in the debug room view match the source tags (shop, amenity, `pz:room`).

## Outdoors

- [ ] Fences alternate their two pieces, have posts where asked, and every gate opens and closes. A wicket has its frame.
- [ ] Mailboxes stand at the curb and face the road, with a garbage bin beside them.
- [ ] Stop signs stand on the right side of the lesser road before the junction and face the drivers coming to it.
- [ ] Parking areas spawn cars inside the stalls (sandbox car spawn on). Each stall has its wheel stop at the end the cars face.
- [ ] Village yards have their props clear of the doors, and farms have a scarecrow.

## Nature and zones

- [ ] Foraging (debug: zone view or the foraging window) reports Forest/DeepForest in woods, TownZone near houses, FarmLand in open country and ForagingNav on roads.
- [ ] After walking away and back, WorldGen has not put jumbo trees on roads, in yards or against walls, and authored trees and bushes are still there.
- [ ] ZombiesType zones change the zombie outfits inside them.

## World seams

- [ ] Walk along a section seam. Buildings, fences and roads that cross it are continuous, with no half buildings and no double walls.
- [ ] The world map (M) shows roads, water, forest and building outlines aligned with the ground.

Report a failing line with the map coordinates (debug: player position) and a screenshot.
14 changes: 14 additions & 0 deletions docs/STATUS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# Build 42 map depth: 18 items from TileZed, PZ Mapping Tools and the wiki — 2026-10-18

- **Biome maps (1):** `maps/biomemap_X_Y.png` per cell gives Build 42 its foraging zones; squares with authored content use the featureless `dirt` biome, so WorldGen does not replace authored vegetation with its own jumbo trees (5).
- **Rooms (2):** 588 room names from TileZed `RoomNames.txt`; shop, amenity, building and `pz:room` tags pick vanilla rooms (bakery, barn, church…).
- **Zones (3, 4, 12):** land use, parking and `pz:zone` become TownZone, Forest/DeepForest, Farm, Vegitation, ParkingStall and ZombiesType zones. Parking snaps to whole 3×5 stalls in the direction the cars face, in the importer and the editor.
- **Walls and fences (7, 8):** the TileZed wall catalog (175 sets, 42 of them irregular) with NW corner pieces and SE pillars; 14 reference fences with alternation, posts, gate frames and matching wickets.
- **Spawns (13):** `spawnpoints.lua` starts players inside houses, has all 25 profession keys and adds workplaces.
- **Map files (6):** a 256×256 `thumb.png`. A separate `worldmap-forest.xml` is not written: the game merges it with `worldmap.xml` from the same folder, and ours already has the forest polygons.
- **Interiors (9, 10):** TileZed furniture against the walls of bedrooms, kitchens, bathrooms and living rooms (map format 14, world format 9); `pz:style=tilezed:<template>` applies one of 25 building templates. The distributed housing templates keep their legacy furnishing until they are re-exported.
- **Streets and villages (11, 15):** curbside mailboxes and bins, stop signs on lesser roads, parking wheel stops, village yard props and scarecrows. Curbs, lane and parking lines and drains are not placed, because their sprites carry no properties that identify them.
- **Tools (14, 16, 17, 18):** `conflicts` and `build --game` list installed maps on the same cells; sections import only their neighbourhood of the source (PilnaVillage build and export: 2:35 → 1:30); `simplepzmap.exe` ships in the release and CI builds a two-section scene with `--strict`; `serve` starts a playtest server, with a [checklist](PLAYTEST.md).
- Real PilnaVillage through `build --strict --game`: 1,640 section and 61 shared buildings, 0 lost, 4,939 tile names found. `server-check`: 25 professions, 256 spawn points, all 256 inside buildings. Cell check: 118 cells shared with Raven Creek from the Workshop.
- **Not verified in a client:** foraging zones read from the biome maps, WorldGen around authored squares, furniture and sign orientation, cars in stalls. See the [playtest checklist](PLAYTEST.md).

# Transfer items 2, 4–7 and 9 — 2026-10-17

- **Building tags (item 4):** `pz:kind`, `pz:style`, `roof:colour` (named or `#rrggbb`, matched to roof sets by colours measured from the installed sprites), `pz:wall` and `pz:trim` are applied to section and shared imports and reach the native lot; unknown `pz:*` keys and unusable values are reported, never fatal.
Expand Down
9 changes: 6 additions & 3 deletions docs/TRANSFER-CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ Fences are their own layer, not decorations and not `LinearFeature`s (which are
- `W`, `N`: the edge tiles;
- `NW`: optional corner tile;
- `Post`: optional post tile;
- `WAlt`, `NAlt` with `AltEvery`: optional alternative edges, used for a style that repeats every N edges, such as a brick pillar between forged sections.
- `WAlt`, `NAlt` with `AltEvery`: optional alternative edges, used for a style that repeats every N edges, such as a brick pillar between forged sections;
- `GateSpaceW`, `GateSpaceN`: optional gate frame drawn under a gate on that edge.

The built-in styles are TileZed's reference fences (`Fences.txt`): `vanilla-planks`, `vanilla-short-planks`, `vanilla-tall-planks-2`, `vanilla-picket`, `vanilla-short-picket`, `vanilla-wire`, `vanilla-tall-wire`, `vanilla-cattle`, `vanilla-black-metal`, `vanilla-black-bars`, `vanilla-sheet` (tall concrete), `vanilla-military`, `vanilla-construction` and `vanilla-construction-barbed`. Two-piece fences alternate `west1`/`west2` with `AltEvery = 2`. A gate added without a style gets the wicket that TileZed pairs with the fence: `vanilla-wicket`, `-short`, `-picket`, `-wire` or `-metal`, each a full closed/open set.

`GateStyle`: `Width` 1–8 edges, `ClosedW`, `ClosedN` and optional `OpenW`, `OpenN`, each with one piece per edge in line direction. `OpensTo = "left"` mirrors the order. Vanilla example: `fixtures_doors_fences_01_12`…`15`, the solid wooden wicket (closed W, closed N, open W, open N). Tiles from a resource mod add that mod to `RequiredMods`. A style ID used by both the world and a section must mean the same tiles.

Expand Down Expand Up @@ -132,9 +135,9 @@ Building feature properties read by the importer (OSM keys keep their OSM meanin
| `building:levels` | 1–8 | storeys (existing) |
| `roof:shape` | `gabled`, `hipped`, `flat`, … | roof form (existing) |
| `roof:colour` | `grey`, `red`, `blue`, `green`, `brown`, `white`, `black`, `silver`, or `#rrggbb` | roof tile set: grey `roofs_30_04`, red `roofs_30_09`, blue `roofs_30_01`, green `roofs_30_05`, brown `roofs_30_07`, white `roofs_30_03`; a hex colour takes the nearest set by the average colour measured from the installed Build 42 sprites. Flat roofs keep their own surface. |
| `pz:wall` | `set:<first tile>`, a bare first tile, or the ID of a project palette whose first wall resource starts the set | exterior wall set: wall `+0/+1`, window frame `+8/+9`, door frame `+10/+11` (W/N), on every exterior edge except balconies |
| `pz:wall` | `set:<first tile>`, a bare first tile, or the ID of a project palette whose first wall resource starts the set | exterior wall set, on every exterior edge except balconies. The pieces come from TileZed's wall catalog (`wall-catalog.json`, 175 sets); a set outside it uses wall `+0/+1`, window frame `+8/+9`, door frame `+10/+11` (W/N). Export draws the set's NW corner piece where plain N and W walls meet, and its SE pillar on outer corners |
| `pz:trim` | overlay tile pair `<north tile>:<west tile>` | window surround overlay on exterior window edges (wall edge overlays, PR #1) |
| `pz:style` | building style id | interior and furniture style |
| `pz:style` | building style id, or `tilezed:<template>` | interior and furniture style; a TileZed template (`Bank`, `Barn`, `Diner`, `Police`, `Spiffo`, `Gigamart`… 25 in `building-templates.json`) sets the exterior wall set, door, window, and per room the floor and interior wall |
| `pz:kind` | `house`, `shed`, `garage`, `bathhouse`, `toilet`, `greenhouse` | building kind when OSM `building=*` is ambiguous; `bathhouse` and `toilet` make a one-room building a bathroom |

Unknown `pz:*` keys are kept as source tags and reported once per key as a warning; they never fail an import.
Expand Down
7 changes: 7 additions & 0 deletions docs/licenses/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@
MCP C# SDK2.2.0 license is copied from its package repository commit6fa3825973949a9c4f0cd8af344e15a8db09dc35, as recorded in the NuGet package metadata: https://github.com/modelcontextprotocol/csharp-sdk/blob/6fa3825973949a9c4f0cd8af344e15a8db09dc35/LICENSE .

The self-contained publisher copies LICENSE and THIRD-PARTY-NOTICES from the exact included .NET runtime packs into artifacts/app/licenses. The locally bundled Three.js/fflate loader notices remain under wwwroot/vendor/three. Original game/mod resources are read locally and are not distributed.

Data taken from TileZed in [PZ Mapping Tools](https://github.com/Unjammer/PZ_Mapping_Tools) (Tim Baker; Alree/Unjammer), GPL-2.0-or-later, which is compatible with this project's GPL v2 licence:
- `wwwroot/room-catalog.json`, from `TileZed/RoomNames.txt`;
- `wwwroot/wall-catalog.json`, from the `exterior_walls` category of `TileZed/src/tiled/BuildingEditor/BuildingTiles.txt` (revision 42), with tile indices written without leading zeros;
- the reference fence and wicket styles in `GeographicImporter.Fences.cs`, from `TileZed/Fences.txt`;
- `wwwroot/furniture-catalog.json`, beds, dressers, counters, stoves, fridges, sinks, toilets, a bath, sofas, armchairs and TVs from `BuildingFurniture.txt` (revision 48);
- `wwwroot/building-templates.json`, the 25 templates of `BuildingTemplates.txt` (revision 30) with their tile entries resolved.
9 changes: 9 additions & 0 deletions examples/CiScene/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# CI scene

A small synthetic scene for the command line: 512×256 m at 1 m per tile, built as 256-tile sections (two of them hold content). It has a secondary road with a residential side road (a stop sign), houses on both sections, one house across the section seam (a shared world building), a fence across the seam (a shared world fence), a parking area and farmland.

```bash
simplepzmap build examples/CiScene/ci-scene.json --out work/ci/ci.pzworld.json --export work/ci/ci.zip --strict
```

The geometry is invented for testing and describes no real place.
24 changes: 24 additions & 0 deletions examples/CiScene/ci-scene.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"format": "SimplePZScene",
"version": 1,
"name": "CiScene",
"source": "ci-source.geojson",
"reference": {
"bounds": {
"south": 54.0,
"north": 54.0022997,
"west": 32.0,
"east": 32.0078251
},
"metresPerTile": 1,
"originCellX": 200,
"originCellY": 200
},
"sectionSize": 256,
"seed": 7,
"skipEmptySections": true,
"export": {
"modId": "SimplePZCiScene",
"name": "SimplePZ CI scene"
}
}
Loading