The tilemap panel gains Fill and Pick: Fill floods the connected cells of the active layer that carry the same tile (edge neighbours only, bounded by the grid) as one undo step, Pick takes the tile of the clicked cell on the active layer, and holding Alt picks while any tool is active (#133)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c2fd536aaf
commit
0b32d3cbf3
9 files changed
+350
-20
No files matched your search
@@ -24,12 +24,14 @@ import { placementRejection } from '../../core/model/placement';
|
||||
import type { Position, Vec2 } from '../../core/model/Properties';
|
||||
import { findAsset, findAssetOf, type ObjectHolder } from '../../core/model/Project';
|
||||
import {
|
||||
cellAt,
|
||||
cellAtPoint,
|
||||
cellKey,
|
||||
cellOrigin,
|
||||
cellsAlong,
|
||||
cellsInRect,
|
||||
findLayer,
|
||||
floodFill,
|
||||
inGrid,
|
||||
tilemapColumns,
|
||||
tilemapRows,
|
||||
@@ -39,7 +41,7 @@ import type { CellRect } from '../../core/model/tilemapCollision';
|
||||
import { footprintCorners } from '../../core/properties/bounds';
|
||||
import { localMatrixOf, parentTransform, transformOf } from '../../core/properties/transform';
|
||||
import { type EditorState, objectSelection, type TilemapTool } from '../state';
|
||||
import { paintedLayerId, paintedTilemap } from '../panels/TilemapPanel';
|
||||
import { paintedLayerId, paintedTilemap, pickTile } from '../panels/TilemapPanel';
|
||||
import { activeDrag, drawBand, drawGrid, endDrag, onDragChange, Theme, textStyle } from '../../ui';
|
||||
import { collectHolderItems, type DrawItem } from '../../viewport/items';
|
||||
import type { LayerFocus } from '../../viewport/tilemap';
|
||||
@@ -363,6 +365,57 @@ export class SceneViewport extends FreeViewport {
|
||||
return cellAtPoint(item.props, inMap.x, inMap.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* What a press with a painting tool does. Fill and pick are done with the click – a drag adds
|
||||
* nothing to either – so only the other three start a stroke. True when the press was used.
|
||||
*/
|
||||
private usePaintTool(
|
||||
target: { item: DrawItem; asset: TilemapAsset; layerId: string },
|
||||
holderId: string,
|
||||
e: FederatedPointerEvent,
|
||||
): boolean {
|
||||
const tool = this.state.tilemapPaint.tool;
|
||||
if (tool !== 'fill' && tool !== 'pick') return this.startStroke(target, holderId, e);
|
||||
const layer = findLayer(target.asset, target.layerId);
|
||||
const at = this.cellUnder(target.item, e);
|
||||
if (!layer || !inGrid(target.item.props, at.x, at.y)) return true;
|
||||
if (tool === 'pick') this.pickCell(layer, at);
|
||||
else this.fillFrom(target, layer, at);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Takes the tile of a cell on the painted layer – the one painted on next (#133). */
|
||||
private pickCell(layer: TilemapLayer, at: Vec2): void {
|
||||
const tileId = cellAt(layer, at.x, at.y);
|
||||
if (!tileId) this.state.setNotice('This cell is empty on the active layer.');
|
||||
else if (!findAssetOf(this.state.project, tileId, 'tile'))
|
||||
this.state.setNotice('This cell names a tile that no longer exists.');
|
||||
else pickTile(this.state, tileId);
|
||||
}
|
||||
|
||||
/** Fills the connected cells of one tile in a single command, so one click is one undo step. */
|
||||
private fillFrom(
|
||||
target: { item: DrawItem; asset: TilemapAsset; layerId: string },
|
||||
layer: TilemapLayer,
|
||||
at: Vec2,
|
||||
): void {
|
||||
const tileId = this.state.tilemapPaint.tileId;
|
||||
if (!tileId) {
|
||||
this.state.setNotice('Choose a tile to paint with.');
|
||||
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',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private startStroke(
|
||||
target: { item: DrawItem; asset: TilemapAsset; layerId: string },
|
||||
holderId: string,
|
||||
@@ -462,7 +515,7 @@ export class SceneViewport extends FreeViewport {
|
||||
}
|
||||
// A painting tool on the selected tilemap: the press starts a stroke instead of a move.
|
||||
const target = this.paintTargetOf(objectId);
|
||||
if (target && this.startStroke(target, holder.id, e)) return;
|
||||
if (target && this.usePaintTool(target, holder.id, e)) return;
|
||||
// Clicking a selected object keeps the selection so the whole group can be dragged.
|
||||
if (!this.state.selectedObjectIds.includes(objectId))
|
||||
this.state.select(objectSelection(holder.id, [objectId]));
|
||||
|
||||
Reference in new issue
Block a user