Painting over identical tiles no longer adds an empty undo step (#212)
A rectangle drawn over cells that already hold exactly that tile
pushed a command that changed nothing. Undo then seemed to do nothing
and the project was marked as modified. PaintTilesCommand.changes()
was meant to prevent that, but nothing called it since #128.
- New paintTiles() in tilemapCommands.ts returns the command for a
finished stroke or fill, or null when no cell changes. It is the
first caller of PaintTilesCommand.changes().
- All three paint paths in the Scene View go through it: brush and
eraser, rectangle (both in endStroke) and flood fill (fillFrom).
- Flood fill was not affected, because floodFill already returns []
for the same tile. The guard is there anyway, as the issue asked.
- A rectangle that changes even one cell stays a normal undo step.
Tests:
- ./check.sh: 145 files, 2190 tests, CHECK EXIT 0 (before 2187).
- 3 new unit tests in tests/core/tilemap.test.ts: same rectangle
twice gives no command, a wider rectangle still does, a fill that
finds nothing gives no command.
- 1 new e2e check in 40d: the same rectangle again, then one undo
must take 9 cells to 0. A redo restores them for later blocks.
- GM_STEPS=39,40 on the final build: ALL PASSED, 81 checks.
- Counter-probe with changes(...) || true: exactly the two unit
tests fail, and in the browser exactly the new check fails with
{"afterUndo":9}. Original restored via cmp (IDENTICAL), rebuilt.
- Editor suite check total: CI measured 1312 at 0e85856, new total
1313 (calculated) in README and wiki.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0e8585665a
commit
487b3aa5b4
5 files changed
+80
-20
No files matched your search
@@ -82,6 +82,21 @@ export class PaintTilesCommand implements Command {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The command for a finished stroke or fill, or null when it would change no cell: such a step
|
||||
* would undo nothing and still count as an edit of the project (#212).
|
||||
*/
|
||||
export function paintTiles(
|
||||
layer: TilemapLayer,
|
||||
assetId: string,
|
||||
edits: readonly CellEdit[],
|
||||
label?: string,
|
||||
): PaintTilesCommand | null {
|
||||
return PaintTilesCommand.changes(layer, edits)
|
||||
? new PaintTilesCommand(assetId, layer.id, edits, label)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where a placed tilemap's origin goes when `edge` gains `count` cells, starting from `from`, so the
|
||||
* painted cells stay where they are in the scene (#136). The shift is worked out in the tilemap's
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
PlaceAssetCommand,
|
||||
SetOverrideCommand,
|
||||
} from '../../core/commands/sceneObjectCommands';
|
||||
import { type CellEdit, PaintTilesCommand } from '../../core/commands/tilemapCommands';
|
||||
import { type CellEdit, paintTiles } from '../../core/commands/tilemapCommands';
|
||||
import type { TilemapAsset, TilemapLayer } from '../../core/model/Asset';
|
||||
import { hasSceneIcon } from '../../core/model/AssetTypes';
|
||||
import { findObject, subtreeRoots } from '../../core/model/hierarchy';
|
||||
@@ -503,15 +503,13 @@ export class SceneViewport extends FreeViewport {
|
||||
return;
|
||||
}
|
||||
const cells = floodFill(target.item.props, layer, at, tileId);
|
||||
if (cells.length > 0)
|
||||
this.state.execute(
|
||||
new PaintTilesCommand(
|
||||
target.asset.id,
|
||||
target.layerId,
|
||||
cells.map((cell) => ({ ...cell, tileId })),
|
||||
'Fill tiles',
|
||||
),
|
||||
);
|
||||
const command = paintTiles(
|
||||
layer,
|
||||
target.asset.id,
|
||||
cells.map((cell) => ({ ...cell, tileId })),
|
||||
'Fill tiles',
|
||||
);
|
||||
if (command) this.state.execute(command);
|
||||
}
|
||||
|
||||
private startStroke(
|
||||
@@ -588,15 +586,10 @@ export class SceneViewport extends FreeViewport {
|
||||
tileId: stroke.tileId,
|
||||
}))
|
||||
: stroke.edits;
|
||||
if (edits.length > 0)
|
||||
this.state.execute(
|
||||
new PaintTilesCommand(
|
||||
stroke.assetId,
|
||||
stroke.layerId,
|
||||
edits,
|
||||
stroke.tool === 'erase' ? 'Erase tiles' : 'Paint tiles',
|
||||
),
|
||||
);
|
||||
const command =
|
||||
layer &&
|
||||
paintTiles(layer, stroke.assetId, edits, stroke.tool === 'erase' ? 'Erase tiles' : 'Paint tiles');
|
||||
if (command) this.state.execute(command);
|
||||
else this.state.preview();
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user