feat(editor): mode-driven shelf/column/spawn placement + cross-kind floor collision
Migrate the remaining floor-placed kinds onto the unified snapping/modifier
model and generalize floor collision so any solid floor kind blocks any other.
- shelf/column/spawn declare `snapProfile: 'item'` → contextual snapping chip,
Shift=cycle, Ctrl=grid step during placement; their tools read the active
mode (grid/lines/off) instead of legacy Shift/Alt bypass; spawn fresh
placement now respects alignment ("lines") like its move.
- Resize/radial handles claim the handle-drag scope (new RESIZE_HANDLE_DRAG_LABEL)
so the HUD shows no select-mode shortcuts mid-resize.
- Column move migrated to the generic MoveRegistryNodeTool (declare `movable`,
drop the bespoke move-tool) — gains mode-driven snapping, alignment, R/T,
slab lift, grid SFX, and the collision box for free. 2D move still routes
through `floorplanMoveTarget`.
- Cross-kind floor collision: new declarative `FloorPlacedConfig.collides`
(item/shelf/column opt in; spawn/MEP/stair stay off). `canPlaceOnFloor` now
treats every colliding floor kind as an obstacle (was item-only), reading the
declarative footprint; the generic move tool's red/green placement box gates
on `collides`. Column footprint uses the visible `columnFootprintHalf` extent
so the box/slab-lift/collision track the real (round/square) column size.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
04f1b0d59e
commit
8a57105eec
@@ -132,6 +132,7 @@ function shelfHandles(_node: ShelfNodeType): HandleDescriptor<ShelfNodeType>[] {
|
||||
|
||||
export const shelfDefinition: NodeDefinition<typeof ShelfNode> = {
|
||||
kind: 'shelf',
|
||||
snapProfile: 'item',
|
||||
schemaVersion: 2,
|
||||
schema: ShelfNode,
|
||||
category: 'furnish',
|
||||
@@ -197,6 +198,7 @@ export const shelfDefinition: NodeDefinition<typeof ShelfNode> = {
|
||||
rotation: shelf.rotation,
|
||||
}
|
||||
},
|
||||
collides: true,
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
} from '@pascal-app/core'
|
||||
import {
|
||||
getFloorStackPreviewPosition,
|
||||
isGridSnapActive,
|
||||
isMagneticSnapActive,
|
||||
triggerSFX,
|
||||
useAlignmentGuides,
|
||||
useEditor,
|
||||
@@ -83,8 +85,8 @@ const ShelfTool = () => {
|
||||
rawZ: event.localPosition[2],
|
||||
gridStep: useEditor.getState().gridSnapStep,
|
||||
candidates: alignmentCandidates,
|
||||
bypassAlignment: event.nativeEvent?.altKey === true || event.nativeEvent?.shiftKey === true,
|
||||
bypassGrid: event.nativeEvent?.shiftKey === true,
|
||||
bypassAlignment: !isMagneticSnapActive(),
|
||||
bypassGrid: !isGridSnapActive(),
|
||||
})
|
||||
useAlignmentGuides.getState().set(guides)
|
||||
|
||||
@@ -98,10 +100,7 @@ const ShelfTool = () => {
|
||||
lastCursorRef.current = position
|
||||
|
||||
const prev = previousSnapRef.current
|
||||
if (
|
||||
event.nativeEvent?.shiftKey !== true &&
|
||||
(!prev || prev[0] !== position[0] || prev[1] !== position[2])
|
||||
) {
|
||||
if (!prev || prev[0] !== position[0] || prev[1] !== position[2]) {
|
||||
triggerSFX('sfx:grid-snap')
|
||||
previousSnapRef.current = [position[0], position[2]]
|
||||
}
|
||||
@@ -118,7 +117,7 @@ const ShelfTool = () => {
|
||||
activeLevelId,
|
||||
event,
|
||||
useEditor.getState().gridSnapStep,
|
||||
event.nativeEvent?.shiftKey === true,
|
||||
!isGridSnapActive(),
|
||||
)
|
||||
const shelf = ShelfNode.parse({
|
||||
...shelfDefinition.defaults(),
|
||||
|
||||
Reference in New Issue
Block a user