100 lines
6.6 KiB
Markdown
100 lines
6.6 KiB
Markdown
# AGENTS.md — stickman (Godot 4.4)
|
||
|
||
## Project type
|
||
- **Godot 4.4** 2D/GUI project (Forward Plus renderer)
|
||
- No CLI build/test/lint commands; open in the Godot editor to run
|
||
- **Main scene:** `res://scenes/stickman_editor.tscn` (set as `run/main_scene` in `project.godot`)
|
||
|
||
## Project overview
|
||
Stickman Studio is an editor tool for drawing and assembling stick figures. It is a
|
||
`Control`-based GUI (not physics/animation) in its current phase. Body-part vector shapes
|
||
are authored per-panel (each panel supports **multiple shapes** with Z-ordering) and
|
||
assembled in a "Whole Stickman" preview that supports translation, rotation, and scale.
|
||
|
||
## Required addon
|
||
- **Scalable Vector Shapes 2D** (v2.27.7) at `addons/curved_lines_2d/`
|
||
- Declared dependency for the project. The current editor UI does not instantiate it directly,
|
||
but keep it present — it is required for the legacy `stick.tscn` rig.
|
||
|
||
## Architecture
|
||
- `scripts/stickman_editor.gd` — `extends Control`; the main controller. Owns the menu bar,
|
||
save/load/clear flow, JSON (de)serialization, and populates the 10 body-part panels.
|
||
Writes `FILE_VERSION "1.2"`; auto-migrates `"1.0"`/`"1.1"` files on load. Coordinates cross-panel
|
||
selection so only one shape is selected at a time (`shape_selected` → deselect others).
|
||
Collects per-part `{shapes[], position, rotation, scale}` for save/load.
|
||
- `scripts/body_part_panel.gd` — `class_name BodyPartPanel`, `extends PanelContainer`.
|
||
Reusable per-part editor. Public API:
|
||
- `set_shape_data(data: Variant)` — import shape data (Array or single Dictionary; used on Load/Clear)
|
||
- `get_shape_data() -> Array[Dictionary]` — export array of `{shape_type, points, color, closed, vertex_flags}`
|
||
- `clear_shape()` — reset panel, clears all shapes (does **not** emit `shape_changed`)
|
||
- `select()` — mark the panel's topmost shape as selected (white outline highlight)
|
||
- `deselect()` — clear selection and cancel any in-progress vertex drag
|
||
- `signal shape_changed(shapes: Array)` — emitted when any shape is created, modified, deleted, or reordered
|
||
- `signal shape_selected()` — emitted on left-click; the editor deselects all other panels
|
||
- **Phase 2 vertex editing:** left-click on a shape selects it; drag a vertex handle to
|
||
reshape in real time; with a shape selected, right-click near an outline edge offers
|
||
"Create Point" (inserts a vertex flagged `1` at the edge midpoint). Original vertices
|
||
are filled circles; user-created vertices are hollow rectangles.
|
||
- **Phase 4 multi-shape:** panels store a `shapes[]` array. Right-click context menu
|
||
includes "Send Back" (id 7) and "Bring Forward" (id 8) for Z-ordering. "Delete" (id 5)
|
||
removes the specific shape under the mouse. `_selected_shape_idx` tracks which shape
|
||
is active for vertex editing.
|
||
- **Per-panel zoom:** mouse wheel multiplies `_zoom` by 1.10, clamped to `[0.3, 3.0]`;
|
||
drawing and input hit-testing both run in world space via `draw_set_transform`.
|
||
- **Drawing approach:** closedness is read from the `closed` flag (Phase 2), not
|
||
`shape_type`. Closed shapes are filled via `draw_colored_polygon(pts, color)` before a
|
||
2 px outline is drawn with `_draw_polyline(..., closed=true)`. Open shapes render
|
||
outline-only (`closed=false`). Shapes draw in array order (z-order).
|
||
- `scripts/whole_stickman_preview.gd` — `class_name WholeStickmanPreview`,
|
||
`extends PanelContainer`. Assembles all parts and handles drag-to-reposition, rotation, scale.
|
||
- `set_body_parts(parts_data)`, `set_all_part_positions(pos)`, `get_part_position(name)`
|
||
- `get_part_rotation(name) -> float`, `set_part_rotation(name, degrees)`
|
||
- `get_part_scale(name) -> Vector2`, `set_part_scale(name, scale)`
|
||
- `_selected_part` (String) — part with white bounding box selected
|
||
- `_interaction` (enum: NONE, TRANSLATE, ROTATE, SCALE) — current gizmo interaction
|
||
- Rotation gizmo: filled circle centered below bounding box; Ctrl = 15° snap
|
||
- Scale gizmo: crosses at 4 corners; Ctrl = aspect ratio lock
|
||
- `signal part_moved(part_name, new_position)`
|
||
- Per-panel zoom via mouse wheel (×1.10, clamped to `[0.3, 3.0]`); drag hit-tests and
|
||
rendering run in world space.
|
||
- Scenes:
|
||
- `scenes/stickman_editor.tscn` — main editor layout; unique-name nodes (`%Prefix`) used
|
||
for typed `@onready` access: `%MenuBar`, `%StickmanNameEdit`, `%LeftColumn`,
|
||
`%CenterColumn`, `%WholeStickmanPreview`, `%SaveDialog`, `%LoadDialog`,
|
||
`%ClearConfirmDialog`, `%ErrorDialog`.
|
||
- `scenes/body_part_panel.tscn` — instantiated 10× at runtime (5 per column). Each panel
|
||
sets `size_flags_vertical = SIZE_EXPAND_FILL` so the panels expand to fill the column
|
||
height in their parent VBoxContainer.
|
||
|
||
### Body-part data model
|
||
- 10 internal part keys (ordered): `head`, `torso`, `left_upper_arm`, `left_lower_arm`,
|
||
`right_upper_arm`, `right_lower_arm`, `left_upper_leg`, `left_lower_leg`,
|
||
`right_upper_leg`, `right_lower_leg`.
|
||
- Shape dictionary: `{ "shape_type": String, "points": Array[{x,y}], "color": "#hex",
|
||
"closed": bool, "vertex_flags": Array[int] }`.
|
||
- `shape_type` values: `"line"`, `"rectangle"`, `"circle"`, `""` (empty).
|
||
Descriptive tag in Phase 2 — rendering uses `closed`.
|
||
- `closed`: `true` = filled + closed outline, `false` = open outline-only.
|
||
- `vertex_flags`: same length as `points`; `0` = original vertex (filled circle),
|
||
`1` = user-created via "Create Point" (hollow rectangle).
|
||
- **Phase 4:** A panel stores a `shapes[]` array of shape dictionaries. Z-order = array
|
||
position (first = back, last = front). Per-part data includes `{shapes[], position,
|
||
rotation, scale}`.
|
||
- The JSON `.stk` format is defined in `README.md` (versioned `"1.2"`, extensible;
|
||
`"1.0"`/`"1.1"` files auto-migrate on load).
|
||
|
||
### Legacy scene (do not delete)
|
||
- `stick.tscn` — the original rigged/animated figure using `Skeleton2D` + IK targets +
|
||
`RemoteTransform2D` + `Line2D` limbs, plus an embedded `@tool` script drawing the head
|
||
circle. Animations: "walk", "walk_to", "RESET". **Not** the current main scene; kept for
|
||
future animation/rigging phases.
|
||
|
||
## Editor conventions
|
||
- `.godot/` is gitignored; never edit it manually.
|
||
- Scene files (`*.tscn`) and `.import` files are text-based; use Godot's editor for complex changes.
|
||
- UID references (Godot 4 native) exist in scenes; do not change them by hand.
|
||
- Use standard Godot `Control` nodes where applicable.
|
||
- Use `class_name` for globally referenced scripts (`BodyPartPanel`, `WholeStickmanPreview`).
|
||
- Prefer `%UniqueName` access over exported node paths in `stickman_editor.gd` / `body_part_panel.gd`.
|
||
- Keep the `.stk` JSON format backward compatible — never change the meaning of an existing key.
|