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)
game-maker / gate (push) Successful in 1m42s
game-maker / suite (push) Successful in 6m9s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5 committed 2026-09-18 16:01:12 +02:00
1 parent c91c18f495
commit 6d7d0005cf
19 files changed
+187 -195

No files matched your search

+3 -3
View File
@@ -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
View File
@@ -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),
+1 -4
View File
@@ -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 });