The tab headers are one TabHeader, the node board and the tile board pan and zoom through one helper, reordering tile pictures and tilemap layers goes through one moveItem, and a tilemap cell is one named type (#85)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c91c18f495
commit
6d7d0005cf
19 files changed
+187
-195
No files matched your search
@@ -26,14 +26,14 @@ export const TILE_COLLISION_SCALE_MIN = 0.1;
|
||||
export const TILE_COLLISION_SCALE_MAX = 2;
|
||||
|
||||
/** Only the two that make sense for a tile that is always on screen; there is no end to hold. */
|
||||
export const TILE_PLAYBACKS = ['loop', 'pingpong'] as const;
|
||||
const TILE_PLAYBACKS = ['loop', 'pingpong'] as const;
|
||||
export type TilePlayback = (typeof TILE_PLAYBACKS)[number];
|
||||
|
||||
/** Quadrant columns of a block: one tile for `none`, two for the autotile blocks. */
|
||||
export const BLOCK_COLUMNS: Record<TileKind, number> = { none: 2, floor: 4, wall: 4 };
|
||||
const BLOCK_COLUMNS: Record<TileKind, number> = { none: 2, floor: 4, wall: 4 };
|
||||
|
||||
/** Quadrant rows of a block: `none` is one tile tall, `floor` three and `wall` two. */
|
||||
export const BLOCK_ROWS: Record<TileKind, number> = { none: 2, floor: 6, wall: 4 };
|
||||
const BLOCK_ROWS: Record<TileKind, number> = { none: 2, floor: 6, wall: 4 };
|
||||
|
||||
export function tileKind(asset: TileAsset): TileKind {
|
||||
const value = textOf(asset.props[TILE_KIND_KEY] ?? 'none');
|
||||
|
||||
+13
-19
@@ -49,11 +49,17 @@ export function tilemapSize(props: PropertyBag): { w: number; h: number } {
|
||||
return { w: tilemapColumns(props) * tile, h: tilemapRows(props) * tile };
|
||||
}
|
||||
|
||||
/** A cell of the grid by column and row; `0,0` is the top-left corner. */
|
||||
export interface Cell {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export function cellKey(x: number, y: number): string {
|
||||
return `${x},${y}`;
|
||||
}
|
||||
|
||||
export function parseCellKey(key: string): { x: number; y: number } | null {
|
||||
export function parseCellKey(key: string): Cell | null {
|
||||
const [x, y] = key.split(',').map(Number);
|
||||
return Number.isInteger(x) && Number.isInteger(y) ? { x: x as number, y: y as number } : null;
|
||||
}
|
||||
@@ -152,7 +158,7 @@ export function tilemapTileIds(asset: { layers: TilemapLayer[] }): string[] {
|
||||
* The cell a point in the tilemap's own frame falls into. The grid is centred on the object's
|
||||
* position, as every footprint is, so the top-left corner lies half a map up and to the left.
|
||||
*/
|
||||
export function cellAtPoint(props: PropertyBag, x: number, y: number): { x: number; y: number } {
|
||||
export function cellAtPoint(props: PropertyBag, x: number, y: number): Cell {
|
||||
const tile = tilemapTileSize(props);
|
||||
const { w, h } = tilemapSize(props);
|
||||
return { x: Math.floor((x + w / 2) / tile), y: Math.floor((y + h / 2) / tile) };
|
||||
@@ -214,12 +220,8 @@ export function edgeCompensation(
|
||||
}
|
||||
|
||||
/** The cells of the rectangle between two corners, both included and clipped to the grid. */
|
||||
export function cellsInRect(
|
||||
props: PropertyBag,
|
||||
a: { x: number; y: number },
|
||||
b: { x: number; y: number },
|
||||
): { x: number; y: number }[] {
|
||||
const cells: { x: number; y: number }[] = [];
|
||||
export function cellsInRect(props: PropertyBag, a: Cell, b: Cell): Cell[] {
|
||||
const cells: Cell[] = [];
|
||||
const x0 = Math.max(0, Math.min(a.x, b.x));
|
||||
const x1 = Math.min(tilemapColumns(props) - 1, Math.max(a.x, b.x));
|
||||
const y0 = Math.max(0, Math.min(a.y, b.y));
|
||||
@@ -234,12 +236,7 @@ export function cellsInRect(
|
||||
* count, or a fill would leak through the gap between two corners. Filling with the tile already
|
||||
* there changes nothing, so that returns no cells.
|
||||
*/
|
||||
export function floodFill(
|
||||
props: PropertyBag,
|
||||
layer: TilemapLayer,
|
||||
start: { x: number; y: number },
|
||||
tileId: string,
|
||||
): { x: number; y: number }[] {
|
||||
export function floodFill(props: PropertyBag, layer: TilemapLayer, start: Cell, tileId: string): Cell[] {
|
||||
if (!inGrid(props, start.x, start.y)) return [];
|
||||
const from = cellAt(layer, start.x, start.y);
|
||||
if (from === tileId) return [];
|
||||
@@ -269,13 +266,10 @@ export function floodFill(
|
||||
* The cells a stroke from one cell to another covers. A pointer jumps between events, so without
|
||||
* this a fast drag would paint dots instead of a line.
|
||||
*/
|
||||
export function cellsAlong(
|
||||
from: { x: number; y: number },
|
||||
to: { x: number; y: number },
|
||||
): { x: number; y: number }[] {
|
||||
export function cellsAlong(from: Cell, to: Cell): Cell[] {
|
||||
const steps = Math.max(Math.abs(to.x - from.x), Math.abs(to.y - from.y));
|
||||
if (steps === 0) return [{ ...to }];
|
||||
const cells: { x: number; y: number }[] = [];
|
||||
const cells: Cell[] = [];
|
||||
for (let i = 1; i <= steps; i++)
|
||||
cells.push({
|
||||
x: Math.round(from.x + ((to.x - from.x) * i) / steps),
|
||||
|
||||
@@ -16,10 +16,7 @@ export interface SceneObjectRef {
|
||||
object: SceneObject;
|
||||
}
|
||||
|
||||
export function objectsWhere(
|
||||
project: Project,
|
||||
predicate: (object: SceneObject) => boolean,
|
||||
): SceneObjectRef[] {
|
||||
function objectsWhere(project: Project, predicate: (object: SceneObject) => boolean): SceneObjectRef[] {
|
||||
const result: SceneObjectRef[] = [];
|
||||
for (const holder of objectHolders(project)) {
|
||||
for (const object of holder.objects) if (predicate(object)) result.push({ holder, object });
|
||||
|
||||
Reference in new issue
Block a user