/** * Which tilemap the painting panel and the scene view's brush work on (#142): exactly one placed * map, selected in the active scene. Everything else – nothing, two objects, the asset itself, a * map in a scene that is not showing – leaves nothing to paint. */ import { describe, expect, it } from 'vitest'; import { CreateAssetCommand } from '../../src/core/commands/assetCommands'; import { PlaceAssetCommand } from '../../src/core/commands/sceneObjectCommands'; import { createEmptyProject } from '../../src/core/model/Project'; import { chooseTool, holdPick, paintedTilemap, pickTile } from '../../src/editor/panels/TilemapPanel'; import { EditorState, objectSelection } from '../../src/editor/state'; function setup() { const state = new EditorState(); state.loadProject(createEmptyProject('p', 'P'), 1); const create = (type: 'tilemap' | 'sprite' | 'scene', name: string) => { const command = new CreateAssetCommand(type, name); expect(state.execute(command)).toBe(true); return command.asset.id; }; // The first scene there is becomes the active one by itself. const sceneId = create('scene', 'Main'); expect(state.activeScene?.id).toBe(sceneId); const place = (holderId: string, assetId: string) => { const command = new PlaceAssetCommand(holderId, assetId, { x: 0, y: 0 }); expect(state.execute(command)).toBe(true); return command.objectId; }; const tilemapId = create('tilemap', 'Ground'); return { state, sceneId, tilemapId, create, place }; } describe('the painted tilemap', () => { it('is the one placed map selected in the active scene', () => { const { state, sceneId, tilemapId, place } = setup(); const map = place(sceneId, tilemapId); state.select(objectSelection(sceneId, [map])); expect(paintedTilemap(state)).toMatchObject({ objectId: map, asset: { id: tilemapId } }); }); it('is none without a selection, for another object and for the asset itself', () => { const { state, sceneId, tilemapId, create, place } = setup(); expect(paintedTilemap(state)).toBeNull(); const sprite = place(sceneId, create('sprite', 'Box')); state.select(objectSelection(sceneId, [sprite])); expect(paintedTilemap(state)).toBeNull(); state.select({ kind: 'asset', assetId: tilemapId }); expect(paintedTilemap(state)).toBeNull(); }); it('is none while a second object is selected with it, map or not', () => { const { state, sceneId, tilemapId, create, place } = setup(); const map = place(sceneId, tilemapId); const other = place(sceneId, tilemapId); const sprite = place(sceneId, create('sprite', 'Box')); state.select(objectSelection(sceneId, [map, other], map)); expect(paintedTilemap(state)).toBeNull(); state.select(objectSelection(sceneId, [map, sprite], map)); expect(paintedTilemap(state)).toBeNull(); state.select(objectSelection(sceneId, [map])); expect(paintedTilemap(state)?.objectId).toBe(map); }); it('picks with the Pick tool and hands the tile to the painting tool before it (#133)', () => { const { state, sceneId, tilemapId, place } = setup(); state.select(objectSelection(sceneId, [place(sceneId, tilemapId)])); chooseTool(state, 'rect'); chooseTool(state, 'pick'); pickTile(state, 'grass'); expect(state.tilemapPaint).toMatchObject({ tool: 'rect', tileId: 'grass' }); // From a tool that does not paint with a tile, the pick goes to the brush. chooseTool(state, 'erase'); chooseTool(state, 'pick'); pickTile(state, 'wall'); expect(state.tilemapPaint).toMatchObject({ tool: 'brush', tileId: 'wall' }); }); it('picks while Alt is held and goes back to the tool before it on release', () => { const { state, sceneId, tilemapId, place } = setup(); state.select(objectSelection(sceneId, [place(sceneId, tilemapId)])); chooseTool(state, 'fill'); expect(holdPick(state, true)).toBe(true); expect(state.tilemapPaint.tool).toBe('pick'); pickTile(state, 'grass'); expect(state.tilemapPaint).toMatchObject({ tool: 'pick', tileId: 'grass' }); expect(holdPick(state, false)).toBe(true); expect(state.tilemapPaint).toMatchObject({ tool: 'fill', tileId: 'grass' }); // Held from Select, too: the release brings Select back. chooseTool(state, 'select'); holdPick(state, true); holdPick(state, false); expect(state.tilemapPaint.tool).toBe('select'); }); it('leaves Alt alone without a map, and a tool chosen while holding it stays', () => { const { state, sceneId, tilemapId, place } = setup(); expect(holdPick(state, true)).toBe(false); expect(state.tilemapPaint.tool).toBe('select'); const map = place(sceneId, tilemapId); state.select(objectSelection(sceneId, [map])); chooseTool(state, 'brush'); holdPick(state, true); chooseTool(state, 'rect'); expect(holdPick(state, false)).toBe(false); expect(state.tilemapPaint.tool).toBe('rect'); // Released after the map was deselected: nothing to paint on, so Select. holdPick(state, true); state.select(null); holdPick(state, false); expect(state.tilemapPaint.tool).toBe('select'); }); it('is none for a map in a scene that is not the one showing', () => { const { state, tilemapId, create, place } = setup(); const otherScene = create('scene', 'Elsewhere'); const map = place(otherScene, tilemapId); state.select(objectSelection(otherScene, [map])); expect(paintedTilemap(state)).toBeNull(); }); });