|
1 | 1 | # Development Guide |
2 | 2 |
|
3 | | -Last updated: March 24, 2026 |
| 3 | +Last updated: March 26, 2026 |
4 | 4 |
|
5 | 5 | This document covers local setup, codebase structure, and how to test features. |
6 | 6 |
|
@@ -36,6 +36,8 @@ addons/hammerforge/ |
36 | 36 | hf_tool_registry.gd Plugin API: tool registration, dispatch, external tool loader |
37 | 37 | hf_keymap.gd Customizable keyboard shortcuts (JSON load/save, action matching) |
38 | 38 | hf_user_prefs.gd Cross-session user preferences (user://hammerforge_prefs.json) |
| 39 | + hf_snap_system.gd Centralized snap (Grid/Vertex/Center modes, threshold-based candidates) |
| 40 | + hf_op_result.gd Lightweight operation result (ok, message, fix_hint) |
39 | 41 | surface_paint.gd Per-face surface paint tool |
40 | 42 | uv_editor.gd UV editing dock |
41 | 43 | highlight.gdshader Selection highlight shader (wireframe, unshaded, alpha) |
@@ -117,13 +119,17 @@ addons/hammerforge/ |
117 | 119 | - **Declarative tool settings.** External tools expose `get_settings_schema()` → Array of `{name, type, label, default, min, max, options}`. Dock auto-generates controls via `rebuild_tool_settings()`. Use `get_setting(key)` / `set_setting(key, val)` for storage. |
118 | 120 | - **Tag-based invalidation.** Call `root.tag_brush_dirty(id)` when a brush is modified; `root.tag_full_reconcile()` for structural changes (hollow, clip). Guard with `root.has_method("tag_brush_dirty")` for test shim compatibility. |
119 | 121 | - **Signal batching.** Wrap multi-brush operations in `root.begin_signal_batch()` / `root.end_signal_batch()`. Transactions do this automatically. On rollback, call `root.discard_signal_batch()` to drop queued signals without emission. |
| 122 | +- **Operation results.** Methods that can fail (hollow, clip, delete) return `HFOpResult` with `ok`, `message`, and `fix_hint`. Use `_op_fail(msg, hint)` in brush_system to both emit `user_message` and return a fail result. Callers can check `result.ok` programmatically, but failures also auto-toast via the `user_message` signal. |
| 123 | +- **Geometry-aware snapping.** `_snap_point()` delegates to `HFSnapSystem`. Three modes (Grid=1, Vertex=2, Center=4) as a bitmask. Vertex mode collects 8 box corners from all brushes; Center mode collects brush centers. Closest candidate within `snap_threshold` beats grid snap. Pass `exclude_ids` to skip the brush being dragged. |
| 124 | +- **Reference cleanup.** `delete_brush()` calls `_cleanup_brush_references()` which strips group_id meta (+ cleans empty groups via `visgroup_system._cleanup_empty_group()`), clears visgroup membership, and calls `entity_system.cleanup_dangling_connections()` to remove I/O connections targeting the deleted node. Always fires before the node is removed from the tree. |
| 125 | +- **Live dimensions.** `input_state.get_drag_dimensions()` returns `Vector3(W, H, D)` during DRAG_BASE/DRAG_HEIGHT; `Vector3.ZERO` otherwise. `format_dimensions()` renders as `"64 x 32 x 48"` (whole numbers omit decimals). The mode indicator banner appends dimensions to the stage hint during drag gestures. |
120 | 126 |
|
121 | 127 | ### CI |
122 | 128 |
|
123 | 129 | The project has a GitHub Actions workflow (`.github/workflows/ci.yml`) that runs on push and PR to `main`: |
124 | 130 | - `gdformat --check` -- verifies formatting |
125 | 131 | - `gdlint` -- checks lint rules (configured in `.gdlintrc`) |
126 | | -- **GUT unit tests** -- 371 tests across 23 test files (runs Godot headless) |
| 132 | +- **GUT unit tests** -- 413 tests across 27 test files (runs Godot headless) |
127 | 133 |
|
128 | 134 | Run locally before pushing: |
129 | 135 | ``` |
@@ -161,6 +167,10 @@ Tests live in `tests/` and use the [GUT](https://github.com/bitwes/Gut) framewor |
161 | 167 | | `test_user_prefs.gd` | 9 | Default values, get/set prefs, section collapsed state, recent files (add/dedup/max 10), JSON roundtrip | |
162 | 168 | | `test_dirty_tags.gd` | 11 | Brush dirty tags (add/dedup), paint chunk tags, full reconcile flag, consume-clears, signal batch queue/flush/discard/nesting | |
163 | 169 | | `test_prototype_textures.gd` | 27 | Catalog constants, path generation, texture existence, material persistence (resource_path), batch loading into MaterialManager | |
| 170 | +| `test_op_result.gd` | 15 | HFOpResult constructors, hollow/clip/delete return values, fail emits user_message, fix_hint population | |
| 171 | +| `test_snap_system.gd` | 12 | Grid/Vertex/Center snap modes, threshold, exclude list, priority (closer geometry beats grid), empty scene fallback | |
| 172 | +| `test_drag_dimensions.gd` | 8 | get_drag_dimensions() in all modes, format_dimensions() whole/fractional/zero | |
| 173 | +| `test_reference_cleanup.gd` | 9 | Delete cleans group/visgroup membership, entity I/O cleanup_dangling_connections, preserves unrelated, no-crash on clean node | |
164 | 174 |
|
165 | 175 | Run all tests: |
166 | 176 | ``` |
@@ -236,6 +246,28 @@ Selection Tools (Brush tab — visible when brushes are selected) |
236 | 246 | - Select a different entity -- confirm the I/O list updates to show that entity's connections. |
237 | 247 | - Save .hflevel with entity I/O connections, reload, and confirm connections persist. |
238 | 248 |
|
| 249 | +Snap Modes |
| 250 | +- In Brush tab, click V (Vertex) toggle next to Grid Snap presets. |
| 251 | +- Place a brush, then start drawing another near a corner of the first brush -- confirm it snaps to the exact corner. |
| 252 | +- Click C (Center) toggle. Draw a brush near the center of an existing brush -- confirm it snaps to the center. |
| 253 | +- Disable G (Grid) and both V and C -- confirm brush placement is unsnapped. |
| 254 | +- Re-enable G -- confirm grid snapping resumes. |
| 255 | + |
| 256 | +Live Dimensions |
| 257 | +- Start drawing a brush and observe the mode indicator banner showing "Step 1/2: Draw base — W x H x D" with live updating dimensions. |
| 258 | +- Click to advance to height stage and observe "Step 2/2: Set height — W x H x D" with height updating as you move the mouse. |
| 259 | +- Type "64" and press Enter -- confirm the dimension display reflects the typed value. |
| 260 | + |
| 261 | +Operation Feedback |
| 262 | +- Select a very small brush (e.g. 4x4x4) and press Ctrl+H with wall thickness 4 -- confirm a toast appears with "Wall thickness too large" and a fix hint. |
| 263 | +- Select a brush and press Ctrl+H with a valid thickness -- confirm success (no error toast, 6 walls created). |
| 264 | +- Press Shift+X on a brush -- confirm success toast or appropriate error if split position is invalid. |
| 265 | + |
| 266 | +Reference Cleanup |
| 267 | +- Place a brush, add it to a group (Ctrl+G), then delete it. Confirm the group is automatically cleaned up. |
| 268 | +- Place a brush, add it to a visgroup in the Manage tab, then delete the brush. Confirm the visgroup no longer lists the deleted brush. |
| 269 | +- Create two entities with an I/O connection between them. Delete the target entity. Confirm a toast reports the removed connection count. |
| 270 | + |
239 | 271 | Brush workflow |
240 | 272 | - Draw an Add brush and confirm resize handles work. |
241 | 273 | - Draw a Subtract brush and apply cuts. |
|
0 commit comments