Files
feuerwerk/src/core/model/Properties.ts
T
rootandClaude Opus 5 a9c3111377 Let a variable hold a list
A variable could only be a number, a text or a yes/no, so every set of values had
to be smuggled through a string - which is where Tetris got its unreadable
"03066326048763714" colours from. A list is now a value of its own, with an item
kind (number, text, yes/no, colour) that decides how the inspector reads the one
comma-separated line it is typed as, and eight nodes to work with: List, Fill,
Count, Item at, Set item, Sub list, Join lists, Index of.

Two traps came with it. An array is typeof 'object', so every place that told a
value from an entity had to learn the difference, or a list would have been
passed around as a game object. And a value was compared by identity in three
places - with lists that marks everything as an overridden property after each
reload, so those comparisons now compare contents.

There is no list of objects: a list is written to the project file and an object
is a live handle that no reload survives. A set of objects stays a list of names.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 21:26:31 +02:00

49 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* A list is deliberately flat – a list of lists would need a nested editor and a deep compare on
* every override, and nothing so far wants one: a board is addressed as `row · width + column`.
*/
export type ListValue = readonly (number | string | boolean)[];
/** Colors are stored as 0xRRGGBB numbers. */
export type PropertyValue = number | string | boolean | ListValue;
export type PropertyBag = Record<string, PropertyValue>;
export const isList = (value: unknown): value is ListValue => Array.isArray(value);
/** Values are compared by content, because two equal lists are never the same array. */
export function sameValue(a: PropertyValue | undefined, b: PropertyValue | undefined): boolean {
if (isList(a) || isList(b)) {
if (!isList(a) || !isList(b) || a.length !== b.length) return false;
return a.every((item, i) => item === b[i]);
}
return a === b;
}
/** `undefined` removes the key, which is how an undo of "no value" works. */
export function setBagValue(bag: PropertyBag, key: string, value: PropertyValue | undefined): void {
if (value === undefined) delete bag[key];
else bag[key] = value;
}
export interface Vec2 {
x: number;
y: number;
}
/** `x`/`y` in world units; `z` is the draw order – higher is on top, ties keep hierarchy order. */
export interface Position extends Vec2 {
z: number;
}
export type Axis = keyof Position;
export function toPosition(point: Vec2 | Position): Position {
return { x: point.x, y: point.y, z: 'z' in point ? point.z : 0 };
}
/** Stable draw order: ascending `z`, ties keep the input (hierarchy) order. */
export function sortByZ<T extends { z: number }>(items: readonly T[]): T[] {
return [...items].sort((a, b) => a.z - b.z);
}