Terrain Change Policy
Terrain brush paint and restore (demolish-to-previous-ground) can be restricted per cell. GridPlacement provides a generic, engine-agnostic validator; games choose occupancy behavior and may read TileSet custom data in the Godot adapter.
When this applies
| Action | Validated by |
|---|---|
Terrain palette paint (GridMode.Paint / tiles category) | TerrainChangeValidator via PlacementControllerBase.CanChangeTerrainAt |
| Terrain restore on demolish (when no ECS occupant removed) | Same check before TerrainPaintManager.RestoreCells |
| Structure / crop placement | Not affected — still uses IsOccupied, IsBuildable, and placement rules |
TileSet custom data (hard block)
Add a bool custom data layer on your TileSet:
| Key | Type | Effect |
|---|---|---|
terrain_change_blocked | bool | When true, paint and restore are denied for that atlas tile |
This is defined in Core as TerrainChangeCustomData.ChangeBlockedKey. The Godot adapter reads it from the committed ground TileMapLayer via TileMapCustomData.
Designer-authored examples: bedrock, story tiles, water that must not be overwritten.
Occupancy policy (game choice)
TerrainChangeOccupancyPolicy controls whether occupied cells (per PlacementControllerBase.IsOccupied) may still be brushed:
| Policy | Behavior |
|---|---|
AllowWhenOccupied (default) | Player may paint over crops/buildings; the game handles cleanup (farmability, crop drops, demolish rules, etc.). |
BlockWhenOccupied | Brush is denied while the cell is occupied (preview shows invalid). |
Set on your derived placement controller:
| |
Tile data terrain_change_blocked always wins: a blocked tile cannot be painted even when occupancy is allowed.
Core API (headless / tests)
| |
Godot override points
On PlacementControllerBase:
| Member | Purpose |
|---|---|
TerrainChangeOccupancyPolicy | Occupancy policy property |
CanChangeTerrainAt(CoreVector2I) | Uses validator; override for extra game rules |
IsTerrainChangeBlockedByTileData(CoreVector2I) | Reads terrain_change_blocked from GroundLayer; override for non-TileSet sources |
After a successful paint, continue to sync simulation in OnTerrainPainted(TerrainPaintImpact) (farmability, crops, etc.) — that remains game-level.
Related guides
- Terrain Sync Guide — autotile connect and neighborhood sync
- Tile Placement Mode —
GridMode.Paint