Add regression tests for prop attachment, centering, and editor UX

- Implemented test_phase9a_prop_attach.gd to validate anchor schema, alignment math, and preview isolation.
- Implemented test_phase9b_prop_centering.gd to check canonical origin presentation, bounds, centering, and save threshold.
- Implemented test_phase9c_prop_editor_ux.gd to assess the user experience in the prop editor, including anchor renaming and UI responsiveness.
- Added corresponding .uid files for each test.
- Created sync_sources.py script to manage markdown file synchronization with the notebook.
- Added .gitignore for virtual environment files.
This commit is contained in:
2026-09-21 16:48:24 -04:00
parent e3689c86c8
commit 00abacc7b2
30 changed files with 4903 additions and 55 deletions
+597
View File
@@ -0,0 +1,597 @@
# Prop Editor User Guide
The Prop Editor is a standalone tool for making vector props and saving them as
`.prop` files. It is separate from the main Stickman Editor and Sandbox Stage.
This guide describes the current controls and provides a complete manual test
checklist.
## Start the editor
1. Open `res://scenes/prop_editor.tscn` in the Godot editor.
2. Press **F6** to run the current scene.
The Prop Editor is not the project's configured main scene. If F6 runs a
different scene, open `prop_editor.tscn` first.
## Layout
The window is divided into these areas:
| Area | Purpose |
| --- | --- |
| `📁 File` and `👁️ View` | File operations and canvas view reset |
| `Grid`, `Snap`, and `🎯 Center Prop` | Shared grid, snapping, and prop placement settings |
| `Prop Name` | The name stored in the `.prop` file |
| Mode bar | Switch between `🖐️ Arrange`, `✏️ Reshape`, and `📌 Attach` |
| Authoring canvas | Draw, select, transform, and reshape the prop |
| Inspector | Material, shape, primitive, and anchor controls |
| Holding Preview | A private stickman-and-prop preview shown in Attach mode |
| Status bar | Current prop/preset/dirty state and cursor coordinates |
There are two adjustable splitters:
- Drag the main splitter between the canvas and Inspector to make the Inspector
wider or narrower.
- In Attach mode, drag the splitter between the authoring canvas and Holding
Preview in either direction. The preview keeps a minimum width so its
controls remain usable.
The Inspector starts at its minimum usable width so the canvas has room to
work. Drag the canvas/Inspector splitter to widen it when labels or controls
need more space. Its background is the theme color `#262C39`, intentionally
darker than the canvas; the Inspector also scrolls vertically on smaller
windows.
The Inspector scrolls vertically. On a small window, widen the Inspector or
scroll it rather than expecting every section to fit at once.
## File and View menus
### `📁 File`
| Command | What it does |
| --- | --- |
| `🆕 New` | Clears the current prop and starts a new document. If the current document is dirty, asks whether to save first. |
| `💾 Save` | Saves to the current path. If the prop has never been saved, opens Save As. |
| `📄 Save As...` | Opens the save dialog and changes the current path to the new file. |
| `📂 Load...` | Opens a `.prop` file. A successful load replaces the current document. |
| `🕑 Recent` | Shows up to five most-recently saved or loaded prop paths. Missing files are removed from this list. |
Save dialogs use the `.prop` filter. Save As normalizes a filename so it ends
in `.prop` even if that extension was omitted. Keeping authored props under
`res://props/` is recommended.
### `👁️ View`
| Command | What it does |
| --- | --- |
| `🔍 Reset View` | Resets the authoring canvas zoom and pan to 1x, with the local origin centered in view. It does not reset anchor values or the Holding Preview camera. |
The Holding Preview has its own `Reset View` button; see [Holding Preview](#holding-preview).
### Top-right canvas controls
| Control | What it does |
| --- | --- |
| `Grid` | Sets the visible grid spacing and the snap increment, from 1 to 512. |
| `Snap` | Enables or disables snapping for shape transforms and anchor movement. |
| `🎯 Center Prop` | Moves the complete prop so the combined shape bounds are centered on the fixed local origin. It also translates every non-origin anchor by the same amount and focuses the canvas on the origin. |
Center Prop is a deliberate one-time command. The editor does not recenter
automatically after drawing, reshaping, or moving a shape. Empty and already
centered props are unchanged.
## Prop name and modes
### Prop Name
Type a name in the `Prop Name` field. The name is saved in the file and marks
the document dirty as soon as it changes. An empty name is displayed as
`Unnamed Prop` in the window title/status, and as `Untitled` in the generated
window title when no name is present.
### `🖐️ Arrange`
Arrange is the mode for selecting, moving, rotating, scaling, mirroring, and
ordering complete shapes. The Add Shape inspector and the shape Erase button
are available in this mode.
When a shape is selected, the canvas draws:
- a two-tone selection outline and bounding box;
- a small rotation dot below the bounding-box center;
- four corner scale crosses.
Drag the shape body to translate it. Drag the rotation dot to rotate it. Hold
**Ctrl** while rotating to snap to 15-degree increments. Drag a corner cross
to scale. Hold **Ctrl** while scaling to keep the aspect ratio.
Clicking a shape selects the frontmost shape under the cursor. With the canvas
selected (rather than a text field), **Delete** or **Backspace** also removes
the selected shape.
### `✏️ Reshape`
Reshape is for editing individual vertices. It does not move the whole shape
and does not show the Add Shape section or Erase Shape button. When the canvas
is empty, it does not show the Arrange-only “Right-click to add a shape” hint.
- Original vertices are filled circles.
- User-created vertices are hollow rectangles.
- Drag a vertex to reshape the outline.
- A vertex drag, insertion, or removal marks the shape as reshaped.
- Releasing a vertex drag sanitizes polygon/line points (including grid snap
when enabled, collinear cleanup, and winding cleanup).
- A reshaped circle hides its Radius control and shows `✏️ This shape was
reshaped`. Choosing a new radius regenerates a clean circle and clears that
reshaped state.
### `📌 Attach`
Attach is for authoring named anchors and checking a selected grip on the
private stickman Holding Preview. It does not add gameplay attachment or
physics behavior to Sandbox Stage.
Attach mode shows the Attach toolbar and the Anchors inspector. Shape editing
controls are hidden, but the prop remains visible on the authoring canvas.
## Grid, Snap, and canvas navigation
The top-right `Grid:` SpinBox accepts values from 1 to 512. It controls the
drawn grid spacing and the snap increment. The `Snap` checkbox enables or
disables snapping for canvas transforms and anchor movement.
In Attach mode, the top `Snap` checkbox also controls anchor movement. There is
no separate anchor snapping control. The grid is still available while
snapping is off; it is only a visual guide in that state.
The authoring canvas supports these gestures in every mode:
| Gesture | Result |
| --- | --- |
| Mouse wheel up/down | Cursor-centered zoom in/out, clamped to 0.3x–3.0x |
| Pinch/magnify gesture | Cursor-centered zoom |
| Middle-mouse drag | Pan the canvas |
| Two-finger pan gesture | Pan the canvas |
| Left-click | Mode-dependent selection or editing |
| Right-click | Mode-dependent context menu |
The bottom-right status text shows `X` and `Y` world coordinates while the
cursor is over the authoring canvas. It shows dashes elsewhere.
## Authoring canvas context menus
### Arrange right-click menu
Right-clicking a shape selects the frontmost shape under the cursor and shows:
| Action | Result |
| --- | --- |
| `Copy` | Copies the selected shape into the editor clipboard. |
| `Paste` | Appears when the clipboard contains a shape; inserts a copy offset from the cursor. |
| `Erase` | Removes the selected shape. |
| `Flip Left-Right` | Mirrors the shape around its own bounding-box center horizontally. |
| `Flip Up-Down` | Mirrors the shape around its own bounding-box center vertically. |
| `Bring Forward` | Moves the shape one step toward the front of the drawing stack. |
| `Send Back` | Moves the shape one step toward the back of the drawing stack. |
Right-clicking empty canvas shows:
`Add Box`, `Add Plank`, `Add Triangle`, `Add Line`, and `Add Circle`.
`Paste` is also available there when the clipboard is populated. New shapes
are placed at the clicked world position and become selected.
### Reshape right-click menu
Reshape intentionally has a smaller menu:
| Where you right-click | Result |
| --- | --- |
| A vertex | `Remove Point` |
| Near an outline segment | `Create Point`; the new point is inserted at the cursor's projection onto that segment and flashes briefly in yellow |
| Inside a closed shape | Selects the shape; no menu is shown |
| Empty canvas | Nothing; shapes cannot be added from this mode |
Removing a point from a shape with fewer than two remaining vertices removes
the shape. A two-point polygon becomes an open line.
### Attach right-click behavior
Right-click does not open a shape menu in Attach mode. Select and edit anchors
with left-click, the Attach inspector, or the anchor presets.
## Material and shape Inspector
The Inspector is scrollable and changes with the active mode and selection.
### Material
The Material section is hidden in Attach mode to keep the anchor controls and
Holding Preview readable. Return to Arrange or Reshape to edit the material.
The material preset dropdown contains `None`, `Wood`, `Rubber`, `Cardboard`,
and `Metal`. The preset chooses the default colors used when a new primitive
is added and displays read-only physics information:
- `Mass`
- `Friction`
- `Bounce`
These readouts come from the prop physics tables. They are informational in
the Prop Editor; changing the dropdown does not turn the authoring canvas into
a physics simulation. Save after changing the preset so the new material is
stored in the `.prop` file. The current implementation updates the preset and
readouts immediately; a preset-only change may not show `• Modified` until
another dirty-tracked edit is made.
### Selected Shape
This section appears when a shape is selected in Arrange or Reshape mode.
| Control | What it does |
| --- | --- |
| `Fill` color picker | Changes the selected shape's fill color live. |
| `Outline` color picker | Changes the selected shape's outline color live. |
| `Width` SpinBox | Changes outline width. |
| `Radius` SpinBox | Changes a clean circle's radius and regenerates its points. |
| `Vertices` | Read-only count of the selected shape's points. |
| `Erase Shape` | Deletes the selected shape. It is hidden in Reshape mode. |
The Radius row is only shown for a circle that has not been reshaped. Editing
circle vertices or scaling it non-uniformly changes it into a freeform shape;
the muted reshaped note explains why Radius is unavailable.
### Add Shape
The Add Shape section is available in Arrange mode. Choose a primitive from
the dropdown, adjust its parameters, then press `Add a Shape`.
| Primitive | Parameters |
| --- | --- |
| `Box` | `Size X`, `Size Y` |
| `Plank` | `Length`, `Thickness` |
| `Triangle` | `Base`, `Height` |
| `Line` | `Length` |
| `Circle` | `Radius` |
The new primitive uses the current material's default fill/outline tint and
outline width. `Ball` is not shown in the current menu; Circle is the supported
round primitive in the user interface.
## Anchors and Attach mode
### Anchor concepts
An anchor is a named point in prop-local coordinates. Anchors are saved with
the `.prop` file alongside the shape stack.
Every prop always has one fixed anchor named `origin`:
- role: `pivot`;
- position: `(0, 0)`;
- rotation: `0` degrees;
- Z offset: `0`;
- allowed hands: `both`.
Origin cannot be moved, rotated, renamed, or deleted, and it is not listed as
an editable anchor. A subtle, non-interactive center marker is drawn at the
fixed local origin in Arrange, Reshape, and Attach. It is a canvas reference,
not an anchor gizmo that can be selected.
If an older `.prop` file has no `anchors` section, the loader synthesizes this
origin automatically. Saving that file writes the anchor section back out.
### Attach toolbar
| Control | What it does |
| --- | --- |
| `Show Stickman Preview` | Shows or hides the private Holding Preview pane. |
| `Show Other Markers` | Shows or hides the passive origin marker and unselected anchor markers. The selected anchor remains visible. |
The Holding Preview is enabled by default when Attach mode is entered.
### Anchors list and buttons
The list shows every editable custom anchor. `origin` is intentionally hidden
from this list because it is fixed and cannot be edited.
| Control | What it does |
| --- | --- |
| `Add Anchor` | Creates a normalized ID such as `anchor`, places it at the current shape bounds center, and gives it role `custom` with both hands allowed. |
| `Rename` | Opens a text-entry dialog. Type a new name, then click `Rename` or press **Enter** to confirm; click Cancel or close the dialog to leave the name unchanged. |
| `Delete` | Deletes the selected editable anchor. |
Anchor IDs are lower-case, normalized to identifier-safe characters, limited
to 32 characters, and made unique with a suffix such as `_2` when necessary.
The name is committed only after the rename dialog is confirmed.
### Anchor fields
| Field | Values and behavior |
| --- | --- |
| `Role` | `grip`, `emitter`, `impact`, `mount`, or `custom`. `pivot` is reserved for the hidden fixed origin and is not offered for editable anchors. |
| `Allowed Hands` | `both`, `left_only`, or `right_only`. This affects Holding Preview compatibility. |
| `X`, `Y` | Anchor position in prop-local coordinates. |
| `Rotation` | Anchor rotation in degrees, wrapped to the range -180 through 179. |
| `Z Offset` | Draw-order offset from -5 through 5. |
The X/Y fields can also be edited by dragging the selected marker. For grip
and emitter anchors, the selected marker has a small rotation dot underneath
it. Drag that dot to rotate the anchor; hold **Ctrl** to snap rotation to
15-degree increments. The origin has no rotation dot.
Anchor marker colors identify roles: cyan for grip, red for emitter, pink
diamond for impact, purple for mount, and light gray for custom. The selected
marker has an additional yellow selection ring. The fixed origin uses its own
subtle center marker rather than an editable anchor marker.
### Anchor presets
Presets replace the current non-origin anchor layout and select the first
editable grip:
| Preset | Anchors created |
| --- | --- |
| `One-Handed` | One `primary_grip` |
| `Two-Handed` | `primary_grip` and `secondary_grip` |
| `Tool / Nozzle` | `primary_grip` and `emitter` |
| `Melee Edge` | `primary_grip` and `impact` |
Preset positions are derived from the current shape bounds. Applying a preset
is an edit and must be saved like any other change.
## Holding Preview
Holding Preview appears in Attach mode when `Show Stickman Preview` is on. It
uses a private visual-only rig and displays the complete authored multi-shape
prop when the selected anchor is a grip compatible with the selected hand. The
preview pane is borderless and visually separated from the Inspector by the
layout splitters.
If the selected anchor is not a compatible grip, the prop is hidden and the
hint explains that a compatible grip must be selected. Select a grip in the
Anchors list to see the prop held.
### Preview header controls
| Control | What it does |
| --- | --- |
| `Left Hand` / `Right Hand` | Chooses which hand holds the selected grip. A left-only or right-only grip disables the incompatible choice and forces the compatible hand. |
| `Front Pose` / `Walk Left` / `Walk Right` | Chooses the still front-facing pose or an in-place walking direction. Walking mirrors the preview as needed without changing saved anchor data. |
### Preview footer controls
| Control | What it does |
| --- | --- |
| `Pause Walk` / `Resume Walk` | Pauses or resumes the selected walking animation. The button is disabled for Front Pose. |
| `Reset Arm` | Pauses walking and resets the selected arm's manual IK override. In Front Pose, it returns both preview hand targets to their rest positions. |
| `Reset View` | Reframes the camera around the complete stickman and visible prop. |
### Pose and camera gestures
- Drag the cyan diamond target in the preview viewport to pose the selected
arm. Dragging automatically pauses walking; use Resume Walk to return to the
walk animation.
- The target is clamped to the arm's reachable range, so the IK solver does
not request an impossible wrist position.
- The prop follows the solved wrist and selected grip anchor, including the
anchor's rotation and Z offset.
- Mouse wheel zooms the preview camera around the cursor.
- Middle-mouse drag pans the preview camera.
The preview is for visual authoring only. It does not create a gameplay
`PropBlock`, collision bodies, physics, Sandbox actions, or runtime attachment
state. It currently previews one selected grip and one hand; secondary grips
are saved, but they do not drive a second-hand IK solver.
## Saving, dirty state, and recovery
### Saving an off-origin prop
The editor does not silently recenter a prop. On an explicit Save or Save As,
it warns when the combined shape-bounds center is farther from the origin than
`max(1024 px, 2 × bounds diagonal)`. Choose one of:
| Choice | Result |
| --- | --- |
| `🎯 Center & Save` | Translates all shapes and non-origin anchors together, keeps the fixed origin at `(0, 0)`, focuses the canvas on it, then saves. |
| `💾 Save Anyway` | Saves the current positions unchanged. |
| `❌ Cancel` | Leaves the document and save flow unchanged. |
Autosaves never show this warning and never recenter the prop.
The editor marks the document dirty after changes to the name, shapes, or
anchors. Save after changing the material preset as well; a preset-only change
updates the current model but may not show `• Modified` until another
dirty-tracked edit is made. The status bar shows:
`Prop Name — Material • Modified`
The window title also adds `*` while dirty and includes the current filename
when one exists.
After about one second without further edits, a dirty document is written to
`user://prop_editor_autosave.prop`. This is an internal recovery file, not the
normal saved prop path.
- A successful Save, Load, New, or discard clears the autosave.
- If the editor starts with a usable unsaved autosave, it asks whether to
`🔄 Restore` or `🗑️ Discard` it.
- Restoring keeps the document dirty so it still requires an intentional Save.
- An autosave is silently discarded when its saved source file is already
newer or equal.
- The recent list is stored in
`user://prop_editor_settings.json` and contains at most five paths,
newest first.
Closing the window or choosing New while dirty opens a prompt with `💾 Save`,
`🗑️ Discard`, and `❌ Cancel`. Cancel leaves the document open. Save As and
Load dialogs report malformed or unsupported files through the Error dialog.
## Current limitations and future scope
- Attach mode and Holding Preview are Prop Editor authoring tools only.
Sandbox Stage custom-prop loading, equipping, releasing, physics, collision,
and gameplay effects are future work.
- Preview uses one selected grip and one-hand IK. Two-hand IK, emitter effects,
impact behavior, mount behavior, throwing, and runtime lifecycle are not
implemented here.
- The preview can show the full multi-shape authored prop. Existing runtime
prop spawning still uses a deterministic single-shape conversion: the
frontmost closed polygon, otherwise a circle, otherwise the first shape.
Lines are editor-only and do not become collision geometry in that path.
- There is no document-wide Undo/Redo history yet. Autosave and recovery are
safety features, not an edit history.
- The shape clipboard is an in-memory editor clipboard; it is cleared when a
new document is started and is not saved into `.prop` files.
## Complete manual test checklist
### Launch and layout
- [ ] Open `res://scenes/prop_editor.tscn` and press F6.
- [ ] Confirm the File/View menus, prop-name field, mode buttons, canvas,
Inspector, and status bar are visible.
- [ ] Drag the main canvas/Inspector splitter left and right.
- [ ] Make the window narrow; confirm Inspector content can be scrolled and
controls do not run off the window.
- [ ] Confirm the Inspector starts at its minimum usable width and has a
darker contrasting background than the canvas.
- [ ] Confirm the top `Grid` field and `Snap` button have no clipped top or
bottom edges.
- [ ] Enter Attach mode and drag the canvas/preview splitter both directions.
- [ ] Open `eye.prop` in Arrange mode, switch to Attach, and confirm the
preview does not push into the Prop Name row or bottom status bar.
- [ ] Toggle `Show Stickman Preview` and confirm the preview pane appears and
disappears without affecting the prop document.
### New, naming, material, and status
- [ ] Choose File → New and confirm the canvas is empty except for the subtle
fixed-origin marker visible in all three modes.
- [ ] Type a prop name; confirm the status shows `• Modified` and the window
title gets `*`.
- [ ] Select each material preset and confirm the Mass, Friction, and Bounce
readouts update.
- [ ] Enter Attach mode and confirm the Material section is hidden.
- [ ] Confirm the physics readouts are informational and do not launch a
simulation.
### Add and style shapes
- [ ] In Arrange mode, add a Box, Plank, Triangle, Line, and Circle from the
Add Shape section.
- [ ] Change the primitive parameters before adding each shape.
- [ ] Right-click empty canvas and add each of the same five primitives.
- [ ] Select a shape and change Fill, Outline, and Width; confirm the canvas
updates immediately.
- [ ] Confirm Vertices reports the selected shape's point count.
- [ ] Click Erase Shape and confirm only the selected shape is removed.
### Arrange transforms and context menu
- [ ] Drag a selected shape body to translate it.
- [ ] Drag the rotation dot and confirm the shape rotates continuously.
- [ ] Hold Ctrl while rotating and confirm 15-degree stepping.
- [ ] Drag each scale cross; hold Ctrl and confirm uniform scaling.
- [ ] Use Copy, right-click empty canvas, and Paste; confirm the pasted shape
is offset and selected.
- [ ] Test Flip Left-Right and Flip Up-Down.
- [ ] Stack two or more shapes and test Bring Forward and Send Back.
- [ ] Confirm the two-tone selection outline, bounding box, dot, and crosses
remain visible over light and dark shapes.
### Reshape and navigation
- [ ] Switch to Reshape and confirm Add Shape and Erase Shape are hidden.
- [ ] Drag an original vertex and confirm the outline changes.
- [ ] Right-click a segment and choose Create Point; confirm a hollow vertex
appears at the cursor's projected segment position and flashes yellow.
- [ ] Right-click a vertex and choose Remove Point.
- [ ] Right-click the interior of a closed shape and confirm it selects without
opening a menu.
- [ ] Right-click empty canvas and confirm no menu appears.
- [ ] Reshape a circle and confirm Radius is replaced by the reshaped note.
- [ ] Change Radius after reshaping and confirm a clean circle and Radius row
return.
- [ ] Test wheel zoom, pinch/pan if available, and middle-mouse pan. Use View
→ Reset View and confirm only canvas pan/zoom resets.
### Anchors
- [ ] Switch to Attach and confirm the Attach toolbar and Anchors inspector
appear.
- [ ] Confirm the fixed origin marker is visible in Arrange, Reshape, and
Attach, has no text label, and cannot intercept clicks.
- [ ] Confirm `origin` is not in the editable Anchors list.
- [ ] Add an anchor and confirm it appears in the list at the shape-bounds
center with role `custom`.
- [ ] Rename it with a normalized name; confirm both clicking Rename and
pressing **Enter** commit the change, while Cancel leaves the old name.
- [ ] Add another anchor and confirm IDs remain normalized and unique.
- [ ] Delete a non-origin anchor and confirm it is removed.
- [ ] Test every Role and Allowed Hands option.
- [ ] Confirm Role contains grip, emitter, impact, mount, and custom, but not
pivot.
- [ ] Edit X, Y, Rotation, and Z Offset in the Inspector and confirm the marker
and preview update.
- [ ] Drag a selected anchor marker. Toggle Snap and confirm movement follows
the Grid increment.
- [ ] For a grip or emitter, drag the rotation dot and test Ctrl 15-degree
snapping. Confirm the origin marker has no rotation dot.
- [ ] Toggle Show Other Markers and confirm the origin marker and unselected
markers hide/show while the selected anchor remains visible.
- [ ] Apply each preset and confirm its documented anchors appear.
- [ ] Draw or move a prop far from the origin, click Center Prop, and confirm
shapes and anchors translate together while the prop's relative layout stays
unchanged and the canvas focuses on the center marker.
### Holding Preview and IK
- [ ] Select a grip anchor and confirm the complete authored prop appears in
Holding Preview, not only the cyan target marker.
- [ ] Select a non-grip role and confirm the prop hides with the compatibility
hint.
- [ ] Confirm the hand choices are labeled Left Hand and Right Hand. Set a grip
to left-only and right-only and
confirm the incompatible hand is disabled/forced away.
- [ ] Test Front Pose, Walk Left, and Walk Right. Confirm walking previews move
in place and do not change saved anchor X/rotation values.
- [ ] Drag the cyan target and confirm the arm bends with IK and the prop
follows the solved wrist.
- [ ] Confirm dragging the target pauses walking, then click Resume Walk and
confirm the selected walk resumes.
- [ ] Click Pause Walk and Resume Walk and confirm the button text and motion
state change. Confirm Pause Walk is disabled for Front Pose.
- [ ] Click Reset Arm and confirm walking pauses and the selected arm's manual
IK override resets. In Front Pose, confirm both hand targets return to rest.
- [ ] Wheel-zoom and middle-drag the preview camera.
- [ ] Click Reset View and confirm the complete stickman and visible prop are
framed.
- [ ] Confirm the preview has no gameplay physics or collision response.
### Save, load, recent, autosave, and compatibility
- [ ] Save As with and without typing `.prop`; confirm the final file has one
`.prop` extension.
- [ ] Reload the saved file and confirm name, material, all shapes, colors,
transforms, and anchors survive.
- [ ] Confirm a successful save clears the dirty indicator and updates Recent.
- [ ] Load through File → Recent and confirm the same document returns.
- [ ] Save/load enough files to verify Recent keeps only five, newest first.
- [ ] Move a prop far from the origin and use Save; verify the off-origin
warning offers Center & Save, Save Anyway, and Cancel, and that each choice
has the documented result.
- [ ] Remove or rename a recent file outside the editor; open Recent and
confirm the missing entry is pruned.
- [ ] Make an edit, wait at least one second, close/restart the editor, and
test Restore and Discard in the recovery dialog.
- [ ] Confirm Restore keeps the prop dirty and Discard clears the recovery
file.
- [ ] Test File → New and window close while dirty; verify Save, Discard, and
Cancel each do the expected thing.
- [ ] Load a `.prop` file that has no `anchors` key. Confirm Attach mode
synthesizes the fixed origin, then save and verify anchors are written.
- [ ] Load malformed JSON or an unsupported `version` and confirm the Error
dialog appears without replacing the current valid document.
+8
View File
@@ -45,6 +45,11 @@ This document tracks known technical debt, optimization opportunities, and minor
| 33 | **Prop File save-serialization zeroed every point (fixed, Round 5)** — `PropFile._serialize_shape` assigned an export-format `Array` of `{x,y}` dicts directly to a typed `PackedVector2Array`; Godot's implicit conversion cannot build `Vector2` from a dictionary and silently yielded `Vector2.ZERO` per element, so every `.prop` saved by Round 1-4 wrote all points as `(0,0)` (loading as invisible/degenerate geometry). | High | ✅ Resolved | Round 5: `_serialize_shape` now routes points through `_coerce_points`; regression assertions check point *values* through `to_file_dict` and the full round-trip, plus an end-to-end canvas→save→load test. The previously saved `props/eye.prop` and `props/box.prop` are unrecoverable (coordinates were zeroed at save time) and were re-authored. Lesson: any typed `Packed*Array` assignment from dictionary/JSON data needs a Variant-coercion helper. (2026-09-19) |
| 34 | **Prop Editor per-shape `reshaped` flag** — every shape carries `reshaped` (false by default; set by vertex insert/remove/drag and non-uniform circle scaling, cleared by `set_shape_radius`), which gates primitive-specific inspector fields (today: the circle Radius row, replaced by a muted "reshaped" note). | Low | Open | Round 6 (`specs/phase_9_prop_editor_round6.md`). Only the circle consumes the flag today; future primitive params (box W/H, plank length/thickness) should reuse it instead of adding per-type detection. An "un-reshape" affordance (restoring a clean primitive from its params) is a possible future feature. (2026-09-19) |
| 35 | **Prop Editor selection uses a two-tone halo** — after Round 6's fill-based contrast failed for the reported "shape in front of a white shape" case (the selection extends beyond the shape onto the background), every selection element (outline polyline, bbox, rotation dot, scale crosses, vertex handles) is drawn as a dark backing pass + bright core pass. | Low | Open | Round 7 (`specs/phase_9_prop_editor_round7.md`). The pair is visible on any background but doubles the selection draw calls; negligible at sandbox scale. If a custom selection color is ever wanted (e.g. per-user theme), it should replace the core pass while keeping the halo. (2026-09-19) |
| 36 | **Phase 9a anchor roles have no gameplay consumers yet** — `.prop` assets can author/query `grip`, `emitter`, `impact`, `mount`, and `custom`, but Phase 9a is Prop Editor-only. Secondary grips do not drive two-hand IK, and no role dispatches runtime effects, damage, mounting, or equip behavior. | Medium | Open | User-approved staging in `specs/phase_9a_prop_attach.md`: land a stable authoring/schema seam plus an isolated one-hand IK preview first. A separately approved future runtime phase must design custom-prop loading, equip/release ownership, two-hand IK, emitters, melee impacts, accessory mounting, throwing, and selector/stage persistence without assuming the editor preview is gameplay-authoritative. (2026-09-20) |
| 37 | **Prop Editor still has no document-wide undo/redo** — Phase 9a anchor edits use the existing dirty/autosave/recovery pipeline, but there is no `UndoRedo` history for either shapes or anchors. | Medium | Open | An anchor-only stack was explicitly rejected because Arrange/Reshape edits would remain non-undoable and the UI would be inconsistent. Future work should introduce one document-level command/history boundary covering shapes, anchors, material/name changes, and preset replacement together. (2026-09-20) |
| 38 | **Phase 9a preview does not establish runtime attachment parity** — the Prop Editor uses a private, non-physics, full-multi-shape rig preview with editor-only facing and z-order conventions. It intentionally proves anchor authoring and arm-IK evaluation, not Sandbox loading, collision, physics, mirroring, per-limb interleaving, or attachment lifecycle. | Medium | Open | A future runtime spec must independently validate the anchor transform against gameplay animation/physics and decide its visual representation, sockets or solved-wrist API, mirror strategy, collision/freeze behavior, per-limb draw order, release/ragdoll handling, and multi-shape fidelity. Do not treat preview acceptance tests as runtime tests. (2026-09-20) |
| 39 | **Phase 9b prop centering is geometric AABB centering, not physical center of mass** — `Center Prop` uses the union AABB of internal polygon/line/circle points and deliberately excludes outline expansion, anchors, material mass, and collision geometry. | Low | Open | This is deterministic and appropriate for the fixed authoring origin, but a future physics-pivot workflow may need a separately designed center-of-mass or collision-bounds command. Phase 9b keeps canonical `origin` fixed/serialized, hides it from editing, never auto-centers after edits, and uses an explicit-save warning heuristic of `max(1024 px, 2 × bounds diagonal)`. Document-wide undo remains tracked separately by #37. (2026-09-20) |
| 40 | **Phase 9c walking preview uses exclusive gait-or-manual ownership of the selected hand target** — the private AnimationPlayer owns the target while gait plays; beginning a cyan-target drag pauses the gait before pointer input takes ownership. | Low | Open | This deterministic policy prevents AnimationPlayer and manual IK from overwriting the same Marker2D in one frame. It means an author cannot add a live hand offset while the walk continues. If that becomes valuable, design a preview-only additive offset/blend layer rather than relying on process order. The gait remains an in-place, non-physics authoring preview and is not navigation/runtime parity (#38). (2026-09-21) |
---
@@ -88,6 +93,9 @@ This document tracks known technical debt, optimization opportunities, and minor
| 2026-09-11 | Phase 4c (Trigger Area Reliability) verified: `sandbox_stage.gd` `entered_area` now requires source + area instance IDs and detects visible world-AABB overlap for ANIMATED stickmen/unfrozen props, edge-triggered per area/movable pair. The prop enumeration scope bug was corrected. `tests/test_phase4c_area.gd` passed all 19 assertions, including transformed-area, leg-level overlap, wildcard/re-entry, speech consequence, stale-target, and deletion-cleanup coverage; editor verification confirmed the speech bubble fires for the configured source stickman. Logged #24 (session-only instance-ID rule references). |
| 2026-09-12 | Phase 4.5 collision bodies verified: ten explicitly synchronized `AnimatableBody2D` proxies replace the old feet-point stickman collision poll; `PropBlock` supplies world-space contact data with shape-cast CCD; stage routing preserves rig→prop rules with per-part pair debouncing; bounded quadrant-aware flinches and PLAY→DIRECT authored-pose restoration were verified. `tests/test_phase45_collision_bodies.gd` passed 223 assertions; Phase 4b (53), Phase 4c (19), and Phase 3c (96) remained green. Reconciled #1/#5 and retained #25–#27 as intentional implementation limitations. |
| 2026-09-17 | Phase 6a (Airborne Momentum, Procedural Air Pose, and Landing Absorption) implemented and user-verified. `StickmanRig` now carries signed ledge momentum with tuned exponential drag (`AIR_DRAG_X = 0.75`), swept horizontal proxy collision, additive free-fall pose with forward-before-lift arms, and height-driven landing absorption: falls below 80 px are soft; 80..900 px maps to 32..160 px torso compression and 270..520 px hand drop over 0.30 s. Independent foot contacts preserve ramps/stairs, while `LANDING_IMPACT` blocks primary queue progression and direct ragdoll remains an immediate preemption. `tests/test_phase6a_airborne_landing.gd` passed 24 assertions; `tests/test_phase4b_terrain.gd` passed 8 assertions. Logged #28 (per-proxy airborne sweep scaling). |
| 2026-09-20 | Phase 9a Prop Attach specification written and refined (`specs/phase_9a_prop_attach.md`). Approved architecture: append Attach to the existing Arrange/Reshape system; additive role-based `.prop` anchors with a fixed synthesized `origin`; and an isolated Prop Editor stickman preview that uses real arm IK while showing the full authored prop. All Sandbox/custom-prop loading, attachment, sockets, physics/collision, lifecycle, and runtime tests are explicitly deferred. Logged #36 (all gameplay consumers deferred), #37 (document-wide undo remains absent), and #38 (editor preview is not a runtime-parity guarantee). Existing #29 remains open independently because the current runtime still flattens multi-shape props. |
| 2026-09-20 | Phase 9b Prop Centering specification written (`specs/phase_9b_prop_centering.md`). Approved architecture: hide canonical origin from editable anchors while retaining it in `.prop` v1.0; draw a passive constant-screen-size `(0,0)` marker in all three modes; add an always-visible Center Prop document transaction; center the authoring camera on origin for startup/New/load/reset and preserve zoom after centering; and warn explicit saves with Center & Save / Save Anyway / Cancel when displacement exceeds `max(1024 px, 2 × bounds diagonal)`. Autosave stays silent and automatic recenter-on-edit is explicitly rejected. Logged #39 (AABB authoring center is not a future physics center of mass); #37 continues to track document-wide undo. |
| 2026-09-21 | Phase 9c Prop Editor UX specification written (`specs/phase_9c_prop_editor_ux.md`). Approved architecture: a single Front Pose / Walk Left / Walk Right preview-motion selector with dynamic Pause/Resume; manual arm IK pauses gait before taking target ownership; modal typed anchor rename; Show Other Markers controls origin plus unselected anchors; one global Snap source; theme-driven `#262C39` inspector; container-driven unclipped chrome; simplified full-figure preview with no blue border. The preview directly plays the proven in-place gait/mirror policy and remains isolated from navigation, physics, actions, and Sandbox state. Logged #40 (exclusive gait/manual hand-target ownership). |
---