Let the agent edit animations, frames, tiles and tilemaps (#429)
feuerwerk / gate (push) Successful in 5m14s
feuerwerk / suite (push) Successful in 18m43s

After stage 5a the agent could build behaviors and scenes, but not
animate an object, cut a texture into frames or paint a tilemap.
Stage 5b adds three areas with 30 commands, 115 in all.

- animation (12): set the root, add, retarget and make rows relative,
  set, move, copy and delete keys, easing, insert or remove frames,
  and curve points.
- textures (8): add, change and delete frames, slice a grid, nine
  slice, sorting line and collision outline.
- tiles (10): the images of a tile, painting tilemaps, layers (add,
  copy, move, change) and the border of a placed tilemap.
- get_asset names the targets of an animation with their animatable
  properties. A tilemap up to 400 cells shows its cells, a bigger one
  only their count.
- After such a call the editor opens the tab of the animation, curve,
  texture, tile or tilemap. A frame edit selects the frame there.
- One paint step takes rectangles and single cells. Rectangles go
  first, so a cell can cut a hole into a rectangle. Cells outside the
  grid, ids that are no tile and more than 20 000 cells are refused.
  The grid is the tilemap's largest, a grown placement included.

Left out on purpose: Generate collision, because it reads the pixels
of the texture, and pasting keys, which goes through the clipboard.

Found on the way:
- Nine slice without fill now keeps the fill the image has. The
  model's default is repeat, not stretch as first described.
- Painting ran the other way round, rectangles overwrote cells.

Tests:
- agent-art (new, 11) and the other agent tests green, 64/64.
  5 bends of the code, all red.
- e2e 71 on atx: 23/23, the new check opens the agent's animation in
  its tab and reads its targets.
- Gate on atx: 321 files, 4969 tests.
- Live on a test project: a Spin animation of 360 degrees in 30
  frames matched 12 degrees per frame to 0.01 frames in the game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-18 16:24:27 +02:00
1 parent 0d8fd6ff52
commit d01e2b832e
10 files changed
+1110 -7

No files matched your search

+11 -2
View File
@@ -33,9 +33,12 @@ export interface Frame {
export type Args = Record<string, unknown>;
/** What a step worked on, for the editor to show the user: an asset, objects of a holder, nodes of a graph. */
/**
* What a step worked on, for the editor to show the user: an asset (in the inspector, or with `open`
* in its tab, there with one of its texture frames picked), objects of a holder, nodes of a graph.
*/
export type StepTarget =
| { kind: 'asset'; asset: string }
| { kind: 'asset'; asset: string; open?: true; frame?: string }
| { kind: 'objects'; in: string; ids: string[] }
| { kind: 'nodes'; asset: string; ids: string[] };
@@ -53,6 +56,11 @@ export interface CatalogCommand {
export const id = (description: string): ArgSchema => ({ type: 'string', minLength: 1, description });
export const number = (description: string): ArgSchema => ({ type: 'number', description });
export const whole = (description: string, minimum?: number): ArgSchema => ({
type: 'integer',
description,
...(minimum === undefined ? {} : { minimum }),
});
export const flag = (description: string): ArgSchema => ({ type: 'boolean', description });
export const ids = (description: string): ArgSchema => ({
type: 'array',
@@ -92,6 +100,7 @@ export const numberOf = (args: Args, key: string): number => args[key] as number
export const listOf = (args: Args, key: string): string[] => args[key] as string[];
export const theAsset = (args: Args): StepTarget => ({ kind: 'asset', asset: text(args, 'asset') });
export const theTab = (args: Args): StepTarget => ({ kind: 'asset', asset: text(args, 'asset'), open: true });
export const madeAsset = (_args: Args, made: Record<string, string>): StepTarget => ({
kind: 'asset',
asset: made.id!,