The new node "Raycast all" answers with every collider along the ray instead of only the nearest one: it carries the same inputs and settings as Raycast (Ignore owner, Hit triggers, Layers from #158) plus "max hits" where 0 means all, and it hands back parallel lists sorted by distance - objects, colliders, points x/y, normals x/y, distances - next to count, so index i of every list speaks about the same hit; a collider counts once no matter how many convex parts its shape is built from, while a tilemap counts per cell it is hit in, and the filter sits in Solids.raycastAll like the one of #158, the one path the editor run and Play both take, so all 12 new unit tests run through describe.each against both systems; the node needed the value system to learn something it could not do - a graph list carried numbers, text, booleans and bundles but no object handles, asList turned an object into its name, so objects/colliders would have been lists of names, which loses the object when two carry the same name, and collider children usually do; Value now knows a travelling list that may hold handles and "Item at" gives such an item out as an object, ready for Destroy, Apply force or Name of, while the single door into a stored file - writeTyped/asTypeOf - runs the list through the new storedList() so every object becomes its name again and nothing living reaches a project file, which is its own unit test over JSON.stringify; three list nodes follow the handles: "Index of" compares objects by identity because a name is not the object of that name, "Set item" and "Fill" write an object as an object, and "Join text" prints the names instead of the innards; this decision was mine to make because the question ran into the MCP timeout - the alternative was a list of names plus "Object by name", which is the smaller change but lossy; 4 new e2e checks in the new block 43e (shield and floor in one go reading 2 and "145, 280", the second object answering to its name, which only a real object in a list can do, "max hits" 1 stopping at the nearest, and the node showing the row) and 18 new unit tests, 12 in raycast.test.ts, 4 on the value system and 2 in the node catalog; two counter-checks hold: not counting tilemap cells one by one drops 2 unit tests, and answering with the nearest hit only drops module 43 with 2 FAILs; one finding on the way - the node read "maxHits" while every other input carries a spoken label, because int() took the key as its label, so it has a third parameter now, and the e2e check caught that, no unit test did (#157)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0fdde6cb1e
commit
ffdf0a8ddb
20 files changed
+607
-33
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asListItem, asNumber } from '../../values';
|
||||
import { asNumber, asValueItem } from '../../values';
|
||||
import { any, int, list } from '../shared';
|
||||
|
||||
/** Same order of magnitude as the interpreter's step limit – a typo in `count` must not eat memory. */
|
||||
@@ -15,6 +15,6 @@ export const Fill: NodeDef = {
|
||||
params: [],
|
||||
run: (_ctx, inputs) => {
|
||||
const count = Math.min(MAX_ITEMS, Math.max(0, Math.floor(asNumber(inputs.count))));
|
||||
return { outputs: { result: new Array(count).fill(asListItem(inputs.value)) } };
|
||||
return { outputs: { result: new Array(count).fill(asValueItem(inputs.value)) } };
|
||||
},
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { sameValue, untag } from '../../../model/Properties';
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asList, asListItem } from '../../values';
|
||||
import { asList, asValueItem, isEntity } from '../../values';
|
||||
import { any, list, num } from '../shared';
|
||||
|
||||
export const IndexOf: NodeDef = {
|
||||
@@ -14,9 +14,15 @@ export const IndexOf: NodeDef = {
|
||||
// Compared by content, not by identity: two equal structs are never the same object. A color item
|
||||
// is compared as its number, or searching a list of colors with one would never find it.
|
||||
run: (_ctx, inputs) => {
|
||||
const wanted = untag(asListItem(inputs.value));
|
||||
const wanted = asValueItem(inputs.value);
|
||||
// Two objects are the same one or they are not – there is no content to compare (#157).
|
||||
if (isEntity(wanted))
|
||||
return { outputs: { index: asList(inputs.list).findIndex((item) => item === wanted) } };
|
||||
const value = untag(wanted);
|
||||
return {
|
||||
outputs: { index: asList(inputs.list).findIndex((item) => sameValue(untag(item), wanted)) },
|
||||
outputs: {
|
||||
index: asList(inputs.list).findIndex((item) => !isEntity(item) && sameValue(untag(item), value)),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { untag } from '../../../model/Properties';
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asList, asNumber } from '../../values';
|
||||
import { asList, asNumber, isEntity } from '../../values';
|
||||
import { any, int, list } from '../shared';
|
||||
|
||||
export const ItemAt: NodeDef = {
|
||||
@@ -14,7 +14,11 @@ export const ItemAt: NodeDef = {
|
||||
run: (_ctx, inputs) => {
|
||||
const items = asList(inputs.list);
|
||||
const i = Math.floor(asNumber(inputs.index));
|
||||
return { outputs: { item: untag(items[i]) ?? 0 } };
|
||||
const item = items[i];
|
||||
// An object comes out as the object it is, not as its name: that is the point of a list of
|
||||
// objects, as `Raycast all` hands one out (#157).
|
||||
if (isEntity(item)) return { outputs: { item } };
|
||||
return { outputs: { item: untag(item) ?? 0 } };
|
||||
},
|
||||
params: [],
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { itemLike } from '../../../properties/listItems';
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asList, asListItem, asNumber } from '../../values';
|
||||
import { asList, asNumber, asValueItem, isEntity } from '../../values';
|
||||
import { any, int, list } from '../shared';
|
||||
|
||||
export const SetItem: NodeDef = {
|
||||
@@ -17,7 +17,10 @@ export const SetItem: NodeDef = {
|
||||
const next = [...items];
|
||||
// The slot keeps its kind: writing a number into a color item leaves a color behind, the same
|
||||
// way writing into a number variable leaves a number (see `asTypeOf`).
|
||||
next[i] = itemLike(items[i], asListItem(inputs.value));
|
||||
const value = asValueItem(inputs.value);
|
||||
const slot = items[i];
|
||||
// An object has no kind to keep and none to take on, so it is written as it is (#157).
|
||||
next[i] = isEntity(value) || isEntity(slot) ? value : itemLike(slot, value);
|
||||
return { outputs: { result: next } };
|
||||
},
|
||||
params: [],
|
||||
|
||||
Reference in new issue
Block a user