Deleting a painted tile leaves invisible cells on the tilemaps #268

Closed
opened 2026-09-29 16:22:08 +02:00 by meleki · 1 comment
Owner

Found in refactoring round 7 (#85, bc688f3).

What happens

Deleting a tile asset that is painted on a tilemap removes only the asset. The cells keep the id of the deleted tile. layerTiles skips such cells on purpose, so that Ctrl+Z brings the drawing back. After a reload there is no undo any more, and the cells stay in the project file for good.

  • The delete dialog says "This cannot be undone with Ctrl+Z after reload." It does not mention the tilemaps or the cells (src/editor/actions.ts, wornDetail knows sprites, animations and tiles, but no tilemaps).
  • The cells draw nothing and block nothing, in the editor and in the game.
  • Tile at reports such a cell as not empty: empty is false and tile is the id of the deleted tile, for a cell that looks empty (liveTileAt in src/core/model/liveTilemap.ts).
  • The fill tool treats them as a tile of their own (floodFill in src/core/model/tilemap.ts). Filling an empty area stops at them and leaves holes. Filling from one of them fills only the invisible cells.
  • Only the pipette knows about them: "This cell names a tile that no longer exists."

Expected

  • The delete dialog names the tilemaps that use the tile and how many cells, as it does for sprites, animations and tiles.
  • Deleting the tile empties those cells in the same command. Ctrl+Z brings the tile and its cells back together, and nothing invisible stays in the project.
  • Cells that already name a missing tile (projects saved before the fix) count as empty everywhere: drawing, collision, Tile at and the fill tool. No migration of the project files.
Found in refactoring round 7 (#85, bc688f3). **What happens** Deleting a tile asset that is painted on a tilemap removes only the asset. The cells keep the id of the deleted tile. `layerTiles` skips such cells on purpose, so that Ctrl+Z brings the drawing back. After a reload there is no undo any more, and the cells stay in the project file for good. - The delete dialog says "This cannot be undone with Ctrl+Z after reload." It does not mention the tilemaps or the cells (`src/editor/actions.ts`, `wornDetail` knows sprites, animations and tiles, but no tilemaps). - The cells draw nothing and block nothing, in the editor and in the game. - `Tile at` reports such a cell as not empty: `empty` is false and `tile` is the id of the deleted tile, for a cell that looks empty (`liveTileAt` in `src/core/model/liveTilemap.ts`). - The fill tool treats them as a tile of their own (`floodFill` in `src/core/model/tilemap.ts`). Filling an empty area stops at them and leaves holes. Filling from one of them fills only the invisible cells. - Only the pipette knows about them: "This cell names a tile that no longer exists." **Expected** - The delete dialog names the tilemaps that use the tile and how many cells, as it does for sprites, animations and tiles. - Deleting the tile empties those cells in the same command. Ctrl+Z brings the tile and its cells back together, and nothing invisible stays in the project. - Cells that already name a missing tile (projects saved before the fix) count as empty everywhere: drawing, collision, `Tile at` and the fill tool. No migration of the project files.
meleki added the high-priority label 2026-09-29 16:22:08 +02:00
admin added this to the Development project 2026-09-29 16:23:14 +02:00
admin moved this to Next in Development on 2026-09-29 16:23:16 +02:00
admin moved this to Done in Development on 2026-09-29 16:23:21 +02:00
admin moved this to Next in Development on 2026-09-29 16:24:27 +02:00
Author
Owner

Done in 9b5eeaa, wiki d345d99.

What you get

  • Deleting a tile empties the cells painted with it on every layer of every tilemap, in the same step. Ctrl+Z brings the tile and its cells back together.
  • The delete dialog says so, for example "3 cell(s) on 1 tilemap(s) are painted with it and become empty."
  • Cells that already name a missing tile (older files, hand edits) count as empty: Tile at reads them as empty and Fill fills across them. Drawing and collision skipped them before already. There is no migration, the file keeps such a cell until it is painted over or erased.
  • A second bug, fixed along the way: with the deleted tile still in hand, Brush, Rect and Fill painted new invisible cells at once. Now painting says "Choose a tile to paint with."
  • Unchanged on purpose: Pick still reports such a cell as naming a tile that no longer exists, and erasing removes it from the file.

Under the hood

  • DeleteAssetCommand keeps the emptied cells per layer for undo (cellsWithout in model/tilemap.ts).
  • presentTile (model/tilemap.ts) is the one rule "a missing tile is empty". World.tileAt, floodFill (now takes the project) and the painter use it.
  • cellsPainted (model/usages.ts) counts cells and maps for the dialog.
  • Wiki: Tiles (deleting a tile, under "Where they are placed"), Tilemaps (Fill, the tile palette, Tile at), Development (sum).

Tests

./check.sh           → 195 files, 2823 tests, CHECK EXIT 0   (+6)
GM_STEPS=39,40 (e2e) → ALL PASSED – 100 checks   (4 new in 40t)
  • The full e2e sum is now 1590: 1586 confirmed by CI on round 8, plus the 4 new checks. The CI run for 9b5eeaa was not awaited.
  • Counter-checks, all fail as expected:
    • 5 in unit tests: delete without emptying, undo without the cells, Fill and Tile at without presentTile, wrong map count.
    • 3 in the browser: dialog without the count, delete without emptying, brush without the check.
  • The second browser counter-check showed my brush check failing for the wrong reason. It now clicks a cell only it touches and was counter-checked again.
  • Not tested on its own: Fill with the deleted tile still in hand. It goes through the same check as the brush.
Done in `9b5eeaa`, wiki `d345d99`. **What you get** - **Deleting a tile empties the cells painted with it** on every layer of every tilemap, in the same step. Ctrl+Z brings the tile and its cells back together. - The delete dialog says so, for example "3 cell(s) on 1 tilemap(s) are painted with it and become empty." - **Cells that already name a missing tile** (older files, hand edits) count as empty: `Tile at` reads them as empty and **Fill** fills across them. Drawing and collision skipped them before already. There is no migration, the file keeps such a cell until it is painted over or erased. - **A second bug, fixed along the way:** with the deleted tile still in hand, Brush, Rect and Fill painted new invisible cells at once. Now painting says "Choose a tile to paint with." - Unchanged on purpose: **Pick** still reports such a cell as naming a tile that no longer exists, and erasing removes it from the file. **Under the hood** - `DeleteAssetCommand` keeps the emptied cells per layer for undo (`cellsWithout` in `model/tilemap.ts`). - `presentTile` (`model/tilemap.ts`) is the one rule "a missing tile is empty". `World.tileAt`, `floodFill` (now takes the project) and the painter use it. - `cellsPainted` (`model/usages.ts`) counts cells and maps for the dialog. - Wiki: *Tiles* (deleting a tile, under "Where they are placed"), *Tilemaps* (Fill, the tile palette, `Tile at`), *Development* (sum). **Tests** ``` ./check.sh → 195 files, 2823 tests, CHECK EXIT 0 (+6) GM_STEPS=39,40 (e2e) → ALL PASSED – 100 checks (4 new in 40t) ``` - The full e2e sum is now 1590: 1586 confirmed by CI on round 8, plus the 4 new checks. The CI run for `9b5eeaa` was not awaited. - **Counter-checks, all fail as expected:** - 5 in unit tests: delete without emptying, undo without the cells, Fill and `Tile at` without `presentTile`, wrong map count. - 3 in the browser: dialog without the count, delete without emptying, brush without the check. - The second browser counter-check showed my brush check failing for the wrong reason. It now clicks a cell only it touches and was counter-checked again. - Not tested on its own: Fill with the deleted tile still in hand. It goes through the same check as the brush.
admin moved this to Done in Development on 2026-09-29 19:02:35 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#268