58 lines
1.9 KiB
TypeScript
58 lines
1.9 KiB
TypeScript
'use client'
|
|
|
|
import { type AnyNode, type AnyNodeId, useScene } from '@pascal-app/core'
|
|
import { triggerSFX } from '@pascal-app/editor'
|
|
import { useViewer } from '@pascal-app/viewer'
|
|
import { useMemo } from 'react'
|
|
import { GRASS_PRESETS, GRASS_SEED_POOL } from './grass-presets'
|
|
import GrassPreview from './grass-preview'
|
|
import { GrassNode } from './grass-schema'
|
|
import { usePlacement } from './placement'
|
|
import { useTreesStore } from './store'
|
|
|
|
/** The grass placement tool — mirrors the trees/flowers tools, reading the grass
|
|
* brush from the shared store. Blade colour is baked from the preset at
|
|
* placement, then editable per-tuft in the inspector. */
|
|
export default function GrassTool() {
|
|
const activeLevelId = useViewer((s) => s.selection.levelId)
|
|
const preset = useTreesStore((s) => s.grassPreset)
|
|
const height = useTreesStore((s) => s.grassHeight)
|
|
|
|
const previewNode = useMemo(
|
|
() =>
|
|
GrassNode.parse({
|
|
preset,
|
|
height,
|
|
bladeColor: GRASS_PRESETS[preset].bladeColor,
|
|
seed: 1,
|
|
position: [0, 0, 0],
|
|
rotation: [0, 0, 0],
|
|
}),
|
|
[preset, height],
|
|
)
|
|
|
|
const { cursorRef, cursorVisible } = usePlacement(activeLevelId, (position) => {
|
|
if (!activeLevelId) return
|
|
const s = useTreesStore.getState()
|
|
const grass = GrassNode.parse({
|
|
preset: s.grassPreset,
|
|
height: s.grassHeight,
|
|
bladeColor: GRASS_PRESETS[s.grassPreset].bladeColor,
|
|
seed: GRASS_SEED_POOL[Math.floor(Math.random() * GRASS_SEED_POOL.length)] ?? 1,
|
|
position,
|
|
rotation: [0, (Math.floor(Math.random() * 8) * Math.PI) / 4, 0],
|
|
})
|
|
useScene.getState().createNode(grass as unknown as AnyNode, activeLevelId as AnyNodeId)
|
|
useViewer.getState().setSelection({ selectedIds: [grass.id as AnyNodeId] })
|
|
triggerSFX('sfx:item-place')
|
|
})
|
|
|
|
if (!activeLevelId) return null
|
|
|
|
return (
|
|
<group ref={cursorRef} visible={cursorVisible}>
|
|
<GrassPreview node={previewNode} />
|
|
</group>
|
|
)
|
|
}
|