HammerForge Editor Smoke Checklist¶
What this document is¶
Two things, and they are not the same.
The Release gate below is short, and it is run. .github/workflows/release.yml
refuses to ship a version that is not recorded here as having passed it.
The Checklist after it is a reference. It is not run end to end, it has never been run end to end, and at its present length it never will be. Treat any step in it as unverified until someone says otherwise.
That distinction is the point. A step written down here is a description of what
should happen, not a report that it does. Adding a line to this document is not
evidence that a case is covered, and closing a fix with a line added here leaves
the case untested. Put the check where it can fail on its own: a test in tests/
if headless can see it, the gate below if only the live editor can.
592 is the worked example. Clicking a brush switched the editor to the¶
HammerForge main screen for eleven days. Section 2b is a dozen steps that each need a brush clicked in the 3D viewport, and this document was edited five times inside that window without anyone hitting it.
Release gate¶
Run before tagging a release. Ten minutes, not a day. Keep it that way: a gate that grows stops being run, which is how the rest of this document got here.
Prep:
godot --headless -s res://tools/prepare_editor_smoke.gd --path .
Then open res://samples/hf_editor_smoke_start.tscn with the plugin enabled.
- The plugin enables with nothing red in the Output panel.
- HammerForge sits in the main-screen switcher beside 2D / 3D / Script, and the left dock tab reads HammerForge.
- Create Starter Level makes a
LevelRootwith a floor, a sun and a spawn, as one undo action (#1029): Ctrl+Z takes theLevelRootand everything in it. Ctrl+Shift+Z brings it all back -- the spawn and the containers as well as the floor and the sun, owned: the Scene dock lists them, and a Ctrl+S after the redo writes them (#772). The spawn shows its capsule again after the redo (#1389). - Draw a box brush. Drag one of its resize handles, the dots on each face; the box resizes and it is one undo step. Ctrl+Z on the drawn brush leaves nothing in its place (#1388).
- Click that brush in the 3D viewport. The 3D view is still what you are looking at. (#592: a plugin that declares a main screen and also handles the selected object gets switched to by Godot.)
- Select a
Camera3D. The dock stays connected to the existingLevelRootand no brush behind the camera is selected. - Bake. The Bake status row goes green and names how long the bake took. Do not look for it in the Console Log:
HFLogonly haswarn(), so a clean bake writes nothing there and the Log should stay at 0 warnings and 0 errors. - Test Level launches a playtest and the player spawns on the geometry.
- Save the scene, restart Godot, reopen it. The
LevelRootand the brushes come back. - Disable and re-enable the plugin. The switcher entry, the viewport lamp and the dock tab icon all come back, and nothing is left behind.
After tagging, before the next release starts, save the release's level with
the release build into tests/levels/<version>/, as tests/levels/README.md
says. CI loads every one of those with every later build (#1640).
Gate record¶
One line, edited by hand when the gate passes. The release workflow reads it.
Gate passed: 0.4.0
Last run: 2026-10-21, Godot v4.7.stable.official, against the 0.4.0 build. All ten checks executed and passed.
It took two passes. The first found two defects, fixed in the release and each verified here in the editor on the second pass:
- Undo of a drawn brush left the draw preview behind as a real brush
(#1388). Draw, Ctrl+Z, and a
PreviewBrushstood where the brush had been, drawn, counted in Live Brushes, saved and baked. Now Ctrl+Z leaves DraftBrushes empty and the status reads All clear. - Entity markers were missing after a scene opened and after redo (#1389). The spawn's capsule was gone after an undo and redo of Create Starter Level and after the scene was reopened. Now it shows in both.
It also corrected two lines above: Create Starter Level has been one undo action since #1029, and the resize handles are the dots on each face, not yellow.
Three notes for whoever runs this next, because all three cost time here:
- Synthetic mouse input no longer carries the left button on a held move, so
neither the Draw tool's base nor Godot's own gizmo follows a press, a run of
small moves and a release. A single drag does reach Godot's gizmos and the
resize handles. Draw the brush by typing its size instead: press in the
viewport, type
4and Enter for the base,3and Enter for the height, release. A real mouse drags fine, and a test through the plugin pins it. - For the Camera3D check, put the camera in front of a brush, not inside one: from inside, the brush's near face is in front of the camera and rightly takes the click.
- Run it against a
.godotthat has settled. A fresh--importmakes the first editor launch report reimport errors, and a half-built class cache can report cyclic-reference parse errors intests/that are not real -- the same tree comes up at zero errors on the next launch. Confirm any parse error headlessly before believing it.
Previous run: 2026-09-19, Godot 4.7.stable, against 0.3.2. Its first pass found four defects (#771 to #774): Test Level with no player or camera, redo of Create Starter Level losing the spawn and saving unowned containers, a Bake row with no duration, and a config warning on every new level.
Previous run: 2026-09-16, Godot 4.7.2.stable. It corrected two of the checks above, and running the resize handle turned up a real defect: undoing a resize rebuilt every brush without its name, which silently unwires entity I/O. Fixed in #597.
Checklist¶
Everything from here down is the reference described above. It is not gated, and
most of it has never been run. It covers the editor-only flows that are hard to validate in headless tests:
- viewport input ownership: native Object Select clicks/marquees and filled hit targets, modal HammerForge Face Select, native navigation/gizmos, shortcut scope, and lost-release recovery
- picking correctness: exact non-box faces and placement, hidden visgroups, internal/geometry-less entity previews, nearest brush/entity ordering, and scaled transforms
- tutorial banner startup before LevelRoot exists
- live dock/tab interaction while the tutorial is visible
- shortcut dialog behavior in the real dock
- prefab save + drag/drop + undo/redo
- cut preview toggle in the viewport
- vertex editing: edge sub-mode, split, merge, wireframe overlay
- polygon tool: click vertices, close, extrude height, brush creation
- path tool: place waypoints, finalize, mitred corridor brushes, close a loop on the first point, Side, auto-stairs/railings/trim extras
- material browser: thumbnail grid, search, filters, favorites, hover preview, context menu
- texture picker: T key eyedropper for sampling face materials
- spawn system: validation debug overlay, Test Level with missing/invalid spawn, Test tab spawn controls
- context toolbar: floating toolbar shows/hides per selection, correct buttons per context
- command palette: search, fuzzy search, gray-out, action execution, Shift+?/F1/Ctrl+K toggle
- coach marks: first-use tool guides appear on tool activation, dismissal persistence
- operation replay: timeline display, hover details, replay undo/redo navigation
- example library: load examples, study annotations, search/filter
- convert selection to heightmap: brush selection → heightmap layer with terrain
- foliage & scatter: circle/spline preview, commit, clear, UI controls in Paint tab
- snapping: Grid/Vertex/Center/Edge/Perpendicular candidates and dock toggle state
- playtest export: player/spawn, nested ownership, world transforms, and entity I/O
Prep¶
Reset HammerForge user prefs to a known state:
godot --headless -s res://tools/prepare_editor_smoke.gd --path .
To verify tutorial resume behavior instead of a fresh start:
godot --headless -s res://tools/prepare_editor_smoke.gd --path . -- --tutorial-step=1
Then open:
res://samples/hf_editor_smoke_start.tscn
Enable the HammerForge plugin if it is not already enabled.
0a. HammerForge Console (main screen)¶
- Confirm HammerForge sits in the main-screen switcher at the top, beside 2D / 3D / Script, wearing the HammerForge mark, and that the mark is the same height as the other entries rather than lifting the toolbar.
- Confirm the left dock's tab reads HammerForge, not "Dock", and wears the same mark.
- Confirm a lamp and a one-line summary sit in the 3D viewport toolbar, and that clicking it opens the Console.
- Open the Console. Confirm the header shows the HammerForge lockup, the version, one lamp, and a summary line, and that the status board fills the screen rather than collapsing to a strip.
- With no
LevelRootin the scene: confirm Level root is red and offers Create Starter Level; confirm the other level checks are grey ("Not measured yet"), not red. - Press Create Starter Level. Confirm the row turns green, names the node, and the scope line fills in with brush and entity counts.
- Confirm each amber and red row carries a coloured left edge and a sentence naming the consequence, and that hovering any part of a row shows a tooltip naming the severity and the threshold.
- Toggle Needs attention only. Confirm green and grey rows disappear and nothing reorders.
- Switch to Controls. Confirm three groups, every switch captioned, and that a switch flipped here also flips in the dock's Test → Settings or Advanced Bake section.
- Type
pathfindinginto Find a setting. Confirm Bake navmesh survives the filter. Clear it and confirm everything returns. - Turn Descriptions off. Confirm captions collapse and the tooltips still carry the same text.
- Start typing in Autosave every (min) and wait two seconds. Confirm the value is not reset under the cursor.
- Switch to Log. Confirm HammerForge's own messages appear with timestamps, levels and categories, and that the level buttons show counts.
- Bake, then confirm the Bake status row goes from amber to green and names how long the bake took. A clean bake writes nothing to the Log:
HFLogcarries warnings only, so the Log stays at 0 warnings and 0 errors unless the bake complains. - Click a level count button to filter, then use the text filter. Confirm Showing N of M retained tracks both.
- Press Copy, paste elsewhere, and confirm only the visible lines came across. Press Save… and confirm the file matches.
- Switch the editor between light and dark themes. Confirm the lockup swaps, the lamps stay legible, and the mark stays on both the panel button and the dock tab.
- Switch to the 3D screen and leave the editor idle. Confirm the profiler shows no per-second Console work while it is off screen, and that the viewport lamp still tracks the level on its own slower beat.
- Disable and re-enable the plugin. Confirm the switcher entry, the viewport lamp and the dock tab icon all come back, and nothing is left behind.
To judge a Console layout change without opening the editor:
godot --path . -s res://tools/hf_console_preview.gd --resolution 1280x430
It writes one PNG per tab under user://console_preview/.
0. Core Loop Default¶
- Confirm the dock leads with Test Level on the Test tab.
- Confirm Power-user overlays is unchecked on a fresh prefs file, both in Test → Settings and in the Console's Controls tab.
- Press backtick and Ctrl+Shift+T. Confirm a toast names both places to turn them on, rather than opening the radial menu or replay timeline.
- Turn them on from the Console's Controls tab and confirm the dock checkbox follows, then confirm the radial menu opens.
- Enable the checkbox, then confirm the radial menu and replay timeline work. Disable it again and confirm they disappear.
- Confirm Cut Preview is the cut-preview checkbox label.
1. Fresh Tutorial Startup¶
- Open
hf_editor_smoke_start.tscn. - Confirm there is no
LevelRootin the scene tree yet. - Confirm the HammerForge dock shows the tutorial immediately.
- Confirm the tutorial title, body text, and progress bar are populated.
- Confirm the four dock tabs remain visible and clickable while the tutorial is shown.
- Switch between
Build,Paint,Objects, andTest; confirm the tutorial banner remains visible and the dock does not collapse or blank.
1b. Starter Level Template¶
- With no
LevelRoot, click Create Starter in the empty-state banner. - Confirm a TempFloor (CSGBox3D), DefaultSun (DirectionalLight3D), and player_start entity appear in the scene tree.
- Confirm the toast reports that the starter level was created.
- Press Ctrl+Z to undo; confirm all three nodes are removed.
- Press Ctrl+Shift+Z to redo; confirm all three nodes reappear.
- Run Test Level; confirm the playtest scene uses the same sun angle as the editor (shadows match).
2. Late LevelRoot Hookup¶
- In the 3D viewport, create
LevelRootusing Create Empty, or with a left-click with Draw active in a new, empty 3D scene. - Confirm the tutorial remains on step 1 rather than resetting or disappearing.
- Confirm step 1 advances only after adding a brush, not merely because
LevelRootappeared.
2a. RMB Camera Navigation Ownership¶
- Hold RMB and move the mouse with each of these selected: nothing,
LevelRoot, a brush, an object/entity, a face, aCamera3D, and a light. Confirm Godot's camera looks around immediately in every case and selection remains unchanged. - While holding RMB, use W/A/S/D camera flight, Ctrl+Arrow, Escape, and LMB/MMB/wheel input. Confirm no HammerForge nudge, cancel step, tool switch, selection change, drawing, or other edit action occurs before RMB is released.
- Repeat in Draw, Select, Paint, Vertex, Measure, Polygon, Path, and Decal modes. Persistent modes must not claim plain RMB while idle.
- Open a G G/B B/R R quick-property popup, then begin RMB camera navigation outside it. Confirm the popup closes and the same press moves the camera; repeat with MMB pan and mouse-wheel zoom.
- Start a Draw, Extrude, Face Select marquee, or vertex-drag interaction, then press RMB. Confirm only the active interaction is cancelled and a second RMB press starts normal camera navigation.
- During an active LMB paint stroke, press RMB and confirm the stroke retains pointer ownership rather than painting while the camera moves. Release LMB and RMB, then press RMB again and confirm camera navigation resumes.
- While Polygon or Path has points in progress, confirm RMB steps back that active interaction. Once the tool is idle, confirm RMB navigates the camera.
- In Measure mode, confirm plain RMB always navigates and Ctrl+Click near a ruler sets its snap reference.
- Press Space and confirm it opens the HammerForge context menu. Plain RMB must never open that menu.
2b. Select, Gizmo, and Native Object Ownership¶
- Select a brush, then click empty viewport space. Confirm Godot's visible Scene-tree selection becomes empty, the HammerForge selection count becomes zero, and a later dock refresh or texture import does not resurrect the brush.
- Create a box, wedge, pyramid, cone, cylinder, sphere, and one custom/merged brush. In Object Select, click the visible interior of each shape. Confirm Godot's native selection outline and Scene-tree selection update from the filled face target—not only when clicking a thin wire edge—and HammerForge reports the same selection.
- Aim through empty space inside a wedge/cone/custom brush's bounding box with another object behind it. Confirm the empty bounds do not select or occlude the near brush and the real visible surface behind can be selected.
- Begin on empty viewport space and drag an object marquee. Confirm Godot's native region rectangle and hit rules are used, the Scene tree and HammerForge selection count agree after release, and no second HammerForge rectangle/result is applied.
- Start the same native object marquee while holding Shift and confirm the new results are added using Godot's normal native behavior. Shift-click selected and unselected objects and confirm Godot's native additive/active-selection result is preserved after HammerForge owner/group normalization.
- Group two brushes, then use the native object marquee to include one member. Confirm both group members appear selected after the native result is normalized. Repeat the relevant Shift selection/removal interaction and confirm normalization never leaves only half the group selected.
- Hold Ctrl/Cmd during an Object Select click and drag. Confirm HammerForge neither performs a custom toggle nor consumes the modifier; Godot's configured transform/navigation behavior remains authoritative.
- Stand five pillars in a row on a floor. Hold I and drag across three of them in Select. Confirm those three are selected and the floor is not. Start a second sweep on a selected pillar and confirm the ones it passes leave the selection. Start one more and press Escape mid drag: the selection goes back (#1886).
- Begin in Draw or another Paint tool with Paint enabled, select two brushes, then enable Face Select. Confirm HammerForge switches to Select, turns Paint off, temporarily clears the object selection, and removes Godot's transform widget plus HammerForge's resize handles. Drag at least 6 pixels across several visible faces; confirm one HammerForge rectangle appears and only projected-center candidates whose canonical ray pick identifies that exact face as frontmost are selected. With X-ray off, put a face behind another brush and confirm the occluded/back-facing face is excluded. Shift adds and Ctrl/Cmd toggles.
- Press Escape with faces selected; confirm only the face selection clears. Press Escape again and confirm Face Select exits and both still-valid brushes return to Godot's visible object selection. Re-enter with no face selected and confirm one Escape exits. Also confirm leaving Paint, choosing an incompatible built-in/external tool, or entering Vertex Edit exits through the same clean restore path. Re-enter once more, then select a different object in the Scene tree; confirm Face Select turns off and keeps that new object rather than restoring the old snapshot.
- Select a box brush, then drag each HammerForge resize handle well beyond the normal marquee threshold, across empty space and another brush. Confirm the handle owns the full press/motion/release, the box resizes without any region rectangle or selection change, and each completed drag creates exactly one undo step. Repeat with Godot's move/rotate/scale widget. A click/no-op and a cancelled handle action must create no undo entry.
- Select a sphere and drag an X, Y, then Z handle; confirm every resize keeps X/Y/Z equal. Repeat with a cylinder, cone, and capsule: X or Z must update both radial dimensions together, while Y changes height without changing the radius. Try to shorten the capsule below its diameter; confirm it clamps at the diameter and the opposite face stays fixed.
- Drag a resize handle on a sphere, an ellipsoid and a torus. Confirm the brush keeps up with the mouse rather than trailing it (#852).
- Drag the side and top handles of a capsule, one as tall as it is wide and one stretched. Confirm it keeps up with the mouse as the sphere does, and that dragging the top past the width grows a straight middle without a hitch (#860).
- Place a cylinder, give its top cap one material and one side another, then change Sides in the Inspector from 16 to 24 and back to 16. Confirm the cap keeps its material, the sides facing the painted one's way take its material, and no face falls back to the default (#851).
- Parent a box below a rotated
Node3Dwith non-uniform scale (for example2, 3, 0.5). Drag all six handles with grid snap enabled. Confirm each grabbed face moves in snapped world units along its transformed local axis, the opposite face remains stationary in world space, and the brush does not jump on first motion. Temporarily collapse one parent scale axis near zero (for example0.000001) and confirm its corresponding handle is ignored without NaN/invalid geometry. - With a brush selected, drag Godot's move/rotate/scale widget. While still dragging, press Ctrl+Arrow and then test Escape in a separate drag. Confirm Godot owns the complete mouse/keyboard gesture and no HammerForge click, marquee, nudge, paint, or external-tool action also fires. Complete a move, run Bake Changed, then Undo/Redo the native move and confirm each transition makes that exact brush eligible for Bake Changed again.
- Select a
Camera3Dand a light, then use their native viewport gizmos. Confirm they remain selectable/editable by Godot while the HammerForge dock stays connected to the existingLevelRoot; no brush behind the native object is selected through the gizmo. - Shift-select one HammerForge brush and one ordinary Godot
Node3D. Try Delete, duplicate, group, nudge, hollow, clip, carve, merge, apply texture, quick prefab, and variant actions through the keyboard, context toolbar, viewport context menu, hotkey palette, and any applicable radial sector. Confirm managed choices are hidden/disabled where appropriate; every invoked path leaves all nodes unchanged, shows “Edit HammerForge and Godot nodes separately”, and performs no partial edit. With only the native node selected, confirm keyboard shortcuts pass through; with only the brush selected, confirm HammerForge owns its managed action. - Give keyboard focus to an unrelated editor
Tree,ItemList,GraphEdit, text field, or dialog and press Delete/Ctrl+D plus a HammerForge tool shortcut. Confirm the focused editor surface keeps the keys and no level node is deleted, duplicated, nudged, or switched into a tool. Repeat from the 3D viewport, the actual Scene tree, and a HammerForge surface to confirm their intended shortcuts still work. - Hold Alt and drag with LMB over empty space, a selected brush, and a native object. Confirm the gesture stays with Godot's configured alternate navigation/transform behavior and never begins HammerForge selection.
- In Face Select, tick X-ray beside Face Select Mode, or press Alt+X, and drag a face marquee across a room from outside it. Confirm the rectangle is amber, the HUD says X-ray, and the inside walls and the faces turned away are taken too. Untick it and confirm covered faces are left out again (#1432).
- Pick one top face of a row of touching floor slabs and press Ctrl+] twice. Confirm the selection spreads across the faces that share an edge, slab to slab. With Same plane only ticked under Selection Filters it stays on the floor. Press Ctrl+[ and confirm it takes the edge back off. Select one step of a stair and press Ctrl+]: the touching steps join, and a turned brush whose box reaches a step but whose shape does not is left out. Confirm the bare [ and ] still halve and double the grid (#1433).
- Pick faces on two brushes, then delete one of the brushes from the Scene dock. Confirm the HUD and the dock count only the faces left. Pick faces, give them a material and press Ctrl+Z: confirm the dock's count and Surface Paint panel follow the undo at once (#1515).
- Select a brush and press Alt+D. Confirm width, depth and height labels sit on the edges of its bounds, coloured by axis, and follow a nudge, a handle drag and an undo. Press Alt+D again to hide them (#1403).
2c. Recovery, Picking, and Tool Exclusivity¶
- Start a native object marquee, then Alt+Tab or release LMB outside the viewport. Return and move the mouse with no buttons held. Confirm Godot's rectangle and HammerForge's native-session bookkeeping both clear, the visible
EditorSelectionremains authoritative, and the next click/drag works normally. - Repeat with a HammerForge Face Select marquee. Confirm its blue rectangle clears without applying a stale face selection.
- Start a resize-handle drag, move outside the viewport, and lose LMB-up. On the first buttonless motion, confirm the brush restores its captured size/position and creates no undo entry. Continue moving and confirm the preview stays frozen rather than reappearing. After the recovery turn, verify a fresh handle drag works even if Godot never delivered the old release; if the old release arrives late, confirm it neither mutates the brush nor creates undo.
- Repeat the lost-release scenario during a vertex drag. Confirm the unfinished move is cancelled back to the captured geometry and the next vertex gesture works.
- While a Godot transform/property gizmo or HammerForge resize handle is active, change application/window focus. Confirm Godot settles its own gizmo exactly once, HammerForge does not race it with a second restore/commit, and the next widget gesture works with one owner and one undo result.
- Start native RMB look, change focus, and release RMB outside the viewport. Return and move with no buttons held, then begin a fresh RMB look. Confirm HammerForge does not leave navigation or editing latched.
- Start a floor/surface/displacement paint stroke, lose its LMB release, then press RMB with LMB physically up. Confirm the stale stroke is safely finished and the following RMB starts normal camera navigation.
- Close a Polygon and begin its height drag, then release LMB outside the viewport and return with no button held. Confirm the height commits once on recovery and a later unrelated click/release cannot commit it again. Repeat by changing application focus during height placement; confirm the tool returns to its editable point loop instead of leaving a hidden active drag.
- Put a brush and an entity preview on the same camera ray at different depths. Confirm clicking chooses whichever visible surface is actually nearest; swap their depths and repeat. Non-uniformly scale one visual and confirm ordering remains based on world distance.
- Select several normal DraftEntity types whose preview mesh/icon is an internal editor child. Confirm clicking the visible preview selects the entity rather than requiring a click near its origin.
- Give an entity an authored name that differs from its node name, aim an output at the authored name, then delete it. Confirm the connection goes. Do it again with a second entity sharing that authored name and confirm the connection stays, because it still resolves.
- Select one or more
DraftEntitynodes. Press Ctrl+D, nudge with Arrow/PageUp/PageDown, undo/redo, then Delete. Confirm duplication uses unique names and preserves entity data plus group/visgroup membership, nudging moves the entities through one managed action, and deletion removes group/visgroup membership plus dangling I/O targets. Repeat with a mixed brush/entity HammerForge selection and confirm both domains edit together without leaving stale selection entries. - Drag two Point Lights from the Entity palette into the viewport. Confirm the Scene dock names the second
DraftEntity2, not@Node3D@.... Wire the first to the second, Save .hflevel, then Load .hflevel: the wire still names the second light (#1179). - Create a truly geometry-less
DraftEntity. Click its one-unit origin area and include it in a native marquee; confirm native selection works, the selected gizmo is only a quiet box marker, and no resize handles appear. Add a valid preview mesh beside a null or line-only preview child and confirm both visible components contribute to the entity's pick/outline bounds. Mark a preview child top-level below a transformed entity and confirm its target remains at the visible world position. Then hide the preview or an ancestor of the entity and confirm no invisible marker remains clickable. - Put a near brush/entity in a hidden visgroup with visible geometry behind it. Confirm object click, Select-mode hover, entity selection, face selection, and extrude face hover ignore the hidden item and reach the nearest visible candidate. A hidden entity preview must not leave an invisible clickable sphere.
- Place a wedge or cone above the construction plane and aim at both a real sloped/curved surface and empty space inside its bounds. Drop an entity/prefab or start a surface-based placement at each point. Confirm the first lands on the exact visible face, while the empty-bounds ray continues to real geometry behind or the forward construction plane rather than landing on an AABB wall.
- Activate Polygon, Path, Measure, and Decal in turn. For each, click once where the tool intentionally misses or passes; confirm the same physical event does not also select, draw, paint, or move a vertex. Switch to a built-in tool and confirm the external tool deactivates and any unfinished preview is cancelled.
- While an external tool is idle and deliberately passes RMB/MMB/wheel input, confirm native camera navigation remains available without another HammerForge tool receiving the event.
2d. Geometry Snap Modes¶
- Draw two offset brushes and toggle the G, V, C, E, and P buttons independently in Build. Confirm the viewport indicator and dock state agree after each toggle.
- With Vertex enabled, drag near a brush corner, a wedge's sloped corner included; with Center enabled, drag near its center; with Edge enabled, drag near an edge's midpoint. Confirm each candidate wins only within the snap threshold.
- With Perpendicular enabled, drag near a brush edge and confirm the point lands at the closest projection on that edge. Combine modes and confirm the closest eligible geometry candidate wins over a farther grid point.
- With G on, start a draw, drag a resize handle and drag a vertex, each with Ctrl held for part of the drag. Confirm the point leaves the grid while Ctrl is down and the HUD says Snap off. Turn G off and confirm Ctrl brings the grid back, with Snap on (#1431).
- With V on, drag a vertex of one brush near a corner of another. Confirm it lands on that corner within about 10 pixels with a marker on it, and the other selected vertices move by the same amount. Turn V off and hold Alt for the same. Drag a resize handle near another brush's corner and confirm its face stops level with it (#1399).
- Press Alt+Left on a selected brush and confirm it moves a tenth of the grid step. Alt+Shift+PageUp moves it up a tenth, while Alt+PageUp alone still moves the drawing grid up.
- Draw a box at a finer snap so it sits off the grid, and turn a second one 30 degrees. Select both and press Ctrl+B. Confirm each one's bounds' minimum corner lands on the grid with its size and turn kept, as one undo step, and that pressing it again records nothing. In vertex mode, Ctrl+B moves the selected vertices to the grid instead, and a snap that would dent the brush is refused with the reason (#1398).
- Rest the pointer on a box's corner for half a second. Confirm a small plus marks it and dotted guides run from it along X, Y and Z. Start a draw near one of its guides across the room and confirm the point lands on the guide, marked with a bar, and the word beside the marker says how far along it is (#1692).
- Mark a second point and place near where the two points' guides cross. Confirm the crossing wins, marked with an X. Rest on a marked point again and confirm it lets go; switch tool and confirm every guide goes.
3. Guided Step Flow¶
- Step 1: draw an additive room brush; confirm the tutorial advances.
- Step 2: click Test Level Now; confirm the guide starts the normal check, bake, spawn-validation, and play flow.
- Confirm
bake_finished(false)does not complete the tutorial. - Confirm only a successful bake completes the tutorial.
- Confirm the completion state auto-closes after the short delay.
4. Tutorial Resume¶
- Close Godot.
- Run
prepare_editor_smoke.gdwith--tutorial-step=1. - Reopen
hf_editor_smoke_start.tscn. - Confirm the tutorial resumes at the saved step instead of restarting from step 1.
5. Shortcut Dialog¶
- Click the
?shortcut button in the dock toolbar. - Confirm the dialog opens at editor size without visual clipping.
- Type a filter such as
paint; confirm only matching actions remain visible. - Clear the filter; confirm categories repopulate correctly.
- Confirm a Transform category lists Rotate CCW, Rotate CW, Flip and Reset Rotation, the same shortcuts the Ctrl+K palette shows (#906).
- Close the dialog with both Enter and Escape paths.
- Bind an action to
F1,Ctrl+K,EscapeorAlt+Leftinuser://hammerforge_keymap.jsonand restart the plugin. Confirm a warning on load names the key, and the shortcut dialog marks the binding in amber with what else takes the key (#1614). - Bind Paint Bucket to
Ctrl+D. Confirm no clash is reported, that in Paint mode Ctrl+D buckets and outside it duplicates. Bind it toQinstead, which answers in every mode, and confirm the clash is reported (#1614). - Bind Operation Replay Timeline to
Ctrl+Shift+Oand Run Trim Along Faces toCtrl+Shift+T. With Power-user overlays on, confirm Ctrl+Shift+O toggles the timeline, Ctrl+Shift+T runs the trim, and the shortcut dialog shows both (#1608).
6. Prefab Save + Drag/Drop¶
- Select at least one brush or entity.
- In
Test -> Prefabs, save a prefab with a unique test name. - Confirm the prefab list refreshes and the new item appears.
- Drag the prefab from the list into the 3D viewport.
- Confirm the prefab instances at the snapped placement point.
- If the prefab contains only entities, confirm the placement still succeeds.
- Create a prefab containing solid, pending cut, and committed cut brushes plus a tied brush entity. Confirm tagging, source updates, and removal affect every member rather than leaving nodes in PendingCuts or CommittedCuts.
- Tie two brushes as one
func_doornamedgate, tie a third as afunc_buttonwiredOnPressedtogateOpen, and save all three as a prefab. Place it twice. Confirm each placed door has a name of its own, each placed button's wire names its own door, and Export .map writes three doors (#1158, #1159). Ctrl+C and Ctrl+V the source door: the pasted one is a fourth door. - Save the
gateprefab with Save Linked and place it three times. Select the third copy, click Push, then Pull. Confirm the first copy's door is stillgate_2and the third'sgate_4, every leaf keeps its node name, and a button outside the prefab wired togate_2still opens it in a playtest (#1168, #1178). - Undo the placement; confirm the full prefab placement is reverted.
- Redo the placement; confirm it is restored.
- Select the prefab in the library, point at the floor in the viewport and press Space. Confirm the menu offers Place
Here and that choosing it puts the prefab where the cursor was, as one undo step named Place Prefab:. Deselect it in the library, press Space again, and confirm the item is gone (#1301). - Drag a brush preset from Test → Presets onto a face in the viewport. Confirm the brush lands there and takes the Active Material (#1580, #1405).
- Save a selection from the Prefabs panel with Save Linked, then press Ctrl+Z. Confirm the selection is no longer linked and the prefab file stays; Ctrl+Shift+Z links it again. Name a brush preset
CON, then..x: confirm each is saved, listed under the name typed, and loads again (#1451, #1464).
6b. Named Groups¶
- Select two brushes and press Ctrl+G. Confirm it asks for a name with one ready, and Enter makes the group. Make a second group, select both groups and press Ctrl+G again. Confirm a click on any piece selects all of them, and the Groups list shows the two inner groups indented under the new one (#1723).
- Double click the outer group. Confirm the rest of the level is drawn faded and a click picks one inner group. Press Escape and confirm the group closes and is selected.
- Open a group of two, with a third brush outside it. Select both members and Ungroup. Confirm the group closes and nothing stays faded. Group them again, open it, and press Ctrl+Z: nothing stays faded (#1926, #1928).
- Press Ctrl+D on the whole outer group. Confirm the copy is a group of its own, named with "copy", and a click on it selects only the copies.
Empty Apply Cuts¶
- With no pending cuts, click Apply Cuts and confirm no undo step appears. With cuts pending, apply them and confirm one Apply Pending Cuts step. Undo and confirm the pending cutters return.
- Draw a Cutout and select it. Confirm Operation is read-only in the Inspector while the cut is pending, and the cut is orange. Apply it: Operation becomes editable and the cut draws red. Ctrl+Z: it is orange and read-only again, and stays so through a save and reopen (#1531, #1551).
7. Cut Preview¶
- Open
Test -> Settings. - Toggle
Subtract Previewon. - Create a solid brush and a cut brush with real overlap.
- Confirm a red CSG cut volume (or AABB wireframe fallback) appears on the overlap.
- Move or resize either brush; confirm the preview updates.
- Toggle
Subtract Previewoff; confirm the wireframe is removed. - Tie the solid brush as
func_detail, thenfunc_wall, thenfunc_clip. Confirm the preview shows no cut through it each time. Untie it and tie the cutter asfunc_detailinstead: confirm it cuts nothing (#1563). - Tie a crate brush as
func_physboxand a thin sheet asfunc_illusionary. Bake and Quick Play: walking into the crate pushes it, and the sheet is seen and walked through. Confirm both are left out of the baked navmesh (#1892).
7b. Geometry Previews (Carve/Clip/Hollow)¶
- Select a solid brush overlapping another. Press Ctrl+Shift+R (Carve).
- Confirm green wireframe overlay appears showing the resulting slice pieces.
- Confirm a confirmation dialog appears ("Carve N brush(es)?").
- Click Cancel; confirm the preview disappears and no geometry changes.
- Repeat and click OK; confirm the carve executes and preview clears.
- Select a brush and press Shift+X (Clip).
- Confirm cyan wireframe for both halves and an orange semi-transparent plane at the split.
- Confirm a confirmation dialog appears. Cancel and verify nothing changes. OK and verify the split.
- Select a brush and press Ctrl+H (Hollow).
- Confirm yellow wireframe shows 6 wall pieces overlaid on the original brush.
- Confirm a confirmation dialog appears. Cancel and verify the original is unchanged.
- OK and confirm the brush is replaced by 6 wall brushes.
- Sculpt the top of a box, then carve a corner off it with a carver turned 45 degrees about Y. Confirm the Carve confirmation adds "1 sculpt cannot follow this cut and will be dropped" before anything is cut, and that a carver square to the box shows no such line (#871).
- Select a box and a cylinder that overlap and press Ctrl+Shift+I (Intersect). Confirm a green preview of the volume they share and a confirmation. OK: one brush is left where they overlapped, its faces on the cylinder's curve wear the cylinder's look, and one Ctrl+Z brings both back. Try two brushes that do not overlap and confirm it is refused with no undo step (#1426).
7b-2. Bulk Delete Confirmation¶
- Select 3+ brushes (marquee or Shift+click).
- Press Delete.
- Confirm a confirmation dialog appears ("Delete N brushes? This can be undone with Ctrl+Z.").
- Cancel; confirm nothing is deleted.
- Repeat and OK; confirm all selected brushes are deleted.
- Ctrl+Z; confirm all brushes are restored.
- Select 1-2 brushes and press Delete; confirm immediate deletion (no dialog).
7b-3. Carve UV Preservation¶
- Apply a distinct grid texture to a large solid brush.
- Place a smaller brush overlapping the textured brush.
- Carve (Ctrl+Shift+R) the small brush. Confirm in the dialog, then confirm the carver is deleted and the target splits into slice pieces.
- Inspect each surviving slice: confirm the grid texture is aligned with the original — no visible seams or jumps at slice boundaries.
- Undo the carve; confirm the original brush and carver are restored.
7c. Merge Brushes¶
- Create two box brushes with different materials, side by side.
- Select both brushes (Shift+click or marquee). Press Ctrl+Shift+M (or click "Mrg" in context toolbar).
- Confirm: originals deleted, one merged brush appears where the two were, centred on them with the size of both, with faces from both.
- Select the merged brush and bake; confirm both materials are visible on the baked mesh (no material loss).
- Undo; confirm both original brushes are restored with their materials.
- Create two brushes, rotate one 90 degrees, then merge. Confirm the rotated brush's geometry is correctly oriented in the merged result.
- Try merging a single brush; confirm an error toast appears ("Select at least 2 brushes").
- Try merging a solid brush with a cut brush; confirm rejection ("all brushes must have the same operation type").
7c-2. Free Transform (Rotate, Flip, Reset)¶
- Draw a box that is clearly not a cube (say 64 x 32 x 16) and select it.
- Press R a few times. Confirm it turns about Y in the step shown in Selection Tools, and that the brush outline and resize handles turn with it.
- Press Shift+R the same number of times. Confirm it returns to where it started.
- Set the axis lock to X, then press R. Confirm it now pitches instead of yawing. Clear the lock.
- Change Pivot to World Origin and press R. Confirm the brush orbits the origin rather than turning in place.
- With Texture Lock on, yaw a textured box 90 degrees. Confirm all six faces carry their texture round with the brush, upright as it was, and that none of the four walls comes back mirrored. Yaw four times and confirm the textures are back exactly as they started. Turn Texture Lock off and yaw again: the texture should keep its place in the level while the brush turns under it.
- Bake the rotated brush. Confirm the geometry is solid and textured from outside, not inside-out.
- Press Shift+M to flip. Confirm the brush mirrors across X and still renders from outside. Press Shift+M again and confirm it returns exactly.
- Repeat the flip on a wedge. Confirm the mirrored wedge slopes the other way, still renders correctly, and its shape reads as Custom in the dock.
- Select several brushes and an entity together, then rotate and flip. Confirm they move as one group and that a
player_start's angle follows the rotation. - Tie a 0.2 x 2.2 x 1.6 box to
func_doorin a wall running north to south, and turn it 90 degrees with Godot's rotate gizmo rather than R. Bake and playtest: the door slides along its new wall. Turn aplayer_start90 degrees with the gizmo and click Test Level: the player faces the way it points. Ctrl+Z each and confirm the angle goes back (#1256, #1285). - Rotate a box 30 degrees, then Hollow (Ctrl+H), Clip and Carve it in turn. Confirm each works in the brush's own frame and every piece keeps the turn.
- Press Alt+R (Reset Rotation) on a brush turned exactly 90 degrees. Confirm the brush does not move or change size on screen.
- Press Alt+R on a brush turned by an odd angle. Confirm it snaps to axis-aligned.
- Create a displacement on a face, then press Shift+M. Confirm the sculpt is mirrored with its face, not refused and not corrupted.
- Undo each of the above with Ctrl+Z and confirm the brush returns to its previous transform.
- Select a box off the origin, tick X and Selection Edge in the Mirror row under Selection Tools > Transform, and press Alt+M. Confirm a mirrored copy lands flush beside the box, both are selected, and one Ctrl+Z removes the copy. Tick X and Z and press it again: three copies. Untick every axis and press it: a toast and no undo step (#1434).
- Select a brush turned 30 degrees and press Alt+G. Type 2 in X, choose Local and press Enter. Confirm it moves 2 along its own X as one undo step. Press Alt+G then Enter again: it repeats. In vertex mode, offset a selected vertex the same way, and confirm an offset that would dent the brush is refused with the reason (#1441).
- Press Alt+P and click a corner of another brush. Confirm a cross marks the point, Pivot reads Pick, and R and Shift+M turn and flip about it. Save and reload: the cross is still there. Press Shift+Alt+P: the cross goes and the pivot is the selection again. Arm Pick, then press Escape: nothing is picked (#1442).
- Turn a box 90 degrees and stretch it with Godot's scale gizmo, then press Apply Rotation and Scale in the Transform row. Confirm nothing moves, the Inspector reads no rotation and a scale of 1, the size reads in metres, and it is still a box with handles. Do the same on a sphere stretched along X: it becomes an ellipsoid. On a box turned 30 degrees it becomes Custom and the toast says so. Each is one undo step (#1435).
7c-3. Array Layouts¶
- Select a brush offset from the origin. In Selection Tools, set Layout to Radial.
- Set count to 5, axis Y, tick Fill 360, and click Create Array. Confirm six evenly spaced copies form a closed ring and each copy faces outward rather than all facing the same way.
- Click Remove Array; confirm only the original remains.
- Set Layout to Grid, cells 3/1/3, X/Y/Z offset 64/0/64, and click Create Array. Confirm a 3x3 lattice of eight copies plus the original.
- Switch back to Linear and confirm the offset row returns and the old behaviour is unchanged.
- Confirm the Radial and Grid rows only appear for their own layout.
7c-3b. Editing an Array After You Have Made It¶
- With Linear selected, count 3, click Create Array. Confirm the button now reads Update Array and a Detach button has appeared beside Remove Array.
- Click one of the copies (not the original). Confirm the section still reads Update Array, shows that array's layout and numbers, and that the ghost appears over it.
- Raise the count to 6 and click Update Array. Confirm there are now six copies and the button still reads Update. Lower it to 2 and press Update again without reselecting; confirm two copies.
- Press Ctrl+Z. Confirm the array goes back to the copy count it had before the update.
- Switch Layout to Radial while a copy is selected, set count 5 and step 45, and press Update Array. Confirm the same array becomes a ring rather than a second array appearing beside the run.
- With a copy of a radial array selected, raise the count and press Update. Confirm the ring stays centred where it was rather than moving to the copy you clicked.
- Click a copy and press Detach. Confirm nothing is deleted, the button goes back to Create Array, and Detach disappears. Click a copy again and confirm the section stays on Create.
- Make another array, click a copy, and press Remove Array. Confirm the copies go, the original stays, and there is no "not a duplicator source" message.
- Make a linear array, select one of its copies and make an array of that copy. Detach the second array, press Ctrl+Z, then Ctrl+Shift+Z. Click the copy you seeded it from and confirm the section shows the first array, Update Array and all (#1309).
- Select an unrelated brush and confirm the section returns to Create Array.
- Make a linear array of four. Drag one copy well off the row, then click another copy. Confirm the section says one copy has been moved by hand and names Detach.
- Raise the count and press Update Array. Confirm nothing rebuilds and the status line asks you to press it again; press it again and confirm the rebuild happens and the message clears.
- Drag a copy off the row again, press Update once (warned), then change the count and press again. Confirm the changed numbers have to be agreed to a second time as well.
- Drag the original instead of a copy. Confirm the message reads "The original has moved" with no mention of Detach, and that Update goes ahead on the first press.
- Resize one copy without moving it, then click another copy. Confirm the section still counts it as edited even though nothing has moved.
- Apply a different material to one copy and confirm the same. Then apply a material to the original instead and confirm the section stays quiet — the copies still agree with each other.
- Paint every face of a box, make a linear array of three, and paint a stroke on one copy inside the layer it already has. Click another copy, raise the count and press Update Array. Confirm nothing rebuilds on the first press and the section counts one edited copy; press Detach instead and confirm the stroke is still there (#875).
7c-3c. Re-hollowing¶
- Draw a box 4 m a side, leave Wall at its 0.25 default, press Hollow (Ctrl+H) and confirm.
- Click one of the walls. Confirm the button now reads Re-hollow, a Detach button has appeared, and the Wall spinbox shows 0.25.
- Set Wall to 0.75 and press Re-hollow. Confirm the room is rebuilt with thicker walls and the button still reads Re-hollow. Press it again at 0.5 without reselecting and confirm that works too.
- Set Wall to 2.5 (more than half the box) and press Re-hollow. Confirm it is refused with a message and the walls you had are still there.
- Select all the walls, drag the room well across the level, click one wall and press Re-hollow. Confirm the room rebuilds where you dragged it, not back at the origin.
- Drag a single wall away from the others, then Re-hollow. Confirm the room rebuilds at its old placement instead of following the one wall.
- Press Ctrl+Z and confirm the previous walls come back.
- Click a wall and press Detach. Confirm nothing is deleted, the button goes back to Hollow (Ctrl+H), and clicking a wall again leaves it there.
- Hollow a fresh box. Drag one wall well away from the others, then click another wall. Confirm the row says one wall has been reworked by hand and names Detach.
- Change the Wall value and press Re-hollow. Confirm nothing rebuilds and the status line asks you to press it again; press it again and confirm the rebuild happens and the message clears.
- Rework a wall again, press Re-hollow once (warned), then change the Wall value and press again. Confirm the changed thickness has to be agreed to a second time as well.
- Resize a wall without moving it, then click another wall. Confirm the row still counts it. Apply a different material to a wall and confirm the same.
- Paint every face of a fresh box, hollow it and click a wall. Confirm the row is quiet. Paint a stroke on one wall in the layer it already has, then click another wall. Confirm the row counts that wall.
- Select every wall and drag the whole room across the level. Confirm the row stays quiet and Re-hollow goes ahead on the first press.
- Hollow a fresh box, paint a stroke on one wall, then save the scene, close it and open it again. Click an untouched wall. Confirm the row counts exactly one wall, not all six (#873: a reopen used to count every wall of a box hollow).
- Open a level saved before October 2026 that has a box hollow you never edited. Click a wall. Confirm the row says nothing and Re-hollow goes ahead on the first press (#878).
- Rework a wall, press Re-hollow once to see the warning, then press Detach. Confirm the message goes and nothing is deleted.
- Hollow a box with Wall at 0.05. Confirm six Custom walls 0.05 thick, and no "cannot be built" warning in the Output panel. Save, reopen and confirm the walls are still 0.05 thick (#1598, #1592).
7c-4. Precision Cutting (Clip and Carve on Any Brush)¶
- Draw a box, rotate it 30 degrees (
R), then press Shift+X. Confirm the cyan preview shows two turned pieces, and that committing leaves two brushes that both keep the rotation. - Draw a cylinder and clip it. Confirm the preview outlines the real curved halves, not two boxes, and that the committed pieces look right from every angle.
- Clip an ordinary axis-aligned box so both pieces are at least 0.1 on every side. Confirm both pieces still have resize handles (they are still boxes).
- Clip a box on a diagonal via Clip to Face Plane. Confirm each piece reports as Custom in the dock and has no box resize handles.
- Bake after each of the above. Confirm no piece renders inside out — look at the cut surface in particular, from both sides.
- Enter Face Select, click an angled face on a rotated brush, select a different brush, and press Shift+Alt+X. Confirm the second brush is cut along the first one's face plane and the reference brush is untouched.
- With no face selected, press Shift+Alt+X. Confirm a message asks for a face rather than cutting anything.
- Carve with a rotated carver, then with a cylinder carver. Confirm the green preview outlines the real resulting pieces and that the committed result matches it.
- Carve a brush that has already been clipped on a diagonal. Confirm it cuts and bakes correctly.
- Position a carver so it fully contains a small brush and carve. Confirm the small brush is left alone rather than disappearing.
- Undo each cut with Ctrl+Z and confirm the original brush returns whole, with its material and per-face textures intact.
- Select a brush with per-face textures, clip it, and confirm each piece kept the textures on the faces they were painted on, and that the new cut surface took the nearest face's material.
- Resize one of those pieces with a handle, then save and reopen the scene. Confirm every face still has its own texture (#863: a clipped box used to move its textures to other faces here).
- Surface-paint a few faces of a box, sculpt its top, and clip it straight across. Confirm both pieces keep their paint where it was, the cut surface is unpainted, and both halves of the top keep the sculpt with no step at the cut. Paint on one piece and confirm the other does not change.
- Clip a corner off a sculpted top. Confirm the Output panel warns that the sculpt could not be kept, naming the brush, and that Ctrl+Z brings the brush back painted and sculpted.
- Carve a doorway through a painted wall, and hollow a painted box. Confirm the paint stays on the outside of every piece and wall.
- Set every face of a box to Cylindrical UVs, clip it across Y and then clip one piece across X. Confirm no texture slides or stretches on any piece, the faces the cut did not touch included (#868).
- Select a box that overlaps nothing and press Carve. Confirm a toast says there are no overlapping brushes, no dialog opens, and the undo history shows no Carve step (#1267).
- Clip a 2 m box 0.05 above its bottom face. Confirm the thin piece is Custom with no resize handles, keeps 0.05 through a save and reopen, and a nudge with Texture Lock on leaves its side textures the size they were (#1598, #1592).
7c-5. Structures (Hollow and the Arch)¶
- Hollow an ordinary box. Confirm the yellow preview outlines six walls, that the confirmation names the wall count, and that committing leaves six brushes that tile the original.
- Rotate a box 30 degrees and hollow it. Confirm the preview follows the rotation and the walls come out rotated with it.
- Hollow a cylinder. Confirm the preview shows a ring of walls, the confirmation warns how many, and the result is a tube. Bake it and walk inside — confirm no wall renders inside out.
- Try hollowing a torus. Confirm it refuses promptly, saying the brush is not convex, and that the torus is untouched. It must not hang.
- Try a wall thickness larger than the brush. Confirm the refusal suggests a thickness that fits.
- Open the Structure section in the Build tab with Type on Arch. Create an arch with the defaults. Confirm eight brushes appear as a half arch centred on the origin.
- Select a brush somewhere off-origin and create another arch. Confirm it lands centred on the selection.
- Set Arc to 360 and Segments to 12, and create. Confirm a closed ring with no gaps between segments.
- Set Arc to 360 and Segments to 2, and create. Confirm it refuses and names the minimum segment count.
- Bake an arch. Confirm every segment is solid and textured from outside, including the flat radial faces where segments meet.
- Carve a doorway through an arch and bake again. Confirm the cut segments still render correctly.
- Rotate an arch upright with
R, then hollow one of its segments. Confirm both operations work on generated geometry. - Draw a single step box offset from the origin. Set Duplicate Array to Radial, count 12, step 30 degrees, Rise 16, and Create Array. Confirm a ring of turned, raised copies. (For a real spiral staircase use the Spiral Stairs structure in 7c-6; this is the array tool, and it repeats a shape rather than computing one.)
- Set Rise back to 0 and create again. Confirm a flat ring, exactly as before the rise existed.
- Undo each of the above and confirm the level returns to its previous state.
7c-6. The Structure Section (Types, Stairs, Spiral Stairs, Domes)¶
- Open the Build tab and find the Structure section. Confirm the Type dropdown lists Arch, Stairs, Spiral Stairs, Dome, Ring, Door Frame and Trim Run, and that Arch is chosen.
- Change Type to Stairs. Confirm the settings below change to Width, Tread, Height from, Total, Rise, Steps, Fill, Slab, Nosing, Collision, Turn, Before landing, Landing and Gap, and that the button reads Create Stairs.
- Press Create Stairs. Confirm eight steps appear, each one a tread further on and a rise higher than the last, with no gaps between them.
- Set Fill to Open and create another. Confirm the treads float and you can see under them.
- Change Type to Spiral Stairs and create one. Confirm twelve treads climb around a newel post, and that each tread is a wedge rather than a rotated box.
- Set Inner R to 0 with the newel post on and press Create. Confirm it refuses with a message naming the inner radius, and creates nothing.
- Turn the post off and create again. Confirm the treads meet at the axis.
- Change Type to Dome and create one. Confirm a faceted hemisphere of 48 panels appears. Look at it from inside: confirm no panel is inside out and there are no gaps between rings.
- Set Sweep to 45 and update. Confirm the crown is open.
- Set Wall equal to Radius and create. Confirm the dome is solid rather than a shell.
- Set Rings to 32 and Segments to 32 and press Create. Confirm it refuses, names 1024, and creates nothing.
- Change Type to Ring and create one. Confirm a closed ring of sixteen pieces lies flat, its tube eight-sided. Set Sweep to 90 and Update: confirm a quarter ring. Set Tube Radius to the Radius: confirm it refuses and changes nothing.
- Draw a wall 6 wide, 3 high and 0.2 thick standing on the floor, select it, change Type to Door Frame and create one. Confirm two posts and a lintel appear in the wall with a pending cut between them, and move the frame down so its foot is on the floor. Apply Cuts: confirm the opening goes through the wall. Set Width to 1.5 and Update: confirm the frame and the opening widen together and the cut stays applied. Set Head to Arched and Update: confirm the arch sits on the posts with no gap and the opening follows its curve. Run Validate: confirm it reports no doorway problem, then set Height to 1.2, Update, and confirm it reports a doorway the player has to crouch through.
- With Arch chosen, set Radius to 5 and press Save beside Preset. Name it Wide and confirm the Preset row shows Wide. Change Type to Dome and back: confirm Radius still reads 5 and the row still shows Wide, since each type keeps its own settings. Pick Defaults: confirm the settings go back to the defaults, the ghost shrinks and nothing is built. Pick Wide: confirm Radius reads 5, the ghost widens and nothing is built. Save again as Wide with Radius 6: confirm it asks before replacing. Press Delete twice: confirm Wide is gone from the list and from
res://hammerforge_presets/structure_presets.json. - With Type on Stairs, set Height from to Total, Total to 3 and Steps to 16. Confirm the line under the settings says 16 steps of 0.1875. Set it to Fit with Rise 0.2: 15 steps. Set Turn to L left, Before landing to 8 and Landing to the Width or more: confirm a landing at the eighth riser and a second flight leaving its side. Try U right with a Gap of 1. Set Nosing to 0.05 with Fill Solid: confirm it is refused and says a solid nosing is at least 0.1 (#1410).
- Set Height from Total, Total 3, Steps 40 and Fill Solid. Confirm it is refused with a hint naming how many steps fit, or Open fill (#1582).
- Set Height from Rise, Rise 0.5, Tread 0.6 and Collision to Ramp, build a flight and bake. Confirm a
RampCollisionbody in the baked output, and that a playtest player walks up the flight without stopping at the first riser. Run Validate: no stairs warning for that flight, though each riser is over the player's step. Clip one of its steps, bake again, and confirm the Console says the stair keeps its steps' collision and Validate now reports the steps (#1416, #1588). - Hollow a 6 x 3 x 6 box and cut a doorway through one wall with a Cutout. On the Paint tab turn on Face Select and pick the four inside wall faces, then press Shift+Alt+T. Confirm a skirting runs round the room's floor, mitred at every corner and stopped square either side of the doorway, as one undo step. Select a piece: the section reads Update Trim Run. Set Edge to Top and Update: a cornice under the ceiling, with no Re-pick. Save, reopen and Update again: it follows the same walls (#1414).
- Move one wall of that room, select a piece of the trim, turn on Face Select, pick the walls again and press Re-pick beside the section's button. Confirm the trim follows the moved wall and keeps its settings. Pick a sloped face and press Shift+Alt+T: it is refused, naming the face (#1414).
- With Type on Door Frame, confirm Wall and Frame stop at 0.1. Set Head to Arched, Width 2 and Height 1: confirm it is refused and asks for a height of 1.1 or more, half the Width and 0.1 more (#1605).
- Bake with a dome, a spiral stair and a flight of stairs in the level. Confirm every surface is visible from outside and nothing renders inside out.
7c-7. Editing a Structure After the Fact¶
- With Type on Arch, pick Defaults in the Preset row and create an arch. Select one segment. Confirm the Type dropdown says Arch, the settings load, the button reads Update Arch, and Detach appears.
- Change Type to Dome, pick Defaults and create a dome elsewhere. Select one of its panels. Confirm the dropdown switches to Dome and the dome's own settings load, not the arch's.
- With an arch piece selected, change the Type dropdown to Dome. Confirm the section switches to creating a new dome (button reads Create Dome, Detach disappears) rather than snapping back to Arch.
- Select an arch piece again, change Radius, press Update Arch. Confirm it rebuilds at the new radius in the same place.
- Paint a distinct material on three different pieces. Change a setting and Update. Confirm all three materials come back on the same pieces.
- Increase the piece count and update. Confirm the extra pieces take the material most of the pieces have and the earlier ones keep theirs.
- Tie every piece of an arch as
func_wallnamedarch_wall, wire a button'sOnPressedtoarch_wallDisable, then change the radius and Update. Confirm every piece is stillfunc_wallarch_wallwith the same node names, and in a playtest the button still hides the arch (#1163, #1165). - Decrease it and update. Confirm the structure shrinks and the surviving pieces keep their materials.
- Set Segments to 0 and press Update. Confirm it refuses with a message and the existing structure is still there — nothing may be deleted by a refused update.
- Build two structures in different places. Update the first. Confirm the second does not move or change.
- Clip one piece of a structure in two, then select an untouched piece and update. Confirm it rebuilds and the clipped pieces are left alone.
- Move a structure and then update it. Select every piece of an arch, drag it well away, then select one piece, change the radius and press Update. Confirm the arch rebuilds where you moved it, not back where it was created.
- Move a single piece of a structure on its own. Select any piece. Confirm the section shows a warning naming how many pieces have been edited and mentioning Detach.
- Vertex-drag a piece of a dome. Select any panel. Confirm the count in the warning goes up.
- Press Update with the warning showing. Confirm the structure rebuilds and the warning clears.
- Press Ctrl+Z. Confirm the hand edits come back.
- Save the level, reopen it, select a piece of each structure. Confirm the section recognises each one, loads its settings, and shows no edit warning — a reopened level must not claim its pieces were edited.
- Build a flight of stairs and give each face of one step a different material. Save, reopen, select a step and press Update. Confirm every face of that step keeps its own material (#873: a reopened flight used to count every step as edited, and Update then put each face's material on another side).
- Open a level saved before October 2026 with a flight of stairs you never edited. Select a step. Confirm no edit warning shows, and that a step you had resized before that save is still counted (#878).
- Press Detach on a structure, then select a piece. Confirm the section is back to Create and loads nothing.
- Select a piece of a structure. Confirm the context toolbar shows Upd and Det, and that a plain brush does not. Change a setting in the Structure section and click Upd: it rebuilds as the section's button does. Click Det on another structure and confirm it detaches (#1302).
- Detach a structure, press Ctrl+Z, then Ctrl+Shift+Z. Select one of its former pieces and confirm the section stays on Create and the toolbar shows no Upd or Det (#1306).
- Press
Ctrl+Shift+Awith the Type dropdown on Dome. Confirm the shortcut builds a dome, not an arch.
7c-8. Sweep Faces¶
- In Face Select, pick one face of a box and run Sweep Faces from the palette. Choose Arc, 90 degrees, Radius 3 and 8 Segments. Confirm the pieces are outlined in cyan as the values change, OK makes 8 brushes that meet face to face in one group, and one Ctrl+Z takes them all away (#1714).
- Lower the Radius until the bend is tighter than the face is wide. Confirm the outline turns red, OK is greyed out and nothing is made.
- Pick two faces that are not in one plane and run it. Confirm it is refused with the reason.
- Give the source box a brick material on its sides and run it again with End width scale 0.5. Confirm the run narrows evenly to half its width, every piece is convex, and the pieces' sides are brick (#1899).
- Open it again on Arc. Drag the orange pivot handle along the face and confirm Radius and Toward follow it in grid steps and the outline follows. Drag the yellow end handle round the pivot and confirm Angle moves in 15 degree steps. Type a Radius and confirm the handle moves. Start a drag and right click: the values go back (#1885).
- Press OK, select one of the pieces, raise Segments in the Structure section and press Update Sweep. Confirm the bend rebuilds smoother in one undo step, still one group (#1885).
- With a piece selected, open Face Select, pick a face of another box painted in another material and press Re-pick. Confirm the sweep moves onto it in one undo step and its pieces take that box's material. Lower Segments and confirm Update asks nothing, since no piece was painted by hand (#1932).
7d. Face Winding Migration (Old Saves)¶
- Open a
.hflevelfile saved before the CW winding fix (April 6, 2026 or earlier). - Confirm all brush faces render with textures visible from outside (not inside-out).
- Select a brush, enable Face Select Mode, and click each face — confirm the face normal gizmo (if visible) points outward.
- Bake the level; confirm baked geometry is textured correctly (not inside-out or black).
- Save the level. Re-open it; confirm faces still render correctly (migration should have written the current
winding_version, 3). - Create a new brush in the same level; confirm its faces also render correctly alongside the migrated ones.
- Run Show Face Direction from the palette. Confirm the front of every brush face is drawn blue and the back red, and that running it again takes the look off (#1869).
8. Vertex Editing (Edge Sub-Mode)¶
- Select a brush and enter vertex mode (V key or the V toggle button in the toolbar).
- Confirm vertex crosses and edge wireframe lines appear on the brush.
- Click a vertex to select it (orange cross). Shift+click to multi-select.
- Select all four vertices of one side of a box and drag along that side's normal. Confirm the face follows the cursor on a view-facing plane through the picked vertex instead of being forced onto world Y, and that the move commits.
- Drag a single corner of a box away from the solid. Confirm the move commits and the brush gains faces: a brush face is a plane, moving one corner of a quad bends it, so each bent quad is cut into two triangles on release. The face count goes up in the stats and the brush is no longer resizable as a box.
- Drag a single corner a little way into the solid. Confirm the move commits: each bent quad folds outward along the diagonal that does not touch the corner, so the brush is still convex. Then drag a corner past the plane through its three neighbouring corners. Confirm the preview drops back to where it started while you are still dragging, with a message saying it would create a non-convex brush, and that releasing leaves the brush as it was. Try each of the eight corners of a box: the answer does not depend on which corner it is.
- Undo the accepted corner drag. Confirm the brush is six faces again.
- Repeat in front and side orthographic views, then drag a selected edge. Confirm each gesture uses the picked vertex or edge midpoint as its stable anchor with no first-motion jump.
- During separate drags, press X, Y, and Z to lock movement. Confirm only the chosen world axis changes and grid snapping remains consistent.
- Aim the camera directly along the locked axis and drag. Confirm the degenerate projection is ignored—geometry must not jump, accumulate a stale prior delta, or become non-convex.
- Drag to move vertices; confirm a move that would leave the brush non-convex reverts, and a move that only bends a face is kept by splitting that face.
- Press E to toggle to edge sub-mode; confirm edges become clickable.
- Click an edge; confirm it highlights orange and both endpoints are selected.
- Select a single edge and press Ctrl+E; confirm the edge is split (9 vertices on box).
- Undo the split; confirm 8 vertices return.
- Select 2 vertices and press Ctrl+W; confirm merge (or rejection if non-convex).
- Press E again to return to vertex sub-mode.
- Merge two vertices of a box with Ctrl+W, then press Ctrl+Z. Confirm it is a box again, with its resize handles, and that changing its size in the Inspector scales it (#1538).
- Select two vertices on each of two brushes and press Merge in the vertex toolbar. Confirm both brushes merge as one undo step. Run Split Edge from the command palette and Convex from the toolbar and confirm each is one undo step (#1533).
- Drag each of the eight top corners of a box straight up in turn, undoing after each. Confirm every one is taken (#1561).
- Select one vertex and open Selection Info above Selection Tools. Type a new Y: confirm the vertex moves there as one Move Vertices step, and a value that would dent the brush puts the field back and says why. Switch World to Local on a turned brush and confirm the numbers change (#1404).
9. Polygon Tool¶
- Press P to activate the Polygon tool.
- Aim at the top or sloped face of an elevated brush and place the first point; confirm it lands on the exact visible surface and uses the enabled snap modes. Aim inside the empty bounds of a non-box brush and confirm placement continues to real geometry behind it or the forward construction plane.
- With only Grid snap enabled, click 3+ more points at different screen heights; confirm they stay on the horizontal plane established by the first point and the cyan outline preview appears. Enable a geometry snap mode and confirm a nearby eligible candidate can win through the shared snap pipeline.
- Draw an L-shape; confirm every vertex is placed, each edge shows its length, and an orange line shows where it will be split. Hold Shift while placing and confirm the edge locks to X or Z. Close it, raise it, and confirm two grouped brushes that one undo removes. Draw a bow tie and confirm closing it is refused with a toast.
- Press Enter (or click near the first point) to close the polygon.
- Move mouse up/down to set height; confirm green vertical edges + top outline preview.
- During height placement, release LMB outside the viewport and return with no buttons held; confirm recovery creates at most one brush. Start again and Alt+Tab during height placement; confirm the polygon returns to editable points and no later unrelated release commits it.
- Click to confirm; confirm a brush appears in DraftBrushes.
- Undo; confirm the brush is removed.
- Redo; confirm the brush returns.
- Press Escape during vertex placement; confirm last point is removed. Press again to cancel entirely.
- On a grid of 2, press Grid Up until the drawing plane is held at an odd height, then place a Polygon's first point over empty grid. Confirm it sits on the held plane, not on the grid step below; do the same with the Path tool (#1583).
10. Path Tool¶
- Press semicolon (;) to activate the Path tool.
- Place the first waypoint on an elevated visible brush surface, then click 2+ waypoints at different screen heights with only Grid snap enabled. Confirm the first point uses exact-surface picking and active snapping, while later points remain on its horizontal plane with cyan width indicators. Enable a geometry snap mode and confirm a nearby eligible candidate can win.
- Press Enter to finalize the path.
- Confirm corridor brushes appear, one per segment, meeting on one plane at each corner with no wedge brush between them.
- Confirm all brushes share a group (click one, all select).
- Undo; confirm all path brushes are removed in one step.
- Redo; confirm they return.
- Test with only 2 waypoints (straight corridor, square ends).
- Set Side to Left, click four corners of a square clockwise seen from above, then click the first point again; confirm four walls build at once, mitred at all four corners, with their inside faces on the clicked lines.
- Press Escape during waypoint placement; confirm last waypoint is removed.
- While placing, confirm each placed segment shows its length and the live one to the cursor is yellow. Hold Shift and confirm the live segment locks to X or Z (#1408).
- Place three points that turn sharply back on themselves and press Enter. Confirm the preview is red and a toast refuses the path, naming the segment, with the points kept. With three points placed, move near the first: it turns green before a click would close the loop (#1412).
- Set Curve to Smooth, click four points and drag from the third to pull out a tangent handle. Confirm the walls follow a smooth curve, each span cut into Curve Segments pieces that meet face to face. Make a bend tighter than the wall is wide: only the part that blocks the build is outlined red, and Enter is refused naming the span (#1436).
11. Material Browser + Texture Picker¶
- Open the Paint tab → Materials section.
- Open Material Library and click Refresh Prototypes; confirm the thumbnail grid populates with 150 texture previews.
- Click Add Texture Folder and pick a folder holding
brick_albedo.pngandbrick_normal.png. Confirm one brick material joins the palette with its normal map, in one undo step, and draggingbrick_albedo.pngfrom the FileSystem dock onto a face paints it with that same material (#1884). - Type "brick" in the search bar; confirm the grid filters to brick-pattern materials only.
- Click a color swatch (e.g. red); confirm only red materials remain. Click "All" to reset.
- Select the "Favorites" view toggle; confirm it is empty initially.
- Right-click a thumbnail → "Toggle Favorite"; switch to Favorites view and confirm the item appears.
- Switch back to "Prototypes" view. Click a thumbnail to select a material.
- With a brush selected (but no faces), double-click a thumbnail; confirm all faces of the selected brush(es) update (whole-brush fallback).
- With no brush or face selected, double-click a thumbnail; confirm a toast "No brushes selected — select a brush first".
- Enable Face Select Mode, click a face on a brush, then double-click a thumbnail or click Assign to Selected Faces; confirm only the selected face material changes.
- Hover a different thumbnail in the grid; confirm the selected face temporarily previews that material. Move the mouse away; confirm the preview reverts.
- Right-click a thumbnail → "Apply to Whole Brush"; confirm all faces of the selected brush(es) update.
- Lock a visgroup holding a brush that wears a material, and open Replace... on that material. Confirm the count leaves the locked brush out and Replace leaves it as it was. Replace with Shown brushes only while a brush is hidden, change Where to the whole level in Adjust Last, then press Ctrl+Z once: the hidden brush goes back too (#1936, #1930).
- Right-click a thumbnail → "Copy Name"; paste elsewhere to confirm clipboard content.
- Press T (Texture Picker); click a face in the viewport. Confirm the browser selection updates to match that face's material.
- Press T on a face with no material; confirm a toast message appears ("Face has no material assigned").
- Reimport synchronization: select a brush, switch to the Paint tab, and scroll through thumbnails (triggering any lazy texture reimport). Confirm the dock and Godot's visible Scene/viewport selection always agree. If the visible selection remains, the dock stays at "Sel: 1 brush"; an intentional visible deselect must immediately show zero rather than retaining a hidden HammerForge selection.
- Drag a material file (
.tres) from Godot's FileSystem dock onto a brush face. Confirm the face takes it, the palette gains it, and one Ctrl+Z undoes both. Drop one on a face in the face selection: every picked face takes it. Drop an image: a toast and no change (#1417). - Drag a thumbnail from the browser onto a face in the viewport and confirm the face takes it (#1580).
- Apply three different materials. Confirm the Recent row lists them newest first, each once. Star a material, delete its file from the FileSystem dock, and confirm its favourite shows "!" with the old path in the tooltip and right click offers Remove Favorite. Right-click a thumbnail, choose Set as Active Material, and confirm the Build tab's Material button names it (#1422).
- Star three materials, select two brushes and press Alt+2. Confirm both take favourite 2 as one undo step. Pick faces and press Alt+1: only the faces change. Press Alt+9 with fewer favourites: a toast names the empty slot (#1439).
- Press T on a face with a turned and scaled texture, pick two faces on other brushes in the same plane and press Alt+T. Confirm they take the same material, scale, offset and rotation and line up across the seam, as one undo step. Press Alt+T with only brushes selected: a toast asks for faces (#1427).
- Press T on a face, select three brushes and press Shift+T. Confirm all three take the material and one Ctrl+Z puts all three back (#1565).
- Pick an Active Material on the Build tab and draw a box, a Polygon and a Stairs structure: each takes it. Draw a Cutout: it keeps the cut look. Press Clear and draw again: no material. Save, close and reopen the scene: the Active Material is still set (#1405).
11b. UV Editor and Texture Alignment¶
- Pick a face in Face Select and open Paint → Face Tools → UV Editor. Confirm the canvas shows the face's texture under its outline. Press x2, then /2: the Scale boxes double and halve, each one undo step. Turn on the link between the Scale boxes, type a U scale, and confirm V follows (#1418, #1421).
- On a 4 x 1 wall face press Flip U, Flip V, Rotate 90, then Fit U. Confirm Fit U gives four whole repeats across the face, and each press is one undo step (#1421).
- Drag a UV corner on the canvas with UV Snap on: it lands on the grid shown, and Ctrl places it freely. Ctrl+Z undoes the drag in one step. Click a corner without moving it, and press Escape in the middle of a drag: neither records a step (#1445, #1544).
- Press Fit, then type an Offset. Confirm the face keeps its fit and only shifts (#1539).
- Pick a 45 degree chamfer face and press Align to Face. Confirm the texture lies in the face's plane with no stretch down the slope, and Align to World puts it back on Box UV (#1419).
- Pick one side face of a box pillar, press Wrap, then click each face round the pillar. Confirm the texture carries over each edge with no seam, one undo step a click, and Escape ends it (#1428).
- Pick the faces of a wall made of three brushes and press Link. Move one brush and confirm the texture stays continuous across all three. Select Group on one face picks the whole group, and Unlink frees them (#1437).
- Turn on Texture Gizmo (or Giz in the context toolbar) with faces picked. Drag the middle square: the texture slides on every picked face. Drag an axis end: it stretches. Drag the ring: it turns by Step. Each drag is one undo step. Start a drag and press Escape: the texture goes back (#1420).
- Open a level saved before October 2026 with a cylinder in it. Pick its side faces, set Smoothing to 1 and bake: confirm it shades round. Draw a new cylinder: it is round in the preview and the bake with nothing set (#1429).
12. Spawn System + Test Level Validation¶
- Delete all
player_startentities (or start with a fresh scene). - Click Test Level; confirm a toast warns "No player_start found — auto-creating default spawn".
- Confirm the playtest launches and the player spawns standing on the floor under the middle of the level.
- Repeat on a level whose floor is one box 1 unit thick. Confirm Test Level launches with no spawn warning or fix dialog (#961).
- Stop the playtest. Move the auto-created
player_startinside a solid brush. - Click Test Level; confirm a dialog appears listing "Spawn inside a brush".
- Click Fix & Play; confirm the spawn moves beside the brush on the floor it was on and playtest launches.
- Stop the playtest. Stand a 1 x 3 x 1 pillar on the floor, put
player_startin its middle, and repeat with a ceiling slab resting on the pillar. Each time, confirm the dialog lists "Spawn inside a brush" and Fix & Play moves the spawn beside the pillar, not onto it (#1002). - Put the editor camera inside the pillar and press Play from Camera. Confirm Fix & Play starts the player beside the pillar, the
player_starthas not moved, and Ctrl+Z has nothing new to undo. Select a room's brushes, moveplayer_startinto a wall of that room and press Play Selected Area; confirm Fix & Play plays that room alone (#1010). - Press Validate Spawn with
player_startin a wall, and switch to another scene tab within a few seconds. Come back after ten seconds. Confirm the red capsule and markers are gone (#1016). - Stop the playtest. Place
player_starthigh above geometry (floating in space). - Click Test Level; confirm a warning dialog about floating/no floor.
- Click Cancel; confirm the playtest does not launch and cancellation feedback appears.
- Open Test → Spawn section.
- Click Validate Spawn; confirm the debug overlay appears (capsule, floor ray, markers) for ~10 seconds.
- Build an 8 x 4 x 8 block and cut a 6 x 3 x 6 room out of its middle with a cut brush. Delete every
player_startand click Test Level. Confirm the player starts on the room's floor, not on the block's roof (#1008). - Check Preview Spawn Debug; confirm the overlay stays persistent.
- Uncheck Preview Spawn Debug; confirm the overlay is cleaned up.
- Place two
player_startentities. Setprimary = trueon the second one via Entity Properties. - Click Test Level; confirm the player spawns at the primary-flagged entity (not the first one).
- Set
angle = 90on the primary spawn; playtest and confirm the player faces 90 degrees rotated. - Build five boxes 2 m wide and 0.7 m deep, each 0.6 m on from the last and 0.5 m taller, so each runs 0.1 m into the next. Validate: one stairs warning, from the first box to the last, at 0.50 m a step (#1118). Turn the top two 12 degrees each about their own middle: still one warning (#1104).
- Cut a doorway through a
func_wallwith a Cutout and run Validate. Confirm no doorway is reported for it (#1563). - Let the bake issue list make a spawn, move it into a brush, and use Fix on its row. Undo back past the spawn being made, then redo all the way: the spawn ends where the fix put it, not where it was made (#1108).
12a. Bake Optimizations + Test Modes¶
- Select 2-3 brushes drawn since the last Bake. Click Bake Selected; confirm only those brushes are baked and previously baked geometry is preserved (not replaced). Move one and click Bake Selected again: its earlier selection bake is replaced, not doubled. Select a brush the last full Bake holds: Bake Selected bakes the whole level and says so.
- Modify a brush (move/resize). Click Bake Changed; confirm the level is rebuilt with the change and unmodified geometry is intact. Click Bake Changed again with nothing changed: it says there is nothing to bake.
- Expand a brush's exported
facesresource in the Inspector and change a face material/UV value (and, where available, a paint/displacement value). Confirm Bake Changed includes that exact brush. Undo/Redo the Inspector edit and confirm each transition is detected once; reapply an identical/no-op value and confirm it does not create a false dirty brush. - Set Preview Mode to Wireframe; click Bake. Confirm baked output renders as cyan wireframe overlay.
- Set Preview Mode to Proxy; click Bake. Confirm baked output renders as semi-transparent grey.
- Set Preview Mode back to Full; click Bake. Confirm normal material rendering resumes.
- Create a cut brush that cuts a doorway through a solid wall. Enable Use Face Materials, then test ordinary Bake with the cut pending/applied and again after a frozen Commit Cuts. Confirm every bake visibly keeps the opening and collision permits movement/raycasting through it; the face-material fast path must yield to CSG while structural cuts exist, and a toast must say so and name how many cutters caused it.
- With an existing Wireframe or Proxy bake, Commit Cuts and then Undo/Redo. Confirm the exact pre/post baked geometry and its preview mode return on each transition, with no extra bake, consumed cutter, or stale solid collision.
- Click Check Bake Issues on a clean level; confirm no issues reported.
- Draw a box 2 x 2 x 2 standing on the floor and a second box beside it, and in the Inspector set the second box's Position Y to 1.005 so its bottom face is 0.005 above the floor. Select the first box, enter Face Select, click the second box's bottom face, press
Shift+Alt+X, then delete the second box. The slice left on the floor is a Custom brush 0.005 thick: a box is never built thinner than 0.1, and Validate only reports a brush under 0.01. Click Check Bake Issues; confirm a severity-2 "degenerate brush" issue appears for the slice (#1599). - Create a cut brush floating in empty space (not overlapping any solid brush). Click Check Bake Issues; confirm a severity-1 warning that the cut brush overlaps no solid brush.
- Confirm that warning is a row under the button with Select and no Fix, and Select selects the cutter. Delete the level's
player_start, check again and press Fix on "No player spawn"; confirm a spawn appears and one Ctrl+Z removes it. Stand a spawn inside a pillar, check, press Fix; confirm it moves beside the pillar on the same floor and one Ctrl+Z puts it back. - Texture three floor slabs with three different materials and bake. In the scene tree, confirm the collision body has one
CollisionShape3Dper material rather than one for the level, and that it carries anhf_surface_namesmetadata entry listing them in the same order. - Set Collision to one of the per-brush modes and bake again. Confirm the body carries no surface names: a shape is a brush there, and a brush has six faces.
- Duplicate a brush with Ctrl+D and leave the copy in place. Click Validate. Confirm a row lists the two brushes in the same space and its Select selects the copy, and that Output still logs the line (#1004).
- Save a small level as its own scene, instance it twice into a parent scene, and run Validate on each copy. Confirm neither reports duplicate brush ids: the two instances carry the same ids on purpose and each resolves its own. Select a brush in one copy and nudge it; confirm the matching brush in the other copy does not move.
- Import a legacy .map file with known vertex drift (or create two adjacent brushes with edges offset by ~0.005 units). Click Check Bake Issues; confirm a severity-1 "micro-gap" warning appears for the near-coincident cross-brush vertices.
- Enter vertex mode on a brush and drag a vertex slightly off-plane (quad with 4th vertex drifted ~0.05 on the normal axis). Click Check Bake Issues; confirm a severity-1 "non-planar" warning appears for that face.
- Enable Generate LODs and bake. Confirm the bake completes without a script error and the baked meshes carry LOD levels.
- Stretch a 16-sided cylinder and a wedge along X with Godot's scale gizmo and bake. Confirm the round wall and the slope are lit like the same shapes built at that size, with no band of faces lit as though they faced elsewhere (#884).
- Enable the material atlas with two or more textured materials and bake. Confirm the atlas texture reports mipmaps and distant surfaces do not shimmer.
- Check the bake estimate label updates after each bake (shows estimated time for next bake).
- Bake the whole level, then Bake Selected on one brush. Confirm the estimate still reads about a whole level's time, not close to nothing (#1590).
- Select a brush, set Cast Shadows to Off in Selection Tools and bake. Confirm that brush casts no shadow and the rest of the level does. Put it in a visgroup set to Off and give the brush On: it casts again, since its own choice wins. Each change is one undo step (#1444).
- Set Lightmap Scale to 4 on a floor brush, bake with Lightmap UV2 on, and confirm in the Inspector that its baked mesh has a GI Lightmap Texel Scale of 4 and the others 1 (#1438).
- Tie a brush as
func_clip. Confirm it draws violet whatever material it has. Bake: aClipCollisionbody underNonstructural, with no mesh. In a playtest the player is stopped by it and cannot see it (#1409). - Put a 6-sided cylinder and a sphere in a level with a Cutout elsewhere, and bake. Confirm the baked cylinder's corners line up with the brush's, not turned 30 degrees, and the baked sphere has the brush's own segments (#1596).
- Click Play from Camera; confirm the player spawns at the editor camera position with matching yaw. Stop playtest; confirm the spawn entity is back in its original position.
- Move the camera to an invalid position (inside geometry). Click Play from Camera; confirm the fix dialog appears and spawn is restored on cancel.
- Select a subset of brushes. Click Play Selected Area; confirm only the selected area is baked. Stop playtest; confirm the cordon returns to its previous state (enabled/disabled, original AABB, each cordon's name and Bake switch).
- Uncheck Bake on the first cordon, select brushes and click Play Selected Area. Confirm the selection is baked, not the whole level, and the first cordon is still off afterward.
- With two cordons set (Add from Selection), select brushes in neither and click Play Selected Area. Confirm only the selection is baked, and both cordons are back afterward.
- With cordon disabled, click Play Selected Area, then stop. Confirm cordon is still disabled afterward.
- After each of the two buttons, use Scene > Reload Saved Scene. Godot saves the scene on the way into a run, so this shows what reached the file. Confirm the spawn is where you placed it, not at the camera, and the cordon is as you left it (#822).
- Start Test Level on a level that takes a few seconds to bake, and switch to another scene tab before it finishes. Confirm a message says the bake stopped, Output shows no script error, and the bake buttons work when you come back and bake again (#1290). Start it again and turn HammerForge off in Project Settings, Plugins, before the bake finishes: confirm Output shows no script error (#1288).
Named Brush Lightmaps¶
- Enable Lightmap UV2 and Generate LODs. Bake a named func_wall with a brush material override and a dense named static brush. Confirm their baked meshes have UV2, the dense mesh has LODs, and the override survives. Confirm the editor meshes remain unchanged.
- Bake a func_door and inspect its mesh leaf. Confirm GI Mode is Dynamic. Confirm any static mesh still missing UV2 is named in the bake warning.
12b. Auto-Connectors (Terrain Bake)¶
- Create two paint layers at different Y heights (e.g. layer 0 at Y=0, layer 1 at Y=4). Paint adjacent cells so at least one cell in each layer borders the other.
- Open Test → Advanced Bake. Check Auto Connectors.
- Set Mode to Ramp. Click Bake. Confirm
AutoConnector_*MeshInstance3D nodes appear in the baked output connecting the two height levels with sloped geometry. - Undo the bake. Set Mode to Stairs, Step H to 0.5. Bake again. Confirm stair-step geometry appears instead of a smooth ramp.
- Set Mode to Auto. Bake. Confirm the system chooses ramps for small height differences and stairs for larger ones (threshold = step height).
- Set Width to 3. Bake. Confirm connectors are wider (3 cells).
- Uncheck Auto Connectors. Bake. Confirm no
AutoConnector_*nodes appear. - Select a subset of brushes. Click Bake Selected. Confirm no auto-connectors are generated (selection-only bakes skip connectors).
- With Auto Connectors enabled + NavMesh enabled, bake. Confirm both
AutoConnector_*collision shapes andBakedNavmeshregion exist, and navmesh uses STATIC_COLLIDERS parsed geometry type.
12c. Floor Paint Generative Pass¶
- Enter Floor Paint with Shift+P, choose Rect with R, and drag a footprint. Confirm a live footprint overlay follows only the active cells, disappears on release, and the flat floor/walls appear as DraftBrush nodes under
Generated. - Press Y, move vertically, and confirm with LMB. Confirm the raise cage previews the height, only the last footprint's boundary walls change height, and Ctrl+Z first undoes the raise then a second Ctrl+Z undoes the paint stroke.
- Repeat the raise gesture and press Esc. Confirm the original wall heights return immediately and no raise overlay remains.
- Toggle Mirror X with X, paint once, and confirm the source plus grid-origin reflection disappear together with one Ctrl+Z. Repeat for Z, then both axes. Confirm the banner/context toolbar names the active axes and both toggles default off after a fresh plugin session.
- Draw a Rect of a distinctive size, move the pointer, and press H. Confirm one room stamp creates the same filled dimensions with boundary walls and one Ctrl+Z removes the entire room.
- Leave Inference cleanup off and paint an isolated cell plus a one-cell hole/gap pattern; confirm the authored cells are unchanged. Turn it on and repeat: confirm only isolated one-cell noise is removed, a cardinal one-cell hole/gap is filled, and a fast one-cell corridor gains one adjacent row. Confirm distant painted work and erase strokes are untouched. Then click a single cell on empty ground with cleanup still on and confirm the cell stays.
- Create a second paint layer at a different Y. Paint adjacent to it and confirm a translucent ramp/stair ghost appears only for the touched boundary. Press Esc and confirm it vanishes; repaint, press Enter, and confirm one undo removes the committed connector definition.
- Disable Auto Connectors, confirm a connector again, and run a full bake. Confirm exactly one
AutoConnector_*mesh plus collision is produced. Enable automatic detection and bake again; confirm the same boundary is still emitted only once. - Enable a heightmap on a paint layer and repeat paint, mirror, room, and raise. Confirm floors use
Generated/HeightmapFloorsMeshInstance3D output while boundary walls retain scoped per-cell heights; no action rewrites the heightmap. - Enable region streaming, perform a mirrored stroke across a region boundary, raise it, unload/reload the region, and confirm occupancy and wall heights persist. Preview work must remain limited to touched dirty chunks.
- During every idle and active Floor Paint state, use plain RMB/WASD viewport navigation. Confirm no paint action consumes it and no ghost remains after Esc or stroke completion.
12d. Occluder Generation (Automated Culling)¶
- Draw 3-4 large brushes forming walls and a floor (total visible surface > 4 units²).
- Open Test → Advanced Bake. Check Generate Occluders. Leave Min Area at 4.0.
- Click Bake. In the Scene tree, expand
BakedGeometry→ confirm anOccludersnode exists containingOccluder_0,Occluder_1, etc. (OccluderInstance3D nodes). - Set Min Area to 10000. Bake again. Confirm the
Occludersnode is gone (all surfaces below threshold). - Set Min Area back to 4.0 and bake with Chunk Size > 0 (default 32). Confirm occluders are still generated despite meshes being nested inside
BakedChunk_*intermediary nodes. - Uncheck Generate Occluders. Bake. Confirm no
Occludersnode appears. - Re-enable Generate Occluders. Click Check Bake Issues. Confirm an "Occlusion: N occluders covering ~X%" info entry appears in the results.
12e. Bake Profiles¶
- Open Test → Advanced Bake on a new level. Confirm Profile shows Editing.
- Pick Shipping. Confirm Merge Meshes and Generate LODs tick, nothing else changes, and a toast says 2 options changed. Press Ctrl+Z once. Confirm both untick and the list says Editing again.
- Pick Shipping, then untick Generate LODs. Confirm the list says Custom.
- Tick Bake Navmesh, type
Arenain the name box and press Save. Confirm the list says Arena. Pick Editing, then Arena. Confirm Bake Navmesh is ticked again and the name box saysArena. - Type
shippingin the name box. Confirm Save is greyed out and its tooltip says why. - With
Arenain the name box, press Delete once. Confirm the status line asks for a second press and Arena is still listed. Press again. Confirm it is gone. - Restart the editor after saving a profile. Confirm it is still in the list.
- Tick Project, type
Studioand press Save. Confirmhammerforge_presets/bake_profiles.jsonexists and the list says Studio (project). Untick Project withStudioin the box. Confirm Save greys out and says to tick Project. - With the level on Editing, leave Export with on Shipping and press Export Game Scene. Confirm the toast names Shipping, the Profile list still says Editing, and Ctrl+Z does not undo a profile switch (#1003).
- Export again, and while it bakes pick Arena in the Profile list and tick Bake Navmesh. Confirm each springs back with a toast to change the options when the export finishes, the list says Editing afterwards, and Ctrl+Z does not put Shipping on (#1011).
- Put a line
<<<<<<< HEADat the top ofhammerforge_presets/bake_profiles.json. Tick Project, type a name and press Save. Confirm the status line says the file could not be read and the file is unchanged (#1006).
13. Context Toolbar + Command Palette¶
- Select a brush in the viewport. Confirm the floating context toolbar appears at the top of the 3D viewport showing "1 brush" with Extrude/Hollow/Clip/Carve/Merge/Intersect/Duplicate/Delete buttons.
- Select multiple brushes; confirm the label updates to "N brushes".
- Click the "Hol" button in the toolbar; confirm hollow executes on the selected brush.
- Click "Dup"; confirm a duplicate brush is created.
- Switch to Draw tool (D key). Confirm the toolbar shows shape buttons (Box/Cyl/Sph/Cone), an "Add" toggle, and Repeat, disabled until the first Draw.
- Click the "Sub" toggle; confirm the operation mode switches to Subtract.
- Begin a drag (click+hold in viewport). Confirm the toolbar switches to "Drawing" context with live dimension display and X/Y/Z axis lock buttons.
- Right-click to cancel the drag. Confirm the toolbar returns to Draw idle context.
- Enable Face Select Mode. Select one or more faces. Confirm the toolbar shows face count, the favourites strip, UV justify buttons, the Giz toggle, and "All" (Apply to Whole Brush) button.
- Click a justify button (e.g. "Fit"); confirm UV justify applies to selected faces.
- Click "Stretch"; confirm UVs fill 0..1 on both axes (non-uniform).
- Click "Tile" on a non-square face; confirm the shorter axis fills 0..1 and the longer axis tiles.
- Enter Vertex mode (V key). Confirm the toolbar shows Vtx/Edge/Merge/Split/Convex/Snap/Exit buttons.
- Click "Exit"; confirm vertex mode is deactivated.
- Select an entity (if available). Confirm the toolbar shows "1 entity" with I/O and Props buttons.
- Add an ordinary Godot node to the entity/brush selection. Confirm the context toolbar no longer advertises a managed selection context and the command palette grays out selection-dependent HammerForge actions. Invoke a previously open/stale action if possible and confirm the final guard blocks it with no partial change.
- Deselect all (Esc); confirm the toolbar hides when no context applies.
- Press Shift+? (or F1) in the 3D viewport. Confirm the command palette appears with a search field and categorized action list.
- Type "hollow" in the search; confirm only the Hollow action is visible.
- Clear the search; confirm all actions reappear.
- With no brush selected, confirm "Hollow", "Clip", "Carve" entries are grayed out.
- Select a brush; press Shift+? again. Confirm "Hollow" is now enabled (not grayed out).
- Click "Hollow" in the palette (or press Enter); confirm hollow executes and the palette closes.
- Press Esc while the palette is open; confirm it closes without executing anything.
- Toggle paint mode on; press Shift+?. Confirm paint tools (Bucket, Erase, Ramp, etc.) are now enabled.
- Hollow a brush, then change Thickness in the Adjust panel in the viewport's bottom right corner. Confirm the walls rebuild and one Ctrl+Z gives back the solid (#1867).
- Select a brush, Duplicate it, move the copy up with Page Up and press Rotate CW. Open the palette and confirm its top rows show Repeat Run: with those steps. Run Repeat Run twice and confirm two more copies, each up and turned from the last, each press one undo step (#1735).
14. Coach Marks (First-Use Tool Guides)¶
- Press P to activate the Polygon tool for the first time. Confirm a floating coach mark overlay appears with step-by-step instructions.
- Read the steps; confirm they describe the Polygon workflow (click vertices → close → set height → confirm).
- Click Got it; confirm the overlay dismisses.
- Press P again; confirm the coach mark reappears (since "Don't show again" was not checked).
- Press P once more, check "Don't show again", and click Got it.
- Press P again; confirm the coach mark does NOT appear.
- Press V to enter Vertex mode; confirm a different coach mark appears (Vertex Editing guide).
- Press Ctrl+H (Hollow) with a brush selected; confirm the Hollow coach mark appears.
- Restart Godot. Press P; confirm the Polygon coach mark is still dismissed (persisted via user prefs).
- Reset by clearing
coach_dismissed_*keys fromuser://hammerforge_prefs.json.
15. Operation Replay Timeline¶
- Press Ctrl+Shift+T; confirm the operation replay timeline appears (initially empty or with a "Hover an operation to see details" message).
- Draw a brush; confirm a "+" icon appears in the timeline.
- Delete the brush; confirm an "x" icon appears.
- Undo the delete; confirm the timeline still shows both operations.
- Hover an icon in the timeline; confirm the detail label shows the operation name and elapsed time (e.g. "Draw Brush (5s ago)"), and that the Replay button does not appear on hover alone.
- Click an icon; confirm Replay appears and stays there while you move the pointer off the entry and onto the button. Click Replay; confirm the editor undoes/redoes to reach that point in history with a toast ("Replay: undid N steps" or "Replay: redid N steps").
- Clear the brushes; confirm the entry is drawn with the destructive "x" in red rather than the blue "+" a creation gets.
- Draw several more brushes to accumulate 5+ timeline entries. Confirm the timeline scrolls horizontally.
- Press Ctrl+Shift+T again; confirm the timeline hides.
16. Command Palette Fuzzy Search¶
- Press Ctrl+K; confirm the command palette opens (same as Shift+? and F1).
- Type "hollow"; confirm exact match shows the Hollow action.
- Clear and type "hllow" (typo); confirm the "Did you mean: Hollow?" suggestion appears and the Hollow entry is visible.
- Clear and type "extrd"; confirm fuzzy matches for Extrude Up/Down appear.
- Press Enter; confirm the first fuzzy match executes.
- Press Ctrl+K again; type "zzzqq" (no match at all); confirm no entries and no suggestion shown.
- Clear and type "shell"; confirm the top row reads "Hollow (shell)". Type "slice"; confirm Clip is the top row.
- Select a box, run Hollow from the palette and confirm. Select another box, open the palette with nothing typed; confirm the top row reads "Repeat: Hollow (0.25 m)" with the thickness the dock shows, and a Recent list under it starting with Hollow. Click it and confirm; the second box is hollowed with the same walls, and one Ctrl+Z takes it back.
17. Example Library¶
- Open Test tab. Expand the Examples section (collapsed by default).
- Confirm 5 example cards are visible with titles, difficulty badges, and descriptions.
- Type "corridor" in the search bar; confirm only the "Corridor with Doorway" card is visible.
- Clear the search. Type "advanced"; confirm only the "Simple Arena" card is visible.
- Clear the search. Click Study This on "Simple Room"; confirm the annotation panel appears with numbered design notes.
- Click Close on the annotation panel; confirm it disappears.
- Click Load on "Simple Room"; confirm existing brushes are cleared and the example's brushes appear in the viewport at their correct positions (not piled at the origin).
- Click Load on "Jump Puzzle Platforms"; confirm the room brushes are cleared and platform brushes appear at staggered heights with a player_start entity.
- Undo is not supported for example loads (they bypass undo/redo). Confirm a toast "Loaded 'Jump Puzzle Platforms': N objects" appeared.
18. Convert Selection to Heightmap¶
- Draw 2-3 brushes at different heights.
- Select all brushes. Switch to Paint tab → Heightmap section.
- Click Convert Selection → Heightmap. Confirm a toast appears ("Converted N brushes to heightmap layer 'Converted Terrain'").
- Confirm the Paint layer dropdown now shows the new layer as active.
- Confirm terrain geometry appears in the viewport where the brushes were.
19. Foliage & Scatter¶
- In Paint tab → Foliage & Scatter section, click the mesh picker and select any
.tresor.objmesh. - Set density to 2.0, radius to 5.0. Click Preview. Confirm dot instances appear in the viewport around the center of your selection.
- Change preview mode to Wireframe; click Preview again; confirm wireframe preview replaces dots.
- Click Scatter to commit. Confirm a toast appears ("Scattered N instances") and the preview is replaced by a permanent MultiMeshInstance3D. Press Ctrl+Z and confirm the instances go; redo and confirm they come back. Save the scene, reopen it, and confirm they are still there.
- Move or rotate the committed scatter container (or its parent), rebuild/commit again, and confirm instances retain the intended container-local placement rather than receiving the parent transform twice.
- Click Clear. Confirm the preview node is removed from the viewport.
- Switch shape to Spline. Select 3+ nodes/brushes. Click Preview. Confirm scatter instances follow the path defined by the selected node positions.
- With only 1 node selected in Spline mode, click Preview. Confirm a warning toast appears and no preview is created.
20. Path Tool Extras¶
- Activate the Path tool (;). In the tool settings, set path_extra to Stairs.
- Place 2 waypoints at different Y heights (use grid snap or numeric input). Press Enter.
- Confirm step brushes are generated along the sloped segment.
- Undo. Change path_extra to Railing. Repeat the path. Confirm top rails and posts appear on both sides.
- Undo. Change path_extra to Trim. Repeat. Confirm edge strips appear alongside the path.
21. Measure Tool (Multi-Ruler)¶
- Press M to activate the Measure tool.
- Click two points in the viewport; confirm a ruler line appears with distance and dX/dY/dZ labels.
- Hold Shift and click a third point; confirm a second ruler chains from the last endpoint and an angle label appears at the shared vertex.
- Confirm up to 20 rulers can exist (draw several). Older rulers are evicted when the cap is reached.
- Ctrl+Click near a ruler; confirm it is set as snap reference (line turns white, HUD shows "Align: ON").
- Press A; confirm align mode toggles off.
- Press Delete; confirm the last ruler is removed.
- Press Escape; confirm all rulers are cleared.
- Draw a ruler between the corners of two walls and press K. Confirm the dimension stays after the Measure tool is left, drawn as a line with a tick at each end and its length at the middle (#1699).
- Move one wall. Confirm the dimension follows it, with no undo step of its own. Set its Check to At least player width in Entity Properties and narrow the gap below the player's width. Confirm it turns red and Validate names it.
- Clip away the corner one end is on. Confirm that end comes loose and the dimension is drawn amber.
22. Undo History Browser¶
- Open Test → History section.
- Perform 3-4 operations (draw, delete, move). Confirm entries appear in the history browser with color-coded icons.
- Hover an entry; confirm an enlarged thumbnail preview appears beside the row and that the dock panel does not grow, shrink, or jump as you move the cursor down the list.
- Double-click an older entry; confirm the editor undoes to that point in history.
- Click the Undo button in the history header; confirm it undoes one step. Click Redo; confirm it redoes.
- With nothing to undo, confirm the Undo button is disabled. With nothing to redo, confirm Redo is disabled.
23. Performance Monitor¶
- Open Test → Performance section.
- Confirm it shows: Health label, Active Brushes (with ProgressBar), Entities, Vertices (est), Paint Memory, Bake Chunks, Last Bake, Rec. Chunk Size.
- Draw 5+ brushes; confirm the brush count and vertex estimate update.
- Create entities; confirm the entity count updates.
- With many brushes (>100), confirm the Health label turns yellow ("Edits Slowing") and its tooltip says the pause after an edit grows with the count.
- With more than 1,000 brushes, confirm the Health label turns red. With more than 500, confirm the bake estimate's tip says to chunk the bake so the game can cull it (#1593).
24. Theme Sync¶
- Switch Godot to a light theme (Editor Settings → Interface → Theme → Base Color).
- Confirm all HammerForge panels (context toolbar, coach marks, toasts, command palette, operation replay) adapt to light colors.
- Switch back to dark theme; confirm panels revert to dark colors.
- Set the system to reduce motion (on Windows, Animation effects off). With Motion on Follow system under Test → Settings, confirm toasts, the HUD's hints and the context toolbar's hint bar appear and go at once, with no slide or fade (#1703).
- With no overlay colour picked, turn on the system's contrast setting and start the editor. Confirm the overlay colours start in High Contrast and the Console says so once. In the dock, press Tab repeatedly and confirm every button can be reached.
25. Export Playtest Build¶
- Open Test → Advanced Bake. Click Export Playtest Build.
- If no spawn exists, confirm a toast about auto-creating a default spawn, and confirm the spawn creation is undoable.
- Confirm the level bakes and a playtest scene launches with a controllable player at the active spawn and the expected spawn yaw.
- Put a mesh/collision child below an extra nested Node3D and export again. Confirm the nested geometry and collision are present in the packed scene.
- Move or rotate the LevelRoot, an entity container, and representative exported content. Confirm the launched scene preserves their visible world transforms without double-applying a parent transform.
- If entities have I/O connections, confirm the exported scene contains an
HFIODispatcherchild node. - Tie a brush as
func_detailor a trigger, add an I/O connection, export, and confirm both the tied brush and its runtime connection survive. - Bake, delete the floor under the spawn, then press Export Playtest Build. Confirm it bakes first and then shows the spawn fix dialog rather than exporting a player over nothing, and that Fix & Play exports with the spawn moved (#1460).
- Stop the playtest. Undo; confirm the auto-created spawn is removed.
26. Displacement Surfaces¶
- Draw a box brush. Enter Face Select Mode, select a top face (quad).
- Open Paint → Face Tools → Displacement. Set Power = 3. Click Create.
- Confirm the face becomes a subdivided grid (toast: "Displacement created (power 3)").
- Enable Paint Mode. Choose Paint Mode = Raise, Radius = 4, Strength = 1.
- Click and drag on the face. Confirm vertices rise under the brush.
- Stretch a wedge three times as tall with Godot's scale gizmo, put a displacement on its slope, and drag across it with Raise. Confirm the vertices rise under the cursor rather than off to one side (#884).
- Switch to Paint Mode = Smooth. Paint over the raised area. Confirm it smooths out.
- Turn Face Select Mode back on (Paint mode turned it off) and click the displaced face.
- Click Noise. Confirm the surface gets noisy (toast: "Noise applied to displacement").
- Click Smooth. Confirm the surface smooths out (toast: "Displacement smoothed").
- Change Elevation via the spinbox. Confirm the grid height changes.
- Click Destroy. Confirm the face reverts to a flat quad (toast: "Displacement removed").
- Undo (Ctrl+Z) and confirm the displacement reappears.
- Raise part of a displaced face, press Ctrl+S, then use Scene > Reload Saved Scene. Confirm the sculpt comes back with the same shape (#854).
- Sculpt a face of an unpainted box with Raise. Confirm the preview shows the sculpt with its shading relief, the same as a bake does (#1597).
- Fold the Displacement section away and drag on the face in Paint mode. Confirm it paints as usual rather than sculpting (#1527).
27. Edge Bevel and Face Inset¶
- Draw a box brush. Press V for Vertex mode, then E for Edge sub-mode.
- Click an edge to select it (should highlight orange).
- Open Build → More build settings → Bevel. Set Segments = 3, Radius = 2.
- Click Bevel Edge. Confirm the sharp edge is replaced with 3 intermediate faces (toast: "Beveled 1 edge(s)").
- Undo; confirm the edge returns to normal.
- Switch to Face Select Mode. Select a face.
- Open Paint → Face Tools → Face Inset. Set Inset = 2, Height = 0.
- Click Inset Face. Confirm the face shrinks inward with connecting side quads (toast: "Face inset applied").
- Set Inset to a very large value (larger than the face). Click Inset Face. Confirm error toast.
- Undo; confirm the face returns to normal.
- Sculpt the top of a box with Raise, bevel one of its top edges, then inset the top at height 0. Confirm the sculpt is trimmed at each step rather than shrunk onto the smaller face: the bumps stay over the same spots, and the ring around the inset is sculpted too. Undo each and confirm the sculpt comes back whole (#870).
28. I/O Runtime Signal Translation¶
- Create two entities (e.g.
button1anddoor1). - Wire an I/O connection: button1 output
OnPressed→ door1 inputOpen. - Enable Bake Wire I/O in LevelRoot Inspector.
- Bake the level. Confirm the baked container has an
HFIODispatcherchild in the scene tree. - Export a Playtest Build. Confirm the exported scene also contains an
HFIODispatchernode. - Rename a trigger brush to
door_sensorand wire an entity output at it. Bake, then confirm the bakedArea3Dis nameddoor_sensorand carries anentity_namemeta, and that firing the connection reaches the target. - Configure an output on a brush entity, save to
.hfleveland reload. Confirm the connection is still there. Repeat with an undo/redo cycle and with a duplicate. - Place a
logic_timernamedtick, untick its Start On Load, and wire button1OnPressedtotickStart, andtickOnTimerto door1Open. Use Export Game Scene and open the result. Confirmtickis aTimer, not a marker. Run an Export Playtest Build, press the button and confirm the door opens one interval later (#826). Do the same in Test Level (#840). - Tie a 4 m wide brush to
func_doorwith Speed 1 and Wait 2, and wire button1OnPressedto it. In a playtest, press the button and confirm the door slides all the way open, stays open for two seconds, and only then closes (#910). - Place a second
logic_timerwith Start On Load left on, and wire only itsOnTimerto a light'sToggle. Use Export Playtest Build and confirm the light toggles every interval with nothing sent toStart(#835). - Place a
light_pointnamedlamp, untick its Start On Load, and wire button1OnPressedtolampTurnOn. Confirm the viewport still draws its marker. In Test Level, confirm the lamp is dark when the level loads and comes on when you press the button. Use Export Game Scene and confirmlampis anOmniLight3Dwith Visible off (#1258). - Tie a brush to
func_wall, name itsecret_wall, and wire button1OnPressedtosecret_wallDisable. Bake. Confirmsecret_wallis its own node underNonstructural, not part of the world mesh. In a playtest, press the button and confirm the wall disappears and you can walk through where it was (#827). - Save a scene whose root is a
Timerand type its path into aprop_static's Scene property. Confirm the viewport shows the prop's marker and the Output says once that the scene has noNode3Droot to show. Use Export Playtest Build and confirm the exported scene holds aTimerunder the prop's name (#844). - Select two brushes and Tie them as one
func_door. Select one leaf and set its speed in the Entity panel, then select the other: it shows the same speed, and one Ctrl+Z puts both back (#1113). - Tie a brush as
trigger_once, wire itsOnStartTouchto a lamp, Untie it and Tie it asfunc_door. The door lists no wires and notarget(#1112). - Tie a brush as
trigger_multiplewith Retrigger Delay 2 and wireOnStartTouchto a light'sToggle. In a playtest, walk in, out and in again inside two seconds: the light toggles once. Tie a button with Locked ticked: Use does nothing until a wire sends itUnlock(#1121). - Tie two separate brushes as one
trigger_oncewith no name, wireOnStartTouchto a lamp, and playtest. Walk into each brush in turn: the lamp turns on once in all (#1133). - Tie two brushes as one
func_doornamedgate. On button1's I/O wiring panel, confirm the target list offersgateonce. WireOnPressedtogateOpen, then wire a second button to one leaf's node name (type it into the Entity tab's Target field). In a playtest, each button opens both leaves, andOnOpenwired to a lamp turns it on once (#1145, #1149, #1150). - Select both leaves of
gateand press Ctrl+D. Confirm the copy is a second door with a name of its own (gate_2), Validate reports nothing aboutgate, and Export .map writes two doors (#1151, #1161). - Tie two separate brushes as one
trigger_oncenamedhall, select one and wireOnStartTouchto a lamp. Select the other: it lists the same wire. In a playtest, walk into the second brush first: the lamp turns on, and walking into the first afterwards does nothing more (#1129). - Bake a level with the two-leaf door
gateand a two-brushfunc_wallnamedwall, and expand the baked container in the Scene dock. Confirm the second leaf's holder and the wall's second mesh are named after their brushes (Box_7), and that no holder or mesh under it has a name starting@(#1169).
29. Viewport Context Menu¶
- Select a brush. Press Space; confirm a context menu appears at the cursor with brush-specific items (Extrude Up, Extrude Down, Hollow, Clip, Carve, Duplicate, Delete).
- Hover over Grid Snap; confirm a submenu appears with snap values (0.1, 0.25, 0.5, 1, 2, 4, 8 m).
- Click a grid snap value (e.g. 8); confirm the dock's grid snap updates to 8.
- Enable Face Select Mode. Select a face. Press Space; confirm the menu shows UV operations instead of brush operations.
- Hover over UV Operations; confirm a submenu with Fit, Center, Stretch, Tile, etc.
- Deselect all. Switch to Draw tool. Press Space; confirm the menu shows shape and Add/Subtract items.
- Begin a drag (click+hold). Press Space; confirm the menu does NOT open (idle guard).
- Select an entity. Press Space; confirm entity-specific items appear (I/O Connect, Properties, etc.).
- Confirm Highlight Connected appears as a check item and toggles state when clicked.
- Add an ordinary Godot node to the HammerForge selection, reopen the menu, and confirm managed selection commands are unavailable. If a stale menu item can still be invoked, confirm it is blocked with the mixed-selection warning and no partial edit.
- Add a
MeshInstance3Dwith aBoxMeshand aCSGCylinder3Dwith 8 sides outside the level, select both and press Space. Choose Convert to Brushes. Confirm a box brush and an 8-sided cylinder brush stand where they were, with their materials, and the nodes are hidden. One Ctrl+Z removes the brushes and shows the nodes again. Try it on a concave mesh: it is refused with the reason (#1440).
30. Radial Menu¶
- Press
`(backtick) in the 3D viewport; confirm a radial pie menu appears centered at the cursor with 8 labeled sectors. - Move the mouse into the "Box" sector; confirm it highlights.
- Move the mouse to a different sector; confirm the highlight follows.
- Move the mouse into the center dead zone; confirm no sector is highlighted.
- Move the mouse outside the outer ring; confirm no sector is highlighted.
- Left-click on a highlighted sector (e.g. "Measure"); confirm the action executes and the menu closes.
- Press
`again; confirm the menu reopens (reopen test). - Press Escape while the menu is open; confirm it closes without executing.
- Press
`to open, then press`again; confirm it toggles closed. - Right-click while the menu is open; confirm it closes without executing.
- Bind Radial Menu to another key in
user://hammerforge_keymap.json, open the menu with it and press it again. Confirm it closes (#1608). - Open the radial while a drag is in progress; confirm it does NOT open (idle guard).
- With a mixed HammerForge/Godot selection, choose a selection-dependent sector such as Clip or Vertex. Confirm it is blocked with no selected node changed; choose a general non-mutating tool sector and confirm normal tool switching still works.
31. Quick Property Popups¶
- In the viewport, tap G twice quickly (G G); confirm a small popup appears at the cursor with a "Grid Snap" SpinBox showing the current snap value.
- Type a new value (e.g. 4) and press Enter; confirm the grid snap updates and the popup closes.
- Tap G G again; press Escape; confirm the popup closes without changing the value.
- Tap G G again; click somewhere outside the popup; confirm it dismisses (click consumed, no brush placed).
- Select a brush. Tap B twice (B B); confirm a popup appears with 3 SpinBoxes (X, Y, Z) reading the current size exactly, and that the three ranges match the Size X/Y/Z spins in the Build tab. Type a size, press Enter, and confirm the dock spins and the next brush you draw are the same size.
- Enable paint mode. Tap R twice (R R); confirm a popup appears with a "Paint Radius" SpinBox whose range is the one the paint radius control in the dock has.
32. Clean Brush Visuals and Lifecycle¶
- With no Active Material set, draw a new additive box; confirm it has a clean green-tinted surface with no always-on triangle wireframe.
- Resize continuously while drawing; confirm only the current box is visible and no nested outlines or drag-history trail remains.
- Hover the box in Select mode; confirm the highlight contains only the 12 structural box edges, with no diagonal across any face.
- Select the box; confirm one concise structural outline and six resize handles appear only for that selection.
- Hover/select wedges, pyramids, prisms, merged/custom brushes, and polyhedra; confirm only true boundaries and creases appear, with no coplanar triangulation fans or old flat merge diagonals. Set adjustable pyramids/prisms to 3 and 5 sides and confirm their centered visible bounds touch the same six handle planes. Repeat with cylinder, cone, sphere, ellipsoid, capsule, and torus; confirm each uses a restrained, recognizable curved profile rather than every render triangle or a fallback box.
- Move the pointer over a selected brush. Confirm hover and selection share one outline source and never stack a second coincident highlight.
- Change box, ellipsoid, and torus brushes to Subtract; confirm exactly one depth-aware red semantic outline appears for each. The box must have only 12 structural edges and curved shapes must use their sparse profiles, not a dense render-triangle cage. Resize repeatedly and confirm the overlay neither duplicates, double-scales, nor leaves a trail.
- Tie the brush to
func_detail,trigger_once, andfunc_wall; confirm one appropriately tinted blue overlay for each type, without accumulating prior overlays. Tie it tofunc_clip: confirm it is violet. - Untie the brush; confirm it returns to the clean additive surface with no topology overlay.
- Paint floor/wall geometry and confirm generated helper brushes do not fill the viewport with green topology lines.
- Bake the same level three times; confirm the scene tree contains exactly one top-level
BakedGeometrynode and no anonymous baked-container copies. - Reopen a legacy scene that contains duplicated anonymous
BakedChunk_*roots; confirm HammerForge retains the newest populated bake, removes only recognized duplicates, and leaves unrelated anonymous nodes untouched. - Select a cylinder and try to drag its Sides below 5 in the Inspector: it stops at 5. A pyramid's goes down to 3. Open a level saved before 12 Oct 2026 with a cylinder in it: its Sides reads 16, the count it is drawn with (#1103).
- Open Test → Settings → Colours. Change Selected brush and confirm a selected brush's outline changes at once. Press High Contrast: all eighteen colours change, a solid and a cut still look different, a func_detail or trigger brush is no longer blue (#1760), and a pending cut (draw one with Cut on) looks different from an applied one (#1680). Bake and confirm no overlay colour reaches the baked meshes. Press Reset: the defaults come back (#1446).
Drawing Plane and Preview Cancellation¶
- Place a brush, then begin another over empty grid. Confirm its base stays at the first brush's base height.
- Use Grid Up/Down on a 0.5 snap. Confirm each moves the plane 0.5 m along its active axis, and later draws leave it held there. Reset Grid returns to the LevelRoot origin on Y and clears the axis lock.
- Select vertices and Align Grid. Confirm their average sets the coordinate on the current axis. With no vertices selected, align a face; confirm its dominant world axis and center set an axis-aligned plane, including an off-grid coordinate. An angled face does not tilt the plane.
- Align a brush selection and confirm the grid uses the combined bounds' top. With no selection, Align Grid resets it. None of these commands adds an undo step.
- Turn a box 30 degrees about up. In Vertex mode pick one of its bottom edges and press Align Grid. Confirm the grid's lines turn to run along that edge, the Angle field reads 30 and the HUD says
@ 30°. Draw a box beside it and confirm it is square to the turned box with its corners on the turned lines; drag a resize handle and confirm its face stays on a line. Press an arrow key and confirm it moves along the turned grid. Reset Grid and confirm the grid and the next draw are square to the world (#1893). - Pick a wall face and press Align Grid, so the grid stands on the wall. Then pick one bottom edge of the turned box in Vertex mode and press Align Grid again. Confirm the grid lies on the floor, turned along the edge (#1933).
- In separate draws, press Ctrl+Z during the base stage and during the height stage. Confirm only the preview and typed dimension disappear; the previous brush stays. Press Ctrl+Z again while idle and confirm the preceding action is undone.
- Repeat with an unfinished Extrude. Confirm the preview is removed and its source brush stays. With a text field focused, confirm its own undo does not cancel the draw.
- In Draw, move over the floor, a raised box top and a wedge's slope without clicking. Confirm the face under the cursor is tinted and a cross marks the snapped start point, and that both clear when a drag starts. Turn off Draw hover preview in Test → Settings: they go (#1400).
- Press L (or tick On Face under Snap) and draw on a wall. Confirm the base lies on the wall, the size readout follows the wall's own axes, a Solid grows out of the wall and a Cutout cuts into it, and one undo removes it (#1401).
- Draw a box, then press Shift+D (or Repeat on the context toolbar). Click on the floor and on a box top. Confirm a copy stands on the surface under the cursor at each click, one undo step each. Escape or a right click disarms it (#1407).
- Type 30 in the grid's Angle field and draw a 2 by 2 by 2 box. Press Shift+D and confirm the ghost is turned 30 degrees, and a click places a copy turned with it, its corners on the grid's lines. Reset Grid and confirm the next copy is square to the world (#1923).
- While drawing, confirm a size label follows the cursor through both stages, and reads Depth during an Extrude. Type a number during the draw and confirm it shows in brackets (#1402).
- Start an Extrude on a top face, type 3 and press Enter without moving the mouse. Confirm a block 3 deep is placed as one undo step. Type 0, then 300: each is refused with a toast and the extrude goes on (#1560).
33. Grid Size Indicator and Hotkeys¶
- With LevelRoot active, confirm the shortcut HUD shows the snap followed by the plane axis and position, for example "Grid: 0.5 Y 0.0".
- Change the grid snap via the dock SpinBox; confirm the HUD label updates and flashes briefly (yellow-white → fade).
- Press
]; confirm the grid doubles (e.g. 16 → 32) and the HUD flashes. - Set the snap to a fractional value (e.g. 0.125 via the dock SpinBox) and confirm the snap portion reads "Grid: 0.125" before the plane suffix.
- Press
[; confirm the grid halves (e.g. 32 → 16) and the HUD flashes. - Press
[repeatedly until minimum (0.125); confirm it stops halving and displays the snap as "Grid: 0.125" (not "0.13") before the plane suffix. - Press
]repeatedly until maximum (512); confirm it stops doubling. - Double-tap G G to open quick-property popup; change value; confirm HUD updates with flash.
- Perform a state restore (undo a bulk operation); confirm the HUD picks up the restored grid snap.
- Select a few brushes in the viewport and press Ctrl+C, then Ctrl+V. Confirm a copy lands on top of the originals, that the copy is what is now selected, and that one Ctrl+Z removes the whole paste in a single step.
- Paste again without copying. Confirm a second copy appears and that no two brushes report the same id in the Console.
- Copy a piece with a wired button and door in it, open a different level, and paste. Confirm the wiring, the brush entity properties and the materials all came across, and that the piece is not listed as a prefab instance.
- Copy something, close the level, reopen it, and paste. Confirm the clipboard survived.
- With focus in the Scene tree and a non-HammerForge node selected, press Ctrl+C and Ctrl+V. Confirm Godot's own node copy and paste still work and that HammerForge did not claim them.
- On a level with a few hundred brushes and several materials, nudge one brush and press Ctrl+Z. Confirm the undo is quick and that nothing else in the level flickers or changes appearance: a restore only repaints when the palette is part of what changed.
- Then change a palette slot to a different material, and undo that. Confirm every brush using that slot goes back to the material it had. This is the half the check above must not break.
33b. File I/O Refusals and Durability¶
- Create a file named
broken.mapcontaining onlynot a map. Import it from the dock. Confirm the current level is untouched, the status line reports a failed import naming the reason, and no undo entry was added. - Export a level containing a carved or beveled brush to
.mapand reimport it. Confirm the brushes come back the right way out rather than inside out. - Export a room of a known size to
.mapand open the file. Confirm the plane coordinates are the size times the Map units/m row, and thatworldspawncarries one_hf_units_per_metreline rather than two. Import it back and confirm the room is the size it started at. - Set Map units/m to 16, import a file exported at 32, and confirm the level comes back at its exported size rather than doubled: the file's own figure wins. Then export it and confirm the new file is written at 16, which is how a map is moved between conventions.
- Import a
.mapcontaining a floor slab written by another editor. Confirm it lands flat, not upright: a floor is thin in the direction you stand up in. Export a floor from here and open the file; confirm the thin axis is itsz, and thatworldspawncarries one_hf_axis_conventionline readingquake. - Import a
.map, then export it and import the result. Confirm the level is the same size and the same way up both times, and that the second file carries one copy of each of the twoworldspawnkeys rather than two. - Import a
.mapholding atrigger_oncetexturedAAATRIGGER, then press Clear under the palette in the Paint tab. Resize the trigger, Clip it in two, press Ctrl+S and use Scene > Reload Saved Scene, then export. Open the file and confirm both halves still sayAAATRIGGERand no face says__default(#859). - Draw a cylinder, set its Sides to 6 in the Inspector, and stretch it along X with Godot's scale gizmo. Give one wall and the top their own materials, export to
.mapand open the file in another editor. Confirm the prism is the one in the viewport, not turned against it, and that the two materials are on that wall and the top rather than their neighbours (#880). - Give each face of a box its own UV offset, scale and rotation, export to
.mapin Classic Quake and again in Valve 220, and import each file. Confirm every face comes back aligned as it was. Open the Classic Quake file in TrenchBroom with the same textures and confirm each texture is the size and position it was here. Import a file written by another editor with aligned faces and confirm they arrive aligned as that editor showed them (#885, #894). - Stretch a wedge four times as tall with Godot's scale gizmo and set its slope to Box UV. Confirm the texture is not drawn several times longer down the slope than across it, then export to Valve 220 and confirm the slope's texture axes are the ones the viewport shows (#887).
- Tie two door leaves as one
func_doorwith no name, export to.map, import the file and export again. Both files hold onefunc_doorblock of two brushes (#1134). - Tie two door leaves as one
func_doornamedgate, wireOnOpenon the second leaf, export to.mapand open the file. Confirm onefunc_doorblock holding two brush blocks and oneOnOpenline, and that importing it gives two leaves of one door (#1128). Tick Locked on the door before exporting and confirm the imported door bakes with no error and stays shut in a playtest (#1127). - Import a
.mapholding a wedge and an 8 sided cylinder. Confirm no face shows its texture smeared into stripes, the floors and the east and west walls included (#909). - Draw a box with
proto_arrow_up_blueon it. Confirm the arrow points up on all four walls, and on a face set to Cylindrical. Open a level saved before this change that has an aligned or painted wall, and confirm that wall looks exactly as it did and that its untouched walls now point up. Export the box to Classic Quake and open it in TrenchBroom with the same texture: every face should match the viewport (#907). - Rotate a face's texture 30 degrees, export to Valve 220 and open the file in TrenchBroom. Confirm the texture is turned the way it is in the viewport (#899).
- Enable region streaming with a small radius. Paint in one region, move the cursor into another so the first unloads, then return. Confirm the paint is still there.
- Make the
<level>.hfregionsdirectory unwritable (for example put a regular file at that path) and save. Confirm the save is reported as failed rather than succeeding with missing region data. - Save twice in quick succession to the same path while the first write is still running. Confirm the file ends up holding the newer of the two.
- With region streaming on and a region painted, use Load .hflevel on a file that is not a level (a renamed text file will do). Confirm the status line says the load failed and why, Undo has nothing new, and the file is not in recent files. Then move away until the painted region unloads, and confirm its paint lands in this level's
.hfregionsfolder, not one named after the refused file (#823, #824). - With autosave on, use Scene > Save Scene As... to save the level's
.tscnas a.scn. Confirm the.hflevelis written at once, itsscenenames the.scn, and the next autosave goes ahead with no refusal in the Console (#1591). - With region streaming on, paint a region, erase it all, move away until it unloads and come back: it is still empty. Paint again, undo the stroke, and move away and back: the undone paint stays gone. Add a paint layer, remove it and add another: the new layer starts empty everywhere (#1550, #1514, #1564).
- With region streaming on, save a level as
a.hflevelwith paint far from the origin and let it unload. Open a new level, paint near the origin and Save Asa.hflevelover it. Move to where the old paint was: nothing is there. Paint again, Save Asb.hflevel, undo the stroke, move away and back: the undone paint stays gone (#1929).
33c. A LevelRoot Away From the World Origin¶
Most placement bugs are invisible while the root sits at the origin, which is the
only way it usually gets exercised. Run this whole block with the LevelRoot node
translated to (1000, 0, 1000).
- Click in the viewport to place a brush. Confirm it lands under the cursor rather than a thousand units away, and that the grid moves to the brush you just made rather than somewhere else.
- Drag out a second brush. Confirm it lands on the grid you can see.
- Select a brush and open the hollow, carve, clip and cut previews in turn. Confirm each overlay draws around the real geometry.
- Enable the cordon and confirm the wireframe surrounds the region it names. Add a second cordon and confirm both are drawn. Uncheck Bake on one and confirm its wireframe dims and the list marks it "(off)".
- Remove a named cordon, press Ctrl+Z, and confirm it comes back with its name and the list shows it. Drag a cordon spinbox, press Ctrl+Z once, and confirm the whole drag is undone.
- Load a
.hflevelwhose settings differ from the open level's. Confirm the dock shows the loaded settings, cordons included, and that changing one cordon spinbox moves only that bound (#968). Check the entity wiring lines, the vertex and edge handles, and the prefab ghost in the same way. - Place an entity, save state, then undo and redo. Confirm the entity returns to where it was, and that the Output log carries no out-of-tree transform errors.
- Import a
.mapfile. Confirm its entities land where the file put them, relative to the geometry around them, rather than offset by the root. The coordinates are divided by the Map units/m row and turned onto this project's axes on the way in, so a spawn written at0 0 24sits 0.75 above the floor at the default of 32, not 24 along the depth axis. - Create a radial array, then press Ctrl+Z once. Confirm the whole array goes away in that one press and that the action before it is left alone. Redo and confirm every copy comes back.
- Bake the level. Confirm the baked geometry sits on top of the draft brushes rather than a second root offset away. Turn the LevelRoot 90 degrees and bake again, and confirm the output is still lined up rather than spun about the root.
34. Cleanup / Persistence¶
- Dismiss the tutorial with and without
Don't show againchecked. - Restart Godot and confirm the
show_welcomepreference behaves as expected. - Reopen the dock and confirm no layout corruption remains after closing the tutorial and shortcut dialog.
- Change Shape, the snap toggles, the Hollow wall, a Path tool setting and the Stairs fields, then restart Godot. Confirm each comes back as you left it. Press Reset Tool Settings in Test → Settings and confirm they go back to their defaults (#1406).
Expected Outcome¶
If all steps pass, the remaining risk on the tutorial/prefab/shortcut/cut-preview/vertex-editing/polygon/path/material-browser/spawn-system/context-toolbar/command-palette/terrain-scatter/measure-tool/history-browser/performance-monitor/theme-sync/export-playtest/displacement/bevel/auto-connectors/validation-weld-planarity/io-runtime/viewport-context-menu/radial-menu/quick-property/wireframe-colors/grid-indicator feature set is low and limited mainly to edge cases outside this smoke path.