import { useEffect, useRef, useState } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import * as THREE from "three";
import { isDebugEnabled } from "@/utils/debug/isDebugEnabled";
import type { ZoneConfig } from "@/types/gameplay/zone";
interface ZoneDetectionProps {
zone: ZoneConfig;
onEnter: () => void;
height?: number;
}
const _cameraPos = new THREE.Vector3();
function ZoneDebugVisual({
zone,
active,
}: {
zone: ZoneConfig;
active: boolean;
}): React.JSX.Element | null {
if (!isDebugEnabled()) return null;
return (
);
}
export function ZoneDetection({
zone,
onEnter,
height,
}: ZoneDetectionProps): React.JSX.Element {
const camera = useThree((state) => state.camera);
const hasTriggeredRef = useRef(false);
const onEnterRef = useRef(onEnter);
const [isActive, setIsActive] = useState(false);
useEffect(() => {
onEnterRef.current = onEnter;
}, [onEnter]);
useFrame(() => {
if (hasTriggeredRef.current) return;
camera.getWorldPosition(_cameraPos);
const dx = _cameraPos.x - zone.position[0];
const dz = _cameraPos.z - zone.position[2];
const horizontalDist = Math.sqrt(dx * dx + dz * dz);
if (horizontalDist > zone.radius) return;
const zoneHeight = height ?? zone.height;
if (_cameraPos.y < zone.position[1] - zoneHeight / 2) return;
if (_cameraPos.y > zone.position[1] + zoneHeight / 2) return;
hasTriggeredRef.current = true;
setIsActive(true);
onEnterRef.current();
});
return ;
}