{
  "name": "wall",
  "title": "Wall",
  "type": "registry:component",
  "description": "Wall segment with door/window openings and a fixed collider.",
  "dependencies": [
    "@react-three/rapier@^2.2.0",
    "@runek/core@^0.12.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "Wall.tsx",
      "content": "import { RigidBody } from '@react-three/rapier'\nimport { useWorld, type Vec3 } from '@runek/core'\n\n/** A rectangular hole cut into a wall, for a door or window. */\nexport interface WallOpening {\n  /** Horizontal center offset from the wall center, in units. */\n  offset?: number\n  width: number\n  height: number\n  /** Height of the opening's base above the wall base, in units. */\n  sill?: number\n}\n\nexport interface WallProps {\n  position?: Vec3\n  rotation?: Vec3\n  /** Length along the wall's local X axis, in units. */\n  width?: number\n  height?: number\n  thickness?: number\n  /** Defaults to the world palette's `wall` slot. */\n  color?: string\n  /** Holes cut into the wall (doors, windows). Must not overlap horizontally. */\n  openings?: WallOpening[]\n}\n\ninterface Segment {\n  x: number\n  y: number\n  w: number\n  h: number\n}\n\nconst EPS = 1e-4\n\nfunction segments(width: number, height: number, openings?: WallOpening[]): Segment[] {\n  if (!openings?.length) return [{ x: 0, y: height / 2, w: width, h: height }]\n\n  const sorted = [...openings].sort((a, b) => (a.offset ?? 0) - (b.offset ?? 0))\n  const out: Segment[] = []\n  // Left edge of the un-emitted remainder; advances past each opening in turn.\n  let cursor = -width / 2\n\n  for (const opening of sorted) {\n    const { offset = 0, width: ow, height: oh, sill = 0 } = opening\n    const left = Math.max(offset - ow / 2, cursor)\n    const right = Math.min(offset + ow / 2, width / 2)\n    if (right - left <= EPS) continue\n\n    const pierW = left - cursor\n    if (pierW > EPS) out.push({ x: cursor + pierW / 2, y: height / 2, w: pierW, h: height })\n\n    const mid = (left + right) / 2\n    const clampedW = right - left\n    if (sill > EPS) out.push({ x: mid, y: sill / 2, w: clampedW, h: sill })\n\n    const top = sill + oh\n    const topH = height - top\n    if (topH > EPS) out.push({ x: mid, y: (top + height) / 2, w: clampedW, h: topH })\n\n    cursor = right\n  }\n\n  const lastW = width / 2 - cursor\n  if (lastW > EPS) out.push({ x: cursor + lastW / 2, y: height / 2, w: lastW, h: height })\n\n  return out\n}\n\nexport function Wall({\n  position = [0, 0, 0],\n  rotation = [0, 0, 0],\n  width = 4,\n  height = 3,\n  thickness = 0.2,\n  color,\n  openings,\n}: WallProps) {\n  const { unit, palette } = useWorld()\n  const wallColor = color ?? palette.wall\n  const w = width * unit\n  const h = height * unit\n  const t = thickness * unit\n  const scaled = openings?.map((o) => ({\n    offset: (o.offset ?? 0) * unit,\n    width: o.width * unit,\n    height: o.height * unit,\n    sill: (o.sill ?? 0) * unit,\n  }))\n\n  return (\n    <RigidBody type=\"fixed\" colliders=\"cuboid\" position={position} rotation={rotation}>\n      {segments(w, h, scaled).map((s) => (\n        <mesh\n          key={`${s.x.toFixed(3)}:${s.y.toFixed(3)}`}\n          castShadow\n          receiveShadow\n          position={[s.x, s.y, 0]}\n        >\n          <boxGeometry args={[s.w, s.h, t]} />\n          <meshStandardMaterial color={wallColor} />\n        </mesh>\n      ))}\n    </RigidBody>\n  )\n}\n",
      "type": "registry:component"
    }
  ]
}
