9e869df06341dfac93509f16c33aab99ddf4953a
120
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9e869df063 |
Free a redrawn picture only once no shader names it (#293)
Editing a vector drawing made Pixi warn "destroyed while still bound
to a shader", twice with every change, once it had begun. It began
where an object wears the drawing through a shader asset. Each
change draws the picture anew and destroyed the one before it, but
the mesh of that object keeps its picture bound until it syncs. The
issue also asks for the button "Add shape" to go: a shape without
points cannot be given any, the Pen draws a new one.
- GeneratedPictures takes two more callbacks. inUse says that a
picture cannot be freed yet, soon runs a retry once whoever asked
is done.
- A replaced picture that is still in use waits in a list of its
own. It is freed right after the redraw that asked, as a rule. On
a later ask where its wearer was not part of that redraw.
- TextureLibrary tells a bound picture by the listeners for change
on its source. Under WebGL the bind groups are the only ones that
listen there. The retry is a microtask.
- The rule is asked for every drawn picture alike, whoever wears it.
- The inspector of a drawing and of a shape has no "Add shape" any
more. "Delete shapes" for several selected shapes stays.
Decided here:
- Under the list of shapes a second note says "The Pen draws a new
shape." while only the asset is selected.
Found on the way:
- Reproduced on a throwaway copy of the project the report came
from: 6 warnings for 3 changes with the shader override on the
object, none without it. After the change none, and no replaced
picture is left waiting.
- The fixture project of the tests never showed the warning. No
object there wore a drawing through a shader.
Tests:
- Gate on melektaus-atx: 211 files, 3219 tests (3209 + 10), exit 0
- e2e 47,48 on melektaus-atx: 113 checks, all pass
- e2e 28,30,31,39,40,47,48: 350 checks, all pass, run before the
wording of the new note changed
- The first gate was red: an existing test read the wording of the
note that had changed. Only three test files had been run after
the change.
- No full run and no smokes. The sum of 1668 (1667 + 1) is reckoned,
1667 is what CI counted for
|
||
|
|
ec2fd9084f |
Say what a button without words does when the pointer rests (#209)
The top bar, the magnet, the eye and many marks of the inspector are
pictures without a caption. Whoever did not know one could only try
it. The toolkit had no hint on hover, which also forced the point
menu of #195 to be words. The answers in the issue settle the open
points: a cut tab name shows in full, shortcuts are part of the
text, the point menu stays as it is.
- Button takes a tooltip, a text or a function asked each time. It
shows on a small sign once the pointer has rested for half a
second.
- There is one sign for the page, a TooltipLayer on the overlay
above the menus. A control reports each pointer move and the
pointer leaving, the layer does the waiting.
- A press, a key, the wheel or a blur of the window takes the sign
away. initUI listens for them on the window.
- placeTooltip in ui/tooltip.ts is the rule for the place as a pure
function: under the control and flush left, flush right at the
right border, above the control at the bottom.
- toolbarTip says what the five buttons of the top bar do right now.
Undo and redo name their step from CommandStack.undoLabel and
redoLabel, as in "Undo Paint tiles (Ctrl+Z)".
- A SortableList reports its own marks with a key per mark: the eye,
the duplicate mark, the cross and the plus.
- The magnet, both fullscreen buttons, the eye of the scene tab, the
overflow of the tab strip, the plus of the assets panel, the marks
of the inspector rows and the nine anchors of the resize dialog
carry a text.
Decided here:
- Every button whose text is cut short shows the whole text, not
only a tab.
- A row action of the inspector names the one thing a click does. A
button that opens a menu says only Reset, Apply or Size from the
picture.
- A button just clicked stays quiet until the pointer has left it.
- A greyed out button shows no sign.
- The switches of the eye popup, the cross of a document tab and the
tools of pixel art have none. The tools carry words, other than
the issue assumes.
- The marks of a list say Show or Hide, Duplicate, Remove and Add,
whatever the list holds.
Found on the way:
- Signing out empties the overlay and would have destroyed a sign on
screen with it. The editor takes the tooltip away first. This has
no test of its own.
- The text hooks locate and has skip the sign, or it would answer
for the button under it.
Tests:
- Gate on melektaus-atx: 210 files, 3209 tests (3176 + 33), exit 0
- e2e 46 on melektaus-atx: 20 checks, all pass
- e2e 1-14 on melektaus-atx: 508 checks, all pass
- e2e 1-9,14,39,40,44-48: 685 pass, 2 fail, both in module 14. It
measures tabs that modules 10 to 13 open, and the chain 1-14 is
green. The cut chain was not run on the state before.
- No full run and no smokes. The sum of 1667 (1664 + 3) is reckoned,
1664 is what CI counted for
|
||
|
|
345289982b |
Scale, turn and mirror the shapes of a vector drawing (#291)
A shape could only be moved and reshaped point by point. Whoever
wanted it larger or turned pulled every point alone, and for several
shapes or a group there was no way at all. The answers in the issue
settle the rest: the change is baked into the points, nothing of it
is remembered, and the line width stays.
- With Select the selection stands in a frame of eight squares and a
knob, drawn like the one of the scene view. It holds one shape,
several shapes or a group, the same selection that is dragged as
one.
- A corner square scales from the opposite corner, a square on an
edge in its own direction. Shift keeps the proportions, Alt scales
about the middle.
- The knob turns round the middle of the frame, with Shift in steps
of 15 degrees. After the pull the frame stands upright again.
- The inspector has a band TRANSFORM with X, Y, Width, Height and
Rotate by, and the buttons Flip horizontal and Flip vertical.
- Points, handles, gradients and texture tiles are taken through the
same matrix. The rule is a pure function in vectorTransform.ts,
transformShapesCommand makes one undo step of any number of
shapes.
- A press goes to a point or a handle first, then to the frame, then
to a line. shapeFrame.ts holds where the handles are and what a
pull of each means.
Decided here:
- The frame stands 15 pixels off the shapes. The points of a shape
lie on its bounds, so a square on the bounds would cover them.
- The knob only turns. In the scene view it scales as well.
- A side cannot be pulled through the opposite one, the frame keeps
at least 1 pixel.
- Coordinates are rounded to a thousandth of a pixel, so a quarter
turn is exact.
- A texture tile stays upright under a turn and grows to the bounds
of the turned tile. The bands of a gradient stay square to its
axis under an uneven scale.
- The switch Scale stroke of the proposal is not built.
Found on the way:
- The inspector of a shape is now taller than the window. Two checks
of module 47 no longer found their row or button on the screen and
use the new hook inspectorButton, which scrolls there first.
- Not solved: with a shape at the top edge of the view the knob lies
outside the tab. The wiki names it under the limits.
Taken over from #217: the e2e check for two selected shapes without
a group, and unit tests for the snap of the common offset and for
the gradients of the shapes dragged along.
Tests:
- Gate on melektaus-atx: 209 files, 3176 tests (3121 + 55), exit 0
- e2e 47,48 on melektaus-atx: 112 checks, all pass
- No full run and no smokes. The sum of 1664 (1660 + 4) is reckoned,
1660 is what CI counted for
|
||
|
|
2ff3a78607 |
Put the shapes of a vector drawing on layers and in groups (#190)
A vector drawing was one flat list of shapes. With many shapes the list got hard to read, and several shapes could not be kept or moved together. Now the shapes stand on layers, as in pixel art, and can be put into groups that nest. - A drawing has layers, each with a name and an eye. A hidden layer is neither in the tab nor in the picture a sprite wears, and its shapes cannot be pressed. One layer is the one worked on: the pen and Add shape draw onto it, and SHAPES lists what is on it. - The inspector has a list LAYERS over the shapes. Its plus adds a layer, the cross on a row removes it with all on it, the last one stays. A double-click renames, a drag sorts. - Group (Ctrl+G) puts the selected shapes into a group, Ungroup (Ctrl+Shift+G) takes it away. A group stands over its rows in the list, the rows one step in, and a row is dragged into a group or out of it. - In the tab a press on a shape takes its outermost group, and a drag moves the group as one undo step. - The model has layers with a tree of shapes and groups instead of a flat shapes array. The rules are pure functions in core/model/vectorTree.ts. A drawing saved before opens with all its shapes on one layer. - SortableList takes rows with a depth and drops into a tree. The flat lists of tilemap and pixel art run as before. Decided here: - A group has no transformation of its own and cannot be hidden or locked. Rotating and scaling is the follow-up #291. - A group is never empty: the one that loses its last shape goes with it. - Several selected shapes are dragged together, grouped or not, and a click on a shape of the selection keeps the selection. This is what #217 asked for. - Shift+click selects like Ctrl+click. With Select, Shift pans only beside the shapes, as in the scene view. The middle button pans anywhere, and with the pen and Paint Shift pans as before. - A row of the list picks one shape of a group alone. - Shapes change their layer through a row Layer in the inspector, which is there from two layers on. Rows are not dragged between layers. - A group cannot span two layers, the button is off then. - The pen does not draw onto a hidden layer, the hint says so. - Groups are always shown unfolded. - The four order buttons work inside the own group or layer. Tests: - Gate: 204 files, 3067 tests (3008 + 59), exit 0 - e2e 47,48: 108 checks. e2e 39,40,47,48 before the Shift change: 209 checks. No full run, the sum of 1645 (1639 + 6) is reckoned and CI measures it. - 59 unit tests: 36 in tests/core/vector-tree.test.ts, 23 in tests/editor/vector-layers.test.ts - Six e2e checks with a real mouse in block 47j - 47 unit counter-probes for layers and groups and three for the Shift click all fail tests, run on stein2 - Three browser counter-probes fail their checks - Two screenshots looked at (group in the list, hidden layer) - Only unit tested: the row Layer, sorting and removing layers, copying a group, the Shift pan beside the shapes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
393caadc9d |
Head the groups of the inspector with bands that fold (#290)
The groups of the inspector had a small grey label as their title, in the same grid as a property row, so with many rows one group hardly stood apart from the next. Now every group has a band as its head, from edge to edge of the panel, and a click on it folds the group. - A band runs across the padding of its column in the header colour, with the title and a mark that says whether the group is open. The titles stay as they are, as answered in the issue. - A click on a band folds its group: the rows go, the band stays and the next band moves up against it. A control in the band (Open, + Variable, + Property) does its own job and folds nothing. - A placed object has a group SWITCHES under its name, asset and parent: Enabled, Keep on scene load and the switches of a behavior. An asset has SWITCHES and PROPERTIES, and a group without rows is left out. - The same bands head the tilemap panel, the Variables tab, the side of the Pixel art tab and the shape list of vector art. A list with a title (Layers, Frames, Shapes) folds with a click on its header, clear of its plus. - ui/sections.ts holds the rules as pure functions, SectionStack is the column and SectionHeader the band. Every title of the inspector goes through addTitle or sectionHeader in inspector/host.ts. Decided here: - A fold is kept for good, in localStorage of this browser. The issue asked whether for the session or for good and the answer left it open. - A fold is remembered by title, per panel. PROPERTIES folded once is folded on every object, asset and node. A number in a title does not count. - A behavior asset has all its rows under SWITCHES. A Variables asset and a state machine get a group SCOPE. - A greyed out control in a band takes the click too. Without that the click fell through to the band and folded the group. - The Curve tab has no column of this kind and is unchanged. Found on the way: - The inspector of a sprite is taller than the 800 pixel window of the checks now. The hooks that hand out a row scroll it into view first, and module 28 scrolls before it looks for a row by its text. Tests: - Gate: 202 files, 3008 tests (2982 + 26), exit 0 - e2e: no complete run for this state. Modules 28 to 61 passed alone (749 checks). A full run was cut off in the accounts module at 1492 passes without a failure, the modules after it and the smokes are missing. The sum of 1639 (1635 + 4) is reckoned, CI measures it. - 26 unit tests in tests/editor/sections.test.ts - Four e2e checks with a real mouse, three in module 02 and one in module 48 - 25 unit counter-probes all fail tests, run on stein2 - Screenshots of the bands looked at before the last build, none after it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c5ba21533d |
Pick a link on its line and delete it from there (#289)
A link could only be removed through the menu of its port, and the line itself took no input. A transition of a state machine could only be deleted in the list of the inspector. Now the line is the handle, in a behavior's graph and between states alike. - A click on a line selects that link and draws a halo behind it. A press counts within 6 screen pixels of the line, and where two cross the one drawn last wins. - Ctrl-click or Shift-click adds a link or takes it away. The rubber band takes every line it crosses, into the same selection as the nodes and boxes it caught. - Delete or Backspace removes what is picked as one undo step. A right-click on a line offers Hide link and Delete link. - The inspector shows a picked link: the node and port at each end and the type it carries, with Hide link and Delete link below. For several it says how many. - The arrow of a transition is picked the same way, on its name too. Delete or a right-click with Delete transition removes it. - The menu of a port is unchanged, Disconnect included, as answered in the issue. - editor/graph/edgeHit.ts holds the hit test as pure functions, and editor/inspector/edgeSection.ts builds the section. The selection has a new kind edge, and node and annotation carry edgeIds. Decided here: - A hidden link has no line and is never picked this way, also while it is shown on loan. Its mark at the port stays its handle. - The menu of a line speaks about the whole selection, like the menu of a box. With nodes or boxes held alongside it reads Delete N items. - Hiding picked links lets go of them, since no line is left. - A link wins over the box it crosses, and a mark wins over a link. - A transition has no Hide entry. - With nodes or boxes in the selection the inspector keeps showing the node or box, not the link. Tests: - Gate: 201 files, 2982 tests (2967 + 15), exit 0 - e2e, full run: 1635 checks passed (1629 + 6), exit 0 - Smokes: 24 / 19 / 21, exit 0 - The full run was done for exactly this state. - 15 unit tests in tests/editor/edge-selection.test.ts - Six e2e checks with a real mouse, block 6z-xi in module 06 and 26e7 in module 26 - 12 unit counter-probes all fail tests, run on stein2 - Three browser counter-probes fail, the build went through each time: no hit on the line, no halo, a band that skips lines - Screenshots of a picked link and a picked transition looked at before the last build, none after it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1aa824ecb4 |
Keep objects through a Load scene with a switch (#165)
A Load scene destroyed every object of the old scene. A player, a HUD or a music object had to be built again in each scene, with its state pushed through shared variables. A top-level object with the new switch Keep on scene load now travels into the next scene as the same entity, with everything below it. - The switch is a common property right under Enabled, off by default, on the asset and on a placed object. The inspector shows the row on top-level objects of a scene only, as answered in the issue. - runtime/keptObjects.ts holds the rules as pure functions. keptEntities picks what travels, objectsToBring copies the stored objects as the run parents them, and bringObjects puts them into the next scene. - World.instantiate takes a handed entity as it is instead of making one. The next syncToModel then writes the run's values into the scene. - BehaviorSystem.release takes the running instances out before the old world is destroyed, and the next system adopts them. They hear no On destroy and no second On start, and their sounds play on. - PhysicsSystem.resume gives a dynamic body the velocity and spin it had. - Duplicates: the object brought along wins. The next scene does not build its own object with the same id, which is the way back into the scene the object came from. - Decided here: the next scene does not build a top-level object of the same asset that is kept itself either. The issue only names the id. With the id alone a level that places its own player would hold two after a swap. - Decided here: kept objects land behind the scene's own, the switch is no animation row, and collisions are found anew. A finding, fixed along with it: Load scene did not build a scene as it is stored, but as the run had left it. A moved object stayed moved and a destroyed one stayed gone. The way back into a scene needs a clean build. - GameRun copies a scene's objects and groups before it first builds it and puts them back before it builds it again. - GameRun.end puts every such scene back. The player calls it on stop, so spawned objects no longer stay for "Click to play again". - The consequence: an edit made in a scene during Play is gone the next time that scene is loaded. Tests: - Gate: 199 files, 2944 tests (2929 + 15), exit 0 - e2e, full run: 1625 checks passed (1619 + 6), exit 0 - Smokes: 24 / 19 / 21, exit 0 - 15 unit tests in tests/core/keep-on-scene-load.test.ts - Six e2e checks in module 35: the switch is set with a real mouse, the child has no row, the object is in the new scene, its counter still reads 1, its sound plays on, and the stored scenes are unchanged after Stop - Eleven tests with fixed row lists only count the new schema row - 16 unit counter-probes all fail tests, run on stein2. One did not fail at first (same id not left out), it has its own test now. - The browser counter-probe fails 4 of the 6 new checks - Not looked at in the browser: the switch as a port of the Properties node, and a released game with a kept object Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c4a9280140 |
Preview inspector edits once per frame, not per input (#274)
Scrubbing a number in the inspector and dragging a colour stop of a gradient told the editor about every input. Pixi repeats each pointer move shortly after, so both gestures paid for two previews per move. The issue asked to measure first and to build only from 10 % per move. The measurement showed about 50 %. - previewingHandlers still writes the model on every input but requests the preview through a FramePreview of its own. The commit cancels a waiting preview first. - The preview handler of the GradientBar in shapeSection.ts does the same. - Wider than the issue describes: the row handlers also serve typing, the arrow keys and the colour picker. All of them preview once per frame now. Measured on stein2, fixture project with 120 extra objects, 120 moves 8 ms apart, three runs each: - Scrub: 236 project events before, 51 to 61 after. 45 to 50 ms per move before, 22 to 27 after. - Colour stop: 238 project events before, 50 to 51 after. 44 to 49 ms per move before, 22 after. - One preview still costs 13 to 17 ms. That price per frame is what remains. Finding, not fixed here: in the pixel art tab the palette colour and the opacity of a layer still preview on every input (PixelArtSide.ts), and so do the head fields of the curve tab. Neither was measured. Tests: - Gate: 198 files, 2907 tests (2903 + 4), exit 0 - e2e, full run: 1616 checks passed (1615 + 1), exit 0 - Smokes: 24 / 19 / 21, exit 0 - Four new unit tests in tests/editor/frame-preview.test.ts, two for a row and two for a colour stop - One new e2e check in module 02: a held scrub gave 10 project events in 12 frames, and the value is there before the release - Four unit counter-probes fail, 1 to 2 tests each. They ran on stein2, not in the container. - The browser counter-probe fails the new check with 50 events in 16 frames Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0f6e628616 |
Check the declared type when a graph writes a typed variable (#271)
A list variable with an item type only kept its shape in the editor. A graph could write any list into it at runtime, and the List node had no type at all. Now the declaration decides what a write may store. - Writing a list into a typed list variable leaves out every item that does not have exactly the fields of the type. The rest is written and the console names each item that was left out, like Add item does. - The same rule holds for typed list fields inside a struct variable and for lists inside typed items, at any depth. - A typed struct variable refuses a struct with a field the type does not have, and a value that is no struct. A struct with missing fields still merges in as before, so a partial Pack keeps working. - The check runs in the Variables node (own variables and Variables assets) and in the Properties node when it sets a behavior variable of another object. - Ports learn their declaration through GraphLookup: what a variable declares about its type, and which behavior owns a graph. The rule itself lives in the new src/core/graph/typedValues.ts, fitsType moved there from SetItem. - The List node gets a Type setting. With a type its items are shaped and marked, follow the struct asset, count when the type is deleted and offer Split into fields in For each. The mark never reaches the running game. - A type on a List node holding something other than structs is refused with a console line. - Setting the List node back to None removes the marks, unlike a list variable. A node list has no per-item type row, so marked items could never be freed there. Undo brings everything back. - tests/core/kinds.test.ts looked for the value as the first parameter of a literal. Type now comes first on the List node, so the test finds the value by its key. Tests: - Gate: 198 files, 2869 tests (2856 + 13), CHECK EXIT 0 - e2e 55: 19 checks (+55j write at runtime, +2 for 55k List node) - e2e 54: 16 checks, smokes 24 / 19 / 21 - Five mutations, each fails between 1 and 4 tests - e2e total 1606 is computed (1603 + 3), no full run Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ded71a92c9 |
Pick the target of an object variable in the Scene View (#276)
Choosing the target of an object variable meant finding its name in a dropdown. With many objects, or several with the same sprite, the name alone does not say which one it is. The eyedropper lets you click the object where you see it instead. - Every object dropdown gets an eyedropper button next to it. This covers variables, struct fields and list entries, in the Inspector and in the Variables tab. - The button opens the Scene View or the container tab the object lives in. A strip above the view names the field and the current candidate and offers Take and Cancel. The cursor is a crosshair. - A click picks a candidate and writes nothing yet. The candidate gets its own cyan frame, the selection stays on the object with the variable. - Clicking follows the selection rule. `pickOn` in `src/viewport/pick.ts` uses `pressOn` and only counts objects the dropdown offers. A click on empty space keeps the candidate. - Enter or Take writes through the same handlers as the dropdown. That is one undo step, or none if nothing changes. - Esc, Cancel, a right click in the view or a second click on the button cancel without writing. So do a tab switch, another selection, undo, redo, play and loading a project. - After Take or Cancel the editor goes back to the tab the pick came from. - While the eyedropper runs, the view does not drag, marquee, paint, show handles or open its context menu. Panning and zooming stay. Found on the way: - Started from the Variables tab, the click missed the object. The Scene View is hidden while that tab is open, and the layout skips hidden children. The strip appeared then, the view moved down by 32 px but kept its old height. `Editor.display()` now lays the row out again after showing it. Tests (on melektaus-atx): - unit pick and object-pick: 27 passed, 13 new - gate: 196 files, 2844 tests, CHECK EXIT 0. Two more come from svg-icons, which checks every icon twice. - e2e 57: ALL PASSED, 12 checks, 5 new (57e to 57g) - Counter-checks work. Without the layout fix 57g fails. Picking objects the dropdown does not offer fails 2 tests. Writing on cancel fails 2 more. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
14972d057f |
Refactoring round 8: hide data files, safe saves, Esc as Cancel (#85)
Recurring cleanup round over the code since round 7 (
|
||
|
|
bc688f3efc |
Refactoring round 7: split BehaviorSystem, fewer copies (#85)
Recurring cleanup round over the code since round 6 (
|
||
|
|
89aa92d2e3 |
Put a shader on a text and draw pixel fonts sharp (#169)
A text could not wear a shader, and a pixel font was always smoothed. Pixi's Text takes no custom shader. It also shares one texture between texts of the same content and style, and the filter is not part of that key, so two equal texts with different filters would overwrite each other. - TextPicture rasters a text into a texture of its own and draws it like a sprite. Shader, time animation and edge mode work without a special path. A new text, font or size rasters again and keeps the shader. - The old texture is dropped only after the shader holds the new one. Before, Pixi warned about a destroyed texture that was still bound. - A text has a Shader row with the shader's property rows below, as a sprite has. Overrides, saving, animation and the Properties node (type Text) know them. A shader parameter named like an own row of the text loses to that row, as on the particle source. - The font asset has a Filter row, Linear or Nearest. The row existed three times already (texture, vector art, pixel art) and is now one helper filterRow. - Nearest rasters the text 8 times finer and cuts every 8x8 block to one texel: at least half covered is full, else empty. Chrome on Windows smooths canvas text sideways through DirectWrite whatever filter we set later. The cut removes those half pixels on every system, and the texture stays one texel per world unit. Found on the way: - On a font that is no pixel font thin strokes break apart under Nearest. For a pixel font at its own size this does not matter. Not done: - The preview in the Shader tab does not take a selected text as its picture, it shows the test picture then. Tests: - ./check.sh: 189 files, 2759 tests, CHECK EXIT 0. - text-shader.test.ts with 10 tests: rows and name precedence, a clamped override, save and load, animation, the font filter, and sharpCoverage on a smoothed edge and at the texture border. - e2e 30 has 7 new checks: red glyphs through the shader picked with the mouse, the shader survives a new text, red in the game, soft edges under Linear, clean 4x4 blocks under Nearest, and half covered texels (Linear 238, Nearest 0). - Full local run before the Nearest cut: all passed, 1543 checks. GM_STEPS=1,30 on the final bundle: all passed, 32 checks. The sum 1544 in the README is counted from these two runs, not measured. - Counter-checks fail as expected: in the browser no shader on a text fails 30h-30j, an always linear sampler fails the Nearest check, the old 1:1 raster leaves 107 half covered texels and fails 30k-ii. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d7d0a6e7d4 |
Let Tab reach the game in the Game tab (#261)
The game did get Tab, but the editor moved the keyboard focus to a button at the same time. The next Space then pressed that button instead of reaching the game. The key field of On key down could not capture Tab at all, so an inventory on Tab could not be built. - FocusManager asks tabBelongsElsewhere before it moves the focus. The editor answers yes while the Game tab is in front, so Tab stays with the game as long as no field has the focus. - A widget can claim Tab through takesTab. The key capture button does so while it is capturing, so On key down accepts Tab. - The harness knows the key Tab. Found on the way: - The first version of check 20g left the Behavior tab in front, and module 21 then failed in the chained run. 20g now goes back. Tests: - ./check.sh: 2746 tests, CHECK EXIT 0. - e2e 20 has two new checks: Tab reaches the game and leaves the keyboard with it, the key field captures Tab. Both failed against the old bundle. - GM_STEPS=19,20: all passed, 44 checks. The full local run with all three topics of this push passed with 1543 checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
f84defb9b6 |
Pick an object of the same scene for an Object variable (#258)
An Object variable could only be set by a graph while the game runs.
A placed object always showed "(null)", although the other objects of
its scene are there to choose from and the runtime can already find
any object by its id. Now the placed object stores a reference to one
of them, and the running game turns it into the real object.
- Stored as {"$object": "<id>"}. The empty placeholder stays as it
was, so old projects load unchanged and empty still means "(null)".
- The row of an Object variable on a placed object is a dropdown with
None and every object of the scene, indented like the Hierarchy.
The same goes for Object fields in structs and Object items in
lists, also in the Variables and List tabs.
- The behavior asset keeps "(null)". An asset has no scene, so "Apply
to asset" is gone from Object rows.
- The runtime resolves the reference when the Variables or Properties
node reads it. A destroyed target reads as null, a missing one shows
"(missing)" in the row.
- Inside a container only objects of the same container are offered.
The reference keeps the template id, so every placement finds its
own copy.
- Deleting the target clears the reference in the same undo step.
Paste points references inside the pasted group at the copies and
leaves outside references alone. Create container and Unpack carry
references along with the new ids.
- A value a graph writes while running stays in the run. The scene
keeps the picked object, also in "Run in editor".
Found on the way:
- Setting an override coerced every object slot to empty, in plain
values, struct fields and list items. The shared objectPlace() now
keeps the reference.
Not done:
- No eyedropper to pick in the scene, the dropdown is enough for now.
- Deleting a template object in the container editor does not clean up
references in other scenes. They read as null there.
Tests:
- ./check.sh: 187 files, 2724 tests, CHECK EXIT 0
- e2e 1-9, 51, 57: ALL PASSED, 389 checks
- e2e 1, 57 on the final bundle: ALL PASSED, 17 checks
- 13 unit tests in object-references.test.ts, 5 checks in e2e 57
- Counter-checks: 8 unit breaks fail their tests, the browser break
without a pick list fails 57b-57d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
e8b3ac0a3b |
Keep behavior variables private to their own graph (#257)
A behavior often needs working state such as a timer or a counter. Until now every variable showed up on each placed object and in the Properties menu, so the inspector filled up with values nobody should touch. A private variable works like any other inside its graph but is not offered anywhere outside. - Each behavior variable gets a lock switch next to Keep. It starts off, so existing projects stay as they are (`privateVariables`). - A placed object shows no private variable, neither in the inspector nor while the game runs. If all are private, it says so. - Switching a variable to private drops its overrides on every placed object in one undo step. Switching back does not restore them. - A running game does not write a private value back as an override, and a project change during the run does not reset it. - `+ Property` no longer offers private variables. A port that names one by hand still reads and writes it without a warning. - The default value of a private variable is read-only in the asset inspector. Its shape (name, fields, type, delete) stays editable. - Keep is now a pin icon instead of a word. With the lock added, the header of a list variable had no room left and the lock covered `+ Item`. Not done: the animation track picker still offers private variables. Tests: ./check.sh -> 186 files, 2711 tests, CHECK EXIT 0. e2e 1,19,20,21,56 -> ALL PASSED, 119 checks. New module 56 has 7 checks with a real mouse. Counter-checks fail as expected: unit without the write-back filter, the sync guard, dropping overrides or loading the flag. Browser with all variables on the object, an unfiltered menu, or no value lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b0d0868f55 |
Give a list one struct type so every item keeps its shape (#254)
A list of structs used to let every item carry its own fields. Adding an item meant typing the same fields again, and nothing kept the items in step with the struct asset from #253. A list can now name a struct type for its items, and the asset keeps them in shape. - A list variable and a list field get the row Items with Mixed, every struct asset and Make struct type. Make struct type collects every field any item has, so no item loses a field. - A typed item carries its type under the key `$struct` (ITEM_TYPE_KEY). listItems does not count it as a field. - A list that holds anything but structs refuses a type, also when the offending item sits in an override. The console names the list and the count, and the list stays as it was. - In a typed list `+ Item` adds an item with the defaults, without the kind menu. Field names are locked, values stay editable. - The rows of a typed item take their kind from the struct asset (declaredKind in InlineList.ts). An asset field shows the asset picker instead of a text field, as reported in the issue. - A struct item in a mixed list gets its own Type row. Switching a list back to Mixed keeps the type on each item. - syncStructTypes brings marked items into shape and carries renames into them, also inside overrides and nested lists. One undo step. - The delete dialog of a struct asset also counts typed lists. - The game never sees the key. World and sharedState strip it with withoutItemTypes and bagWithoutItemTypes, so a struct stays a plain object at runtime. - Add item and Set item get the setting Type. With a type they only accept a struct with exactly its field names (fitsType in SetItem.ts). Anything else is left out and logged. - The item indent from #252 is back to its old width, the narrow one squeezed the value fields. Not done: writing a variable does not check the type, and the List node offers no type. Found on the way: a test helper in list-of-structs.test.ts called nodes without params. It now passes the same arguments as the interpreter. Tests: - ./check.sh: 183 files, 2679 tests, CHECK EXIT 0 - e2e GM_STEPS=1,21,22,32,54,55: ALL PASSED, 150 checks - e2e GM_STEPS=1-6,21,22,32,54,55 before the follow-up: 404 checks - New: tests/core/typed-lists.test.ts, e2e module 55 with 13 checks - Counter-checks bite in unit and browser, originals restored by cmp Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
61bbb96a96 |
Define a struct once as an asset and reuse its shape (#253)
A struct used to declare its fields wherever it stood. The same shape in two variables meant typing it twice, and Pack/Expand copied it once without staying in sync. A Struct asset now holds the shape in one place, and every struct that picks it as its type follows it. - New asset type "Struct" (not placeable, group Structs, own icon). The inspector shows its fields with name, kind and default value, and "Open" shows them in the variables tab. - Struct variables and struct fields get a Type row: own fields, any Struct asset, or "Make struct type". With a type the fields come from the asset and cannot be added, renamed or removed there. The values stay per variable, overrides on placed objects still work. - Pack and Expand get the setting Type. With a type their ports are the fields of the asset and read-only in the inspector. - A change to the asset reaches every typed place in one undo step (syncStructTypes). A new field arrives with its default. A removed field goes along with its port, edge and typed value. A renamed field keeps its value in variables, overrides and on ports with edges, also through nested types. - Undo for these edits records plain assignments (Recorder), because the reach of a change is not known in advance. - Cycles such as Loot in Loot, also through another type, are refused and not offered in the picker. - Deleting the asset names how many places use it. Afterwards each place keeps its fields as its own. - Loading reshapes a stale copy in a project file to its type. - "Make struct type" creates an asset from an existing struct and links the place to it. Differs from the plan: - A changed default value applies to new fields and newly typed places only. Existing values stay. - Removing a single field shows no warning. Undo brings it back. Typed lists are left for #254. Lists stay dynamic for now. Found by e2e: after deleting or renaming the type the inspector did not rebuild and kept showing the old name. Fixed, the check was red before. Tests: - ./check.sh: 182 files, 2661 tests, CHECK EXIT 0 - GM_STEPS=1,4,21,22,54: ALL PASSED, 176 checks - 14 unit tests in tests/core/struct-types.test.ts - New e2e module 54 with 14 checks, real mouse. Module 04 counts 23 asset types. - Counter-probes: without the rename pass 4 unit tests fail, without reshaping values 8, without the cycle rule 1. In the browser, without spreading changes and with + Field despite a type, 3 checks fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4857bb75e6 |
Show lists in the inspector and variables in their own tab (#252)
The List tab could show one list at a time and was the only place to see its items. A list now shows its items right where it lives, and a new Variables tab shows all variables of an asset or placed object. - InlineList draws the items of a list under its row in the inspector, in struct fields and in the Variables tab. A list inside a list or a struct field is drawn the same way, one step indented. - The index column is the indent. Items sit under their list without an own margin, and the index column is 16 px wide instead of 28 px. - Above 20 items (INLINE_LIMIT) the rest collapses into "... N more - open in tab". During a run the rows show the values from the game. - ListHead puts "N items" and "+ Item" in one row. "N items" opens the list in a tab. A separate Open button squeezed the counter to zero width in the narrow inspector column. - VariablesEditor shows every variable of a behavior asset, a variables asset or a placed behavior. On an asset it edits the declarations. On a placed object an edit only overrides that object, with the usual reset and push buttons. - Opening a list inside a tab adds a new tab and keeps the open one. valuesKey gives each list its own document, so a list that is already open is shown instead of opened twice. - Titles read "Pickup · Variables" or "Listy · nums · [3]". The tab closes when its asset or object is deleted. - Lists on node inputs stay inline only, a node owns no variables. - The List tab, ListEditor and the __gm.list hook are gone. Tests: - ./check.sh: 181 files, 2645 tests, CHECK EXIT 0 - GM_STEPS=1,21,22,32: ALL PASSED, 123 checks - e2e 21 is rewritten for inline lists and the new tab, 22 and 32 are adapted. Sum in README: 1486 (+6 in 21, -1 in 22), counted by hand. - Counter-probes fail as expected: sub-lists not told apart (1 unit test), sub-list replacing the open tab, all items drawn inline (60 rows instead of 20), no live values (0 rows at 24 items in a run). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
393b8ec7f9 |
Build your own easing curve and use it on keys and Lerp (#247)
The built-in eases cover the usual shapes, but a game often wants its own: a coin that bounces twice, a door that overshoots and settles. A new asset type holds such a curve, and keys and the Lerp node can pick it instead of a built-in ease. - New asset type "curve": points like in Vector art, x is t and y is the value. A new curve is the straight line from (0, 0) to (1, 1). - One pure evaluation in src/core/model/curve.ts serves the timeline preview, the running game and the Lerp node. - The curve always has one value per t. The end points stay at t 0 and 1, points cannot pass each other, and a handle that reaches past its neighbour is shortened along its direction. Loading a hand-edited file applies the same rules. - Curve tab: click on the line to insert a point that keeps the shape, click on free space to put one there, drag points and handles, double-click or Delete removes, Alt-click toggles round and sharp. Every step is one undo step. - Presets: all 30 built-in eases plus Linear, fitted with adaptive subdivision. Each stays within 0.4 % of the built-in ease. - Preview: a dot runs along the curve once a second, a second dot shows only the value. The Preview button stops it. - Animation keys get "Curve" under Easing with a curve picker. Lerp gets "Curve" under Ease, and its result may overshoot b. - A missing or unpicked curve runs linear. The inspector says so, the node shows it as a problem, and deleting a used curve warns with the number of keys and Lerp nodes. Tests: - ./check.sh: 180 files, 2612 tests, CHECK EXIT 0 - GM_STEPS=1-6,25,53: ALL PASSED, 309 checks - New e2e module 53 with 13 checks, all with the real mouse. Expected values come from its own Bezier evaluation. - Counter-checks bite: without Bezier evaluation 4 unit tests fail, without handle clamping 2. In the browser, a key that cannot find its curve, a click that sets no point and a handle drag that moves the point all turn checks red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
89b95aa8ac |
Move "Delete row" from the inspector into the timeline menu
The context menu of the timeline is where the other row and key actions live. A separate button in the inspector was easy to miss and sat apart from them. - The timeline menu gets "Delete row" as a danger entry after a separator. It deletes the row under the pointer, whether the click hits the row header or a cell. Below the last row or on the ruler it is disabled. - A right-click on a row header now also selects that row, like a right-click on a key selects the key. A right-click in an empty cell keeps the selection so chosen keys stay available for Copy. - The "Delete row" button in the "Timeline row" inspector is gone. "Delete keys" for a multi-key selection stays. Tests: - ./check.sh: 172 files, 2498 tests, CHECK EXIT 0 - e2e 25: ALL PASSED, 30 checks. New check in 25g deletes a row via a real right-click and restores it with all three keys via Undo. - No counter-probe. No check covers the right-click into a cell. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0c5ac32b59 |
Let structs and lists nest to any depth (#239)
Structs and lists were capped at two fixed levels (list of structs, struct with a list field). Anything deeper was dropped silently on load, in node settings and when written to a variable. The cap came from an early design choice, not from a technical need, so it is lifted completely instead of adding a third fixed level. - One kind list NESTED_KINDS replaces ListItemKind and StructFieldKind. The flat special cases in listItems.ts are gone, every value goes through one recursive path. - A struct inside a struct is declared, like a top-level struct. Values coming in over an edge are brought into that shape. Struct items in lists still take their fields from the value. - New valuePath.ts addresses any nested spot by keys and indices. ListSlot.field is now ListSlot.path. - Field commands (add, rename, remove) take the path to the struct. They change declaration, own value and all overrides in one undo step, as before on the top level. - Inspector: "+ Field" offers Struct and List on every level. A nested struct is a header row with its own "+ Field", its fields are indented one step further. - List tab: a list item shows "n items" and opens one level deeper. The title shows the path, e.g. "Listy · nums · [3]". - Pack in Pack now writes a struct into a struct field instead of text. Objects are kept at any depth and stripped at every exit. - A list edit in a struct field without an existing object override now starts from the shown value instead of an empty struct. - Old projects load unchanged. Save, load and save again gives the same result. Tests: - ./check.sh: 172 files, 2498 tests, CHECK EXIT 0 - e2e 21 + 22: ALL PASSED, 90 checks (new 21d-iii, 22f-ii, 21d and 22f updated) - 20 new unit tests in nested-values.test.ts and nested-list-slot.test.ts, three tests that pinned the old cap turned around - 6 counter-probes, each fails 1 to 8 tests, originals restored and checked with cmp - Not covered: a nested struct inside a struct item in the list tab has no e2e click Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7979b8ff19 |
Let structs and lists hold objects across behavior runs (#238)
An object could only live in a dedicated object variable, kept in slots
beside the variable bag. Struct fields and list items could not hold
one, so a spawned object packed into a struct or list was lost. This
replaces the slots with one mechanism for all three places.
- "No object" has one resting form everywhere: {"$object": ""}. A
running game can tell an object slot from an empty text that way.
- During play the object handle lives directly in the value, also
nested in structs and lists. objectSlots.ts and the special paths for
Keep and Properties are gone.
- liveObjects.ts strips objects to (null) at every exit: overrides the
editor run writes back to the scene, saved values of persistent
Variables assets, live values in the inspector, and the world
snapshot of the test hooks (objects as names there).
- A destroyed object reads as (null) through isAlive, alone, in a
struct field or as a list item.
- "+ Field" and "+ Item" offer Object. The inspector and the list tab
show a fixed (null) row without input.
- Lists from Raycast all stay objects when written to a variable.
Before they turned into names.
- A struct that only changes by an object writes no override. A list
that grows by an object writes [(null)], its length really changed.
Found on the way: CI at
|
||
|
|
98211a8d54 |
Let a variable hold an object across behavior runs
A spawned object was only reachable in the run that spawned it. The Spawn output could not go into a variable because "object" was a port-only kind, so the next run had no way back to the instance. - "Object" is now a variable kind. The reference lives for the run only. The file stores the declaration and an empty value. - The live reference sits in slots beside the variable bag (graph/objectSlots.ts), not in it. The bag is the behavior's props, and syncToModel would have written an object into the scene. - A destroyed object, or one from a restarted or left scene, reads as nothing. A dead reference is dropped from its slot. - Keep (#229) hands the slots of kept variables on to the next call of a sub-behavior. - Properties of kind Behavior reads and writes another behavior's object variable through the same slots. - The inspector shows "(null)" for it and offers no input. - A persistent Variables asset with an object variable shows a warning under "Persistent". Saved values never keep the object, every run starts it at (null). Objects inside lists and struct fields are left out on purpose. Emptiness is checked with Equals against an empty b. A node "Is valid" does not exist yet. The test helper graphEditor().variables() now also reads the kinds of a Variables asset a node points to. Tests: - ./check.sh: 169 files, 2463 tests, CHECK EXIT 0 - e2e 6 + 23: ALL PASSED, 146 checks (new 23k, 2 checks) - e2e 32: ALL PASSED, 18 checks (new 32f-ii) - 10 unit tests in tests/core/object-variable.test.ts, one of them runs a persistent asset over two runs - 5 counter-probes (value in bag, isAlive always true, no Keep hand-over, Properties without slots, object in saved values) each fail their test - e2e check total 1406, computed only Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8b44f97c35 |
Let a child sort with its parent so its Z stays local (#232)
An animated or set Z of -1 on a weapon put it behind everything on Z 0, not just behind the player holding it. A child can now opt in to being drawn at its parent's place, so its own Z only orders it against the parent and its siblings. - New property "Sort with parent" on sprites, texts and particle emitters, shown in the inspector right under Z. It is a normal property, set on the asset and overridable per object. - With it on, the rest of the scene sees only the parent's Z and, where the layer sorts by Y, the parent's feet. Below 0 the child is drawn behind the parent, from 0 on in front. - It works across levels: a grandchild with the switch hangs on the top object of the chain. - It has no effect under a parent that is not drawn itself (container, behavior, camera, collider) or under a tilemap. - New assets start with it on, saved ones read as off, so old projects draw unchanged. For that a schema row now has "initial" next to "default". The gold "changed" mark and reset follow "initial", so a fresh sprite is not marked as changed. No load migration is needed. - parentGroups() in drawOrder.ts gives a slot only to the top of a chain and inserts the children there. Editor, play mode and picking all use it, so they agree. Tests: - 10 new unit tests in tests/viewport/sort-with-parent.test.ts. - 8 existing tests list the sprite rows or defaults and got the row. - New e2e check 5b-ii clicks the switch and checks order and undo. - Counter-checks, all originals restored by cmp: switch ignored fails 7, children not sorted by Z fails 6, no chain fails 1, "initial" ignored fails 9, row missing in the inspector fails e2e 5. - ./check.sh: 166 files, 2426 tests, CHECK EXIT 0. - e2e 5: ALL PASSED, 13 checks. Check total 1398, computed only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
147f8ffbea |
Let animation keys ease with Sine to Bounce in, out or in-out
A key could only ease quadratically, which makes quick moves like a sword swing feel soft. Animators know the Penner curves from CSS, GSAP and Unity, so a key now offers all of them. - Ten curves (Sine, Quad, Cubic, Quart, Quint, Expo, Circ, Back, Elastic, Bounce), each in, out and in-out, next to Linear and Step. - The inspector shows two fields instead of one long list: Easing, and Direction only when a curve is picked. Switching the curve keeps the direction. Coming from Linear or Step starts at in-out. - Old projects keep working. ease-in/out/in-out now read as Quad and are still saved under that name. New curves save as cubic-out, back-in-out and so on. - Back and Elastic overshoot on purpose. A number property with min or max is clamped in play and preview, so a Back in from scale 0 does not flip the sprite. trackWriteAt now takes the property definition, and animatedKind* is renamed to animatedDef*. - The timeline marker still shows only the direction. - In-out is built for every curve from its in-curve and its mirror. easings.net uses own constants for Back and Elastic in-out, so our Back in-out winds up about 5 % instead of 10 %. Tests: - ./check.sh: 165 files, 2413 tests, CHECK EXIT 0. - 12 new unit tests in tests/core/easing-curves.test.ts: round trip of all 30 values, start/end/midpoint of every curve, reference values, only Back and Elastic leave 0..1, clamping in play and preview, unknown values are rejected. - e2e 25: ALL PASSED, 28 checks. New 25b-ii (3 checks) clicks both fields and undoes the four choices. Check sum 1393 -> 1396. - Counter-checks, each fails its test: no clamping (2), Quad saved as quad-* (2), mirrored in-out wrong (2), curve lookup with `in` instead of Object.hasOwn (1), no Direction field (e2e 25). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
454d202109 |
Let each variable of a behavior choose whether calls keep it (#229)
The first version of #229 put a Keep state switch on the Run behavior node. The user wanted it where the variable is declared instead. Whether a value lives on belongs to the variable, not to every call site. - The node switch keepState is gone again. RunBehavior, CallResult and nested.ts are back to what they were before #229. - BehaviorAsset.keptVariables lists the kept names. It lives on the asset and not in PropertyDef, because that is the schema of every inspector row. - A variable added from now on starts with Keep on. Variables in existing projects start with Keep off, so old projects run unchanged. - SetVariableKeptCommand flips the switch as one undo step. Add, remove and rename carry the list along. Loading drops names that are not declared. - The interpreter keeps only the listed variables, per Run behavior node and per object as before. The others start at their defaults. - Local state machines in a called behavior start empty on every call again, as before #229. Global and scene machines are unchanged. - The inspector shows a Keep button in each variable row of a behavior. Its state is part of the asset signature, so a click redraws the row. Without that the button kept its old look until the inspector was reopened. Found: the project Garden of Sacrifices still has keepState = true on the call Player -> Character Movement. It is ignored now. Direction there predates the switch, so Keep is off until set by hand. Tests: - ./check.sh: 163 files, 2392 tests, CHECK EXIT 0 (before 2388) - tests/core/keep-state.test.ts rewritten, 15 tests - e2e 19: ALL PASSED, 35 checks. 19c-ii now clicks the Keep button of a new variable: on when added, off by a click, on again, off after undo. - Counter-checks, each fails its own test: keep list ignored (2), states kept (1), new variable not kept (8), rename drops the list (1), no Keep button (e2e 19c-ii). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
89048413f1 |
Let a texture frame set where a sprite stands for Y sorting (#228)
A sprite sorted by Y stood on the bottom of its box. With a shadow or empty rows under the feet that is too low, so a figure slipped in front of a tree it should stand behind. The line belongs to the frame, not the texture, because one texture can hold several sprite sheets. - New field `sortLine` on a frame: whole image pixels up from the frame's bottom edge, 0 to the frame height. 0 is stored as nothing, so old projects stay the same. Shrinking a frame keeps the line inside it. - `feetOf` in drawOrder.ts lifts a sprite by the same share of its box. Scene view, game view, play and picking all use it. In an animation the line of the frame shown right now counts. - The texture tab draws the selected frame's line dashed in yellow with a knob left of the frame. At 0 the line shares the bottom edge with the nine-slice border and a resize handle, so the knob is tested first. A line inside the frame can be dragged directly, tested after the nine-slice lines. - The inspector has a new "Sorting" section with a "Sort line" field. Dragging or typing is one undo step each. Limit: frames of one walk cycle need the same value, set by hand. Otherwise the figure changes its place in the order while walking. Tests: - ./check.sh: 160 files, 2356 tests, CHECK EXIT 0 (before 2350). - 6 new unit tests in tests/viewport/sort-by-y.test.ts. - e2e 37 and 39-43: ALL PASSED, 162 checks. New check 37b-ii drags the knob up 6 px and undoes it, plus the inspector row. - Counter checks: if feetOf ignores the line, exactly the 3 order tests fail. If the knob cannot be grabbed, 37b-ii fails. - The e2e sum in the README is 1380, counted, not yet seen in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ffb5bbe039 |
Stop a key getting stuck when Shift changes it while held (#227)
Input remembered keys under KeyboardEvent.key. Pressing a, adding Shift and letting go sent "A" on release, which matched nothing, so "a" stayed down until the next release without Shift. - New rule keyName in src/core/keys.ts lowercases a single character. So a and A are the same key, also under Caps Lock. - Input remembers per physical key (e.code) under which name it went down and releases exactly that name. So 1 released as ! is freed too, which lowercasing alone does not cover. - On key down, up and held, Key is down and Axis input compare with the same rule. A node that already stores A reacts to a. - Recording a key in the inspector stores Shift+A as a. - Limit: holding Shift before pressing 1 still sends !, and a node on 1 does not react. Nothing gets stuck by it. Tests: - 3 new unit tests in tests/core/behavior-events.test.ts: the flow from the issue, 1 released as !, and a node on A hearing a. - Without lowercasing exactly the issue test and the A test fail. - Without the code memory exactly the 1/! test fails. - ./check.sh: 159 files, 2340 tests, CHECK EXIT 0 (before 2337). - No e2e change, the check sum stays at 1377. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d444b27fae |
Let behaviors read and set tilemap cells while the game runs (#135)
A behavior could move sprites around but had no way to look at or
change the tilemap it was standing on. Doors, breakable blocks and
"is there ground ahead?" checks all need that.
- New nodes Tile at, Set tile, World to cell and Cell to world. The
conversion honours position, rotation, scale and mirroring of the
placed map. A small Tile literal node lets a graph compare what it
read against a fixed tile.
- The layer is picked by name from all tilemap layers in the project
through a new field kind. "First layer" takes the map's first one.
Because layer is an input, a text can also be wired in.
- Each placement keeps only its edited cells in liveTilemap.ts,
layered over the asset's cells. A second placement of the same map
stays untouched, and the asset itself is never written.
- A read sees a Set tile from earlier in the same flow. Drawing picks
up the edit at once and neighbouring autotile edges adapt.
- Solids see the edits straight away, so a Move in the same frame
already hits the new cell. Rapier rebuilds its shapes on the next
step.
- After Stop the map is back to what was painted. The editor run shows
the edit in the scene view while it lasts and does not touch the
project.
- An unwired tilemap input means no map, like Get position. Cells
outside the grid are refused and done reports false.
Tests:
- ./check.sh: 155 files, 2271 tests, CHECK EXIT 0 (before 2245).
- tests/core/tile-access.test.ts (14), one physics test for a floor
set at runtime, catalog entries for the five nodes.
- e2e 39,40: all passed, 86 checks (before 81), five new in 40s.
- Counter-checks: collision ignoring edits fails 3 unit tests, the
solids cache missing same-tick edits fails only the "at once" test,
drawing ignoring edits fails the 2 drawing checks, the editor run
not reporting a new tile fails only its check.
- 40s uses a still tile. The animated fixture tile redraws every frame
and hid the last counter-check at first.
- Check total 1351 + 5 = 1356, computed. CI confirmed 1351 at
|
||
|
|
2cf72022eb |
Ping an asset from its inspector link instead of selecting it (#221)
A single click on an asset link used to select the asset. That swapped
the inspector over to the asset, so the object being edited was gone
and the second click of a double-click landed on a different row. A
click now pings the asset in the asset panel, like Unity does in its
Project window, and a double-click opens it.
- state.pingAsset(id) emits a new `ping` event. It runs no command and
leaves the selection alone, like openAsset.
- ListView.ping(id) draws an accent overlay on the row. It stays full
for 200 ms and is gone after 1 s, driven by onFrame. destroy cancels
the animation.
- The asset panel expands the group, scrolls to the row and pings it.
- The capture listener and followedLink in the inspector are gone.
The inspector is no longer rebuilt, so pressCount on the label is
enough. followsPress and pressOf are no longer exported from ui/.
- The "Asset" and "Part of" rows of a scene object were a ghost button
that selected the asset. They are now a link label with the same
ping and open behaviour, next to a read-only field with the name.
- The link logic lives in linkLabel() in fieldFactory.ts. createFieldRow
and the asset row share it.
- TextField gets a readOnly option: no focus, no caret, no hover.
- New hook __gm.assetPing(). inspectorLink also reports the field
next to the label (value, readOnly).
Findings:
- Renaming an asset while its object was selected left the old name
in the inspector. This was already the case with the old button.
The refresh now updates the name.
Tests:
- ./check.sh: 151 files, 2228 tests, CHECK EXIT 0 (before 150 / 2226).
New: tests/ui/list-ping.test.ts for pingAlpha and pingAsset.
- e2e 1-16,28 on the final build: ALL PASSED, 575 checks.
28l-ii has 9 checks instead of 5.
- Counter-checks, each failing exactly at its check: ping also selects,
no follow-up frame (alpha stays 0.55), inspector never opens, field
takes focus, name not refreshed, label not a link.
- Check total 1345, calculated. CI last counted 1336 at
|
||
|
|
1aae97b335 |
Open a sprite's texture or shader from the menus and inspector (#221)
A sprite's texture and shader were only reachable by hunting for them
in the asset panel. Every place that shows such a reference now leads
straight to it.
- Object menu (hierarchy and scene view): "Open texture" and "Open
shader" sit right below "Open asset" when the sprite has one. The
object's own values count, so an overridden shader opens the
override.
- Asset context menu: the same entries at the top.
- Inspector: every asset field with a value shows its label as a blue,
underlined link. One click selects the asset, a double click opens
it. An override stays gold and gets the underline too.
- The first click rebuilds the inspector, so the second one lands on a
different row. The inspector catches that second click before any
row sees it.
- New pure rule `references.ts`: the asset behind a reference field. A
frame leads to its sheet. Kinds without a tab (palette, font, sound)
get no menu entry.
- `followsPress` in `clicks.ts` keeps the double-click window and
tolerance in one place.
- `activateAsset` in `actions.ts`: opening means the same everywhere
as a double click in the asset panel, so a scene also becomes the
active one. The asset panel uses it too.
Tests:
- ./check.sh: 150 files, 2226 tests, CHECK EXIT 0 (before 148/2214).
- 12 new unit tests: 6 for the reference rule, 4 for the link in
createFieldRow, 2 for followsPress.
- 5 new e2e checks in 28l-ii: both menus, the link's look, click
selects, double click opens.
- e2e 1-16,26,28,35,39,40 on the final build: ALL PASSED, 732 checks.
- Counter-probes: no "Open ..." entries, no underline and a second
click never recognised each fail exactly at their checks.
- Check total 1341 (CI counted 1336 at
|
||
|
|
0e8585665a |
Load the 500 weight and draw missing glyphs as SVG icons (#214)
The theme asks for Source Sans 3 at weight 500, but fonts.ts only
loaded 400, 600 and 700. The browser synthesised the 500 text. The
issue comment also asked for the symbols the font lacks to become SVG
icons. A scan of src/ against the font's cmap found 20 such characters
in 30 places.
- fonts.ts loads source-sans-3-latin-500-normal.woff2 as well.
- Menus draw their ticks and the "current" dot as icons. The dot is a
new MenuItem.current field and no longer part of the label text.
- Inspector action buttons (reset, apply, fit width/height) are
icons. The menu arrow behind them is the drawn Dropdown chevron.
- Kind marks come from a table in markIcons.ts. Picture kinds use an
icon, # and Aa stay text because the font has them.
- Scene marks in the asset panel, sound play/stop and the transition
arrows are icons too.
- Apply is an icon although the font has the arrow. It sits next to
the reset icon and would look different as a glyph.
- A new Icon widget draws on redraw like Button, so Node tests need no
DOMParser.
- Tests find icons through the SVG id. The new hook
__gm.locateIcons(id) and the harness helpers findIcon/clickIcon
replace locate() on the old characters.
Findings:
- Weight 700 is loaded but the theme never uses it. Left as is.
- The old scene-mark check in module 35 accepted any play glyph on
screen. It now checks the mark on the start scene's row.
- Module 44 needs the textures from module 37 and fails without it.
Tests:
- ./check.sh: 145 files, 2187 tests, exit 0 (before 144 / 2143).
- New tests/ui/fonts.test.ts: every theme weight is loaded, and every
character in src/ exists in every shipped face. The cmap reader
matches fontTools (231 characters).
- Icon tests cover the 19 new files. Each kind mark is either an
icon or text, never both.
- e2e 1-16,22,30,34,35,43,44: 586 green, 0 FAIL, then 44 stopped
for lack of module 37. e2e 1-16,37,44,47,48: 618 green.
e2e 1-14 on the final build: 454 green.
- Two new e2e checks: loaded weights in 14, a reset icon pixel in 04.
- Counter-probes all fail as expected: 500 removed, a play glyph put
back, the struct icon removed (one unit test each). In the browser,
500 removed and reset icon hidden fail exactly the two new checks.
- Editor-suite check sum 1312 (1310 measured by CI at
|
||
|
|
d7c0b7cfdb |
Variables and state machines can reset with every scene (#164)
A shared variable or machine used to live either per object or for the
whole run. Levels need a third lifetime: shared like global, but built
fresh from the declarations whenever a scene is loaded.
- New scope `scene` for Variables assets and state machines. A missing
field keeps the old meaning: `global` for assets, `local` for
machines, so existing projects do not change.
- Returning to a scene that was visited before is fresh as well. There
is no memory per scene.
- A `scene` machine announces its start state again after a scene swap,
so `On state entered` in the new scene hears it. A global machine
stays quiet because it never left its state.
- The rule lives in two pure functions in `sharedState.ts`
(`carriedForward`, `restartedAssets`). BehaviorSystem only calls them.
- The inspector shows a Scope dropdown on Variables assets and a third
step on the machine dropdown, with a note on what `scene` means.
Decisions:
- `scene` and `persistent` exclude each other (variant A). The choice
dialog was cancelled, so this is an assumption to revisit. A
scene-scoped asset hides the Persistent switch, and switching to
`scene` drops the flag (undo restores it). The guard sits in
`isPersistent` itself, so no path can save values the scope throws
away anyway.
Findings:
- `clickText('Scene')` hits the Scene tab, not the menu entry. One e2e
check was red because of that, and its partner was falsely green
because it compared `undefined` with `undefined`. Both use `clickLast`
now.
Tests:
- ./check.sh: 144 files, 2142 tests, CHECK EXIT 0 (before 143 / 2133)
- 9 new unit tests in tests/core/scene-scope.test.ts
- e2e 32 fresh: ALL PASSED, 17 checks
- e2e 1-3,17,18,26,32,35 fresh: ALL PASSED, 213 checks
- 3 new e2e checks (35: scene-scoped asset resets on swap, 32h: dropdown,
note, hidden Persistent switch)
- Counter-checks bite: ignoring `restarted` fails 2 unit tests and the
new check in 35, the old announcement rule fails 1, dropping the scope
guard in `isPersistent` fails 1. Originals restored, cmp IDENTICAL.
- Check sum 1307 (1304 + 3) is computed. Last CI measurement: 1293.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
41772e53f7 |
Variables assets can keep their values from one run to the next (#166)
A game needs a high score or an unlocked level to survive the end of
a run. As agreed in the issue there are no save or load nodes. A
Variables asset gets a switch instead, and the existing Variables
nodes read and write the values as before.
- A "Persistent" checkbox in the Variables inspector. Toggling it is
one undo step, and absent means not persistent.
- The behavior system loads the saved values at start and writes them
after every frame that changed one, so a closed tab loses nothing.
- Values live in localStorage per project, not in the project file.
Another browser or person starts fresh.
- Play and "Run in editor" save apart, so trying a behavior in the
editor leaves the Play save alone.
- Saved values are read through the current declaration. A removed
variable drops out, a new one starts from its default, and a changed
kind is converted.
- "Clear saved values" in the inspector clears this asset in both
areas. The main menu gets "Clear saved data" for the whole project.
Clearing takes effect from the next run on.
Decisions:
- The switch covers the whole asset, not single variables. Mixing
both means two assets.
- A changed default in the inspector shows only after the save is
cleared. That is what persistent means.
Findings:
- The first e2e run died at "debug hook present" right after a Vulkan
error from Chrome. The second run passed, so it was a GPU flake at
start-up.
Tests:
- ./check.sh: 140 files, 2070 tests, exit 0 (before 138 / 2055).
- 15 new unit tests in tests/core/saved-values.test.ts and
tests/editor/saves.test.ts.
- GM_STEPS=32,46 fresh: ALL PASSED, 24 checks. Six new checks in
module 32 go through the UI. Module 46 knows the new menu entry.
- Counter-checks both fail as expected. Loading switched off: 2 unit
tests and 2 e2e checks fail. Editor run writing into the Play area:
1 e2e check fails.
- Check total 1278 (1272 measured by CI at
|
||
|
|
bbbf34109b |
Lists duplicate, rename and edit values right in their rows
Name, opacity and Z each sat in a separate field below or beside
the list. You had to select a row first and then look elsewhere to
change it. Duplicating was a separate button or not possible at all.
Now the row itself carries everything.
- SortableList gets three optional callbacks. onDuplicate shows a
copy button between the eye and the delete button. onRename opens
a text field on double click, Enter or a click outside commits,
Esc cancels, an empty name is ignored. value puts a NumberField
into the row.
- Dragging on the number changes the value and never moves the row.
A whole drag is one undo step, the canvas follows live.
- Copy and delete only show on the hovered and the selected row.
Their space stays reserved, so nothing jumps on hover.
- Pixel layers: copy right above with the same pixels on all
frames, rename, opacity in percent. The Name and Opacity fields
below the list are gone.
- Pixel frames: copy right after, replaces the Duplicate button.
- Tilemap layers: copy with cells, Z and visibility, rename, Z in
the row. The Name and Z fields in the panel are gone.
- Vector shapes: copy at the same place and select it, rename.
- New in core: DuplicateVectorShapeCommand,
DuplicateTilemapLayerCommand, withLayerDuplicated and copyName
("X copy", "X copy 2"). copyName lives in src/core/util/names.ts
because the model never imports from commands/.
Findings:
- Double clicking a shape that is not selected yet only selects it.
The first click moves the list up, so the second click misses the
name. Module 47 selects the row first.
Tests:
- ./check.sh: 138 files, 2055 tests, CHECK EXIT 0 (before 2052).
- GM_STEPS=47,48 (fresh, twice): ALL PASSED, 91 checks (before 86).
- GM_STEPS=39,40,47,48: module 40 green, 47 died on the rename
check, which was a test fault and is fixed.
- Counter probes: a constant copyName fails the shape and pixel
layer tests. A dead copy hit fails the copy check in 47.
- Check total 1272 in README and wiki, computed (1266 + 6).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
ae0247d86f |
Lists add and delete items right in the list header and rows
The layer, frame and shape lists still had their add and delete buttons scattered below the list, each in its own layout. They now live in the shared SortableList, so every list works the same way. - SortableList gets an optional header with the title on the left and a + on the right, plus an optional x per row next to the eye. - The keepOne option hides the x on the last remaining row, so a pixel art image and a tilemap always keep one layer. - Pixel art layers and frames and tilemap layers use header, + and x. Only Merge down and Duplicate stay below the lists. - Vector art shapes get the x but no +. Add shape stays below the list and Delete shapes stays for a multi-selection. - The section headings in the pixel art column use the same style as the list titles and the inspector. Findings: - The wait loop added to check 47i for #196 only waited for the right half of the shape. If the texture landed between the two reads, the left half was still blue and the check failed. It now waits for both halves. Tests: - ./check.sh: 138 files, 2052 tests, exit 0. - GM_STEPS=39,40,47,48 (fresh): ALL PASSED, 156 checks (was 154). - GM_STEPS=47,48 (fresh, final): ALL PASSED, 86 checks. - Two new e2e checks: x on a shape (47) and x on a frame (48). The tilemap check in 40 now uses the x instead of Delete layer. - Counter-check: with the x not reporting to its owner, exactly the two new checks fail. - Check total 1266 in README and wiki is computed (1264 + 2). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
592ea942d9 |
Vector shapes can fill with gradients and textures (#186)
A vector shape could only be filled and outlined with one flat colour. Shading, glows and patterned surfaces had to be faked with stacks of shapes. Every shape now has a paint type for its fill and its outline. - Fill type: solid colour, linear gradient, radial gradient or texture. Line type offers the same without texture. - Colour stops are edited on a bar in the inspector, as agreed in the issue. A click on the bar adds a stop in the colour the gradient already has there, so adding never changes the picture. At least two stops remain. - The new Paint tool in the tab shows two handles for the selected shape: axis start and end, centre and radius, or the corners of one texture tile. "Handles: Fill / Outline" picks which paint is edited. - Moving a whole shape moves its gradients and tile with it. Every gesture is one undo step. - A texture fill always tiles, because Pixi turns a non-repeating pattern into a repeating one anyway. When switched on, one tile covers the whole shape. - Only uploaded textures and frames can be a fill, never a drawing. A drawing filled with itself would rasterise itself mid-raster. - The fingerprint of a drawing includes the texture and frame a fill points to, so the picture redraws when the image is swapped. Findings: - graphics.destroy(true) also destroyed the textures of the fills. With a texture fill it deleted the tile slice and the next redraw crashed on reading 'scaleMode' of null. It is now destroy(). - The gradient cache destroyed old gradients itself, which gave 20 Pixi warnings "destroyed while still bound". They are now handed to Pixi's texture GC with autoGarbageCollect switched on. The console of module 47 is empty again, the last CI log had 0 of them. Tests: - ./check.sh: 135 files, 2011 tests, exit 0 (before 134 / 1986) - npm run build: exit 0 - GM_STEPS=47, freshly seeded: ALL PASSED, 54 checks (before 44), 0 console warnings - 25 new unit tests, 10 new e2e checks in block 47i - Four counter-checks bite, originals restored and verified with cmp - Check sum 1232 in README and wiki, calculated (1222 + 10) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1116818011 |
Vector shapes can be combined by union, subtract and more (#187)
Building a shape out of simpler ones meant drawing the result point by point. The Vector Art tab now selects several shapes at once and combines them with a boolean operation into one ordinary shape. - Ctrl/Cmd+click in the tab and Ctrl or Shift+click in the shape list add a shape to the selection. Shift stays pan in the canvas. - A COMBINE block in the inspector offers Union, Subtract, Intersect and Exclude. Subtract removes every upper shape from the lowest one. - The result replaces the lowest selected shape and keeps its name, colours, stroke and place in the order. It is one undo step. - Curves are flattened to 0.2 canvas pixels, a circle gets about 48 points. Open lines and single shapes cannot be combined, the buttons are off and a hint gives the reason. An empty result is rejected. - Delete now removes every selected shape, and the selection drops shapes that no longer exist. - The geometry comes from clipper2-ts 2.0.1, pinned. It has no dependencies and speaks even-odd directly. Only the engine module is imported, the bundle grows by 55 kB instead of 110 kB. Findings: - clipper2-js was tried and dropped. It pulls in Angular 15 with a high npm audit finding and no fix. - Pixi finds holes by containment, not by even-odd depth. An island inside a hole would be drawn as a hole again. Result rings are emitted outer ring before its islands, and a test mirrors Pixi's rule. - A depth sort turned out to be dead code in a counter-check and was removed. A pixel check in 47h stayed green without the subtract and now checks both colours. Tests: - ./check.sh: 134 files, 1986 tests, exit 0 (before 133 / 1968) - npm run build: exit 0 - GM_STEPS=47, freshly seeded: ALL PASSED, 44 checks (before 39) - 18 new unit tests, 5 new e2e checks in block 47h - Five counter-checks bite, originals restored and verified with cmp - Check sum 1222 in README and wiki, calculated (1217 + 5) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
78595ec80f |
A selected vector shape has its own inspector rows (#129)
Stage 4 of #129. Until now the inspector showed the rows of the asset when a shape was selected, so there was no way to give a shape a colour, an outline or a place in the drawing order without editing the project file. The rows for a shape now sit where every other selection puts its rows. - SHAPES lists every shape of the asset, topmost row drawn last, the same way a layer list reads. Selecting the asset itself shows the same list below its own rows, with no shape selected. - Each row paints its shape with the commands the rasterizer uses (shapeDrawing plus paintDrawing) into a 22 px swatch, the whole canvas scaled in, so the row also says where on the canvas the shape sits. - The four order buttons from the issue (forward, to front, backward, to back) sit in two rows of two, and a row can be dragged to another place with a marker line. They are disabled when there is nothing to move. - VECTOR SHAPE carries name, fill, opacity and an outline checkbox, the checkbox adding and removing the stroke instead of hiding it; line colour and width appear with it. A dragged colour slider is one undo step, the style rows going through previewingHandlers like every other row. - A line width of 0 is no stroke, which the command rejects, so the row clamps at 0.1. Findings: - locate('Outline') aims beside the control, because it gives the centre of the label text and a wider label pushes a fixed offset out of the field. __gm.inspectorField(label) knows the centre of the control. - A click into a text field sets the cursor and selects nothing, so a typed hex landed appended to the old one and parseHex returned null without anything failing - typeText only checks that the text is contained. A Ctrl+A goes first now. - Four buttons side by side cut "Backward" to "Backw...", which only the screenshot showed. Two rows of two now, as in the tilemap panel. Tests: ./check.sh 128 files, 1897 tests (was 127/1883). Full e2e suite locally instead of only CI because the change sits in the inspector and touches every selection: ALL PASSED - 1179 checks (was 1172), module 47 at 24 checks (was 17). Three counter-checks hold: not reversing the list fails 1 unit test and 2 e2e checks, dropping the gap correction when dropping a row fails 2 unit tests and the drag check, and a constant signature kills module 47 because the outline rows never appear. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3d7ad65f34 |
Vector art has a tab to draw in, which is stage 3 of #129
Stages 1 and 2 gave the asset a model and a picture, but the only way to put a shape into it was a test calling mutate. The tab closes that gap: what you draw there is what the sprite in the scene carries, with no extra step, because the fingerprint changes and the picture registry of #185 redraws. - The tab has a header with Select, Pen, Grid and Fit, a chequerboard behind the canvas and the canvas itself as a frame. It paints with the same commands as the rasterizer, paintDrawing being exported from viewport/vectorArtPicture.ts for it, so the tab cannot drift away from the picture. - Pen sets points on a click and pulls the handles out on a drag, both of them mirrored; a click on the first point closes the ring and Esc ends an open line. - Select drags a point, a handle or a whole shape, a click on an edge puts a point there, a double click takes one away, and Alt turns a corner round and back. Delete removes the selected shape. - The rules grew inside polygonEdit.ts instead of a second module, as the plan says, so a collider polygon and a frame outline get the same behaviour where it applies. - A point on a curved edge splits the curve by de Casteljau instead of cutting it: both halves together draw exactly what was there before. - A curved edge is walked instead of measured over its chord, or a click on the bow of a curve would grab nothing; the same walk carries the test for the inside of a ring. - One gesture is one undo step. The preview writes the model directly and the old state is put back on release before the command runs, which is the pattern the frame outlines of #141 already use. - state.ts gained a selection kind "shape". The inspector shows the rows of the asset for it, the shape list with colour, opacity and stroke being stage 4. One counter-check was no counter-check: disabling the pen's closing tolerance by multiplying it with 0 left the run green, and rightly so, because the closing click lands on exactly the screen coordinate point 0 came from, so the distance is 0 and <= 0 is true. Only an unsatisfiable condition really switched it off, and then 2 checks fail. A second trap on the way: a false && in front of the guard breaks the TS narrowing, npm run build bails out, and two runs went against the old bundle. Tests: check.sh 127 files, 1878 tests, EXIT 0 (was 126 / 1860). Full e2e suite ALL PASSED - 1172 checks, module 47 alone 17 (was 10). 18 new unit tests in tests/editor/ring-edit.test.ts and 7 new e2e checks in blocks 47c and 47d. Two counter-checks hold: no de Casteljau fails 1 unit test, measuring a curve over its chord fails another. Originals checked back with cmp and rebuilt. |
||
|
|
4f55f85ad4 |
Collision layers and masks: a collider carries a layer and a mask of the layers it meets, and the pair only meets when both masks name the other's layer - the rule is one pure module (src/core/model/collisionLayers.ts) with layerBit, layersMeet and the names a project starts with, so the three places that have to agree read the same function: the editor run through CollisionSystem, the Solids sweep per pair and Rapier, where the mask becomes the collider's interaction groups; the project file gained collisionLayers: ["Default"] (default one layer, no formatVersion bump), the collider schema two new kinds - layer as a dropdown of the project's layers and layerMask as a menu of tickboxes that stays open while it is ticked (Menu.ts learned rows that do not close on a pick) - and the names are edited through "Collision layers..." in the toolbar's Project menu, not a Project tab, which is the decision I made myself because the question ran into the MCP timeout: the dialog (src/editor/CollisionLayersDialog.ts) holds up to 16 names, Add layer appends "Layer <n>" and Apply writes all of them as one undo step (SetCollisionLayersCommand); a mask read from a file goes through a check that refuses anything that is not a finite number, because Number(null) is 0 and a collidesWith: null would silently have meant "meets nothing"; 9 new e2e checks in module 8 cover the menu entry, the dialog, Add layer, Apply and its undo, both inspector rows, the tickbox menu, and the mover walking straight through the crate that 8c blocked on once their layers no longer meet, and 21 new unit tests cover the rule, the schema and the command (#152)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8e4bb6e1f9 |
User management: signing in is required before anything is loaded, the sign-in screen is drawn in the same canvas (user name, password as bullets, Enter moves on and submits) and the editor is built only after it; the API gained auth/session, auth/login and auth/logout while every other route answers 401 without a session, projects carry an owner and a foreign one comes back as 404, accounts live in data/users.json with a password hash and sessions as files in data/sessions with a sliding 30 days, so a build under the open tab does not sign anybody out; Logout sits at the far right of the toolbar and, as asked in the issue, does not reload the page: Editor.dispose() takes the editor down while the canvas stays, which is why the autosave, the focus handlers, the window listeners of the tilemap panel, the shortcuts and the plugins all got a way back; the tools sign in as e2e (tools/lib/auth.mjs, password from GAME_MAKER_E2E_PASSWORD or the git-ignored data/e2e-password.txt) and the browser gets the same session by cookie, new e2e module 45 covers the screen, the wrong pair, the bullets, logout without a reload and signing in again, new unit tests for HttpAuthStore and for what a session leaves behind (#177)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
010b6a7f6c |
A state can transition to itself: make transition and a second click on the same state connects a self transition, taking it fires On state exited and then On state entered every time, for local and global machines, while Set state into the current state stays silent as before; only states may connect to themselves, every other node is still refused, one self transition per state; the loop is drawn over the middle of the top edge with its arrow pointing down and its name at the apex, the preview already shows it while armed, and the inspector lists it once under Transitions out; new test hook transitionLabelAt, e2e 26e5 (#163)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5385d9e152 |
The one-way tolerance angle is a field: a one-way collider and a one-way tile gain One-way angle (0 to 90 degrees, default 45), a contact blocks when its normal is within that angle of the blocking side, without a value the limit is bit-identical to before, and the rule holds for Move, Raycast and dynamic bodies in Rapier; tiles with different angles never merge into one rectangle, the scene view draws the angle as a cone in the middle of the thick side, and the inspector shows Blocks from and One-way angle only while One-way is on, through the new schema option shownWith (#159)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4728f4fab5 |
A placed tilemap grows and shrinks at every edge: the new GRID EDGES block in the inspector adds or removes one column or row at Left, Top, Right or Bottom as one undo step, shifting the cells of all layers and moving the placement through its own matrix so the content stays in place on screen; removed painted cells are kept outside the grid and return when the edge grows again, and the Columns and Rows fields of a placement keep the content in place too (#136)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c2fd536aaf |
The texture field only offers pictures: each texture with its frames, no animations any more; an animation id left in an older project shows as a missing reference (#143)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5046ddb810 |
The tilemap painting tools move out of the inspector into their own panel beside the scene view, shown only while exactly one placed tilemap is selected in the scene tab; the free viewports keep their content in place on screen when they are resized (#142)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f53279d065 |
The active tilemap layer can be renamed from the painting section, and a Show setting fades or hides the other layers in the scene view while the tilemap is selected (#138)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
bfbcbe604c |
The tile list in the tilemap section is a palette of pictures: each tile shows its lone tile (the four convex corners for floor and wall, the picture itself for none) with its name below, laid out with the image-picker grid (#139)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |