## TriggerZone Components

The `triggers` module provides a powerful system for creating interactive zones in your 3D environment. TriggerZone components can detect when objects enter, stay in, or exit specific areas, and can also execute scheduled actions.

```jsx
import { TriggerZone } from 'vibe-starter-3d-environment';
import { BoxTriggerShape, TriggerType, createScheduleTrigger } from 'vibe-starter-3d-environment';

// Basic contact trigger
<TriggerZone
  config={{
    id: 'my-trigger',
    enabled: true,
    triggerType: TriggerType.CONTACT,
    shape: new BoxTriggerShape(new Vector3(10, 10, 10)),
    conditions: [],
    actions: [
      {
        onAction: (event) => {
          console.log('Object entered trigger zone!', event);
        },
      },
    ],
  }}
  targets={[playerRef, enemyRef]}
  debug={true}
/>;
```

### TriggerZone Properties

| Property       | Type               | Default  | Description                                                    |
| -------------- | ------------------ | -------- | -------------------------------------------------------------- |
| config         | TriggerConfig      | required | Configuration object defining trigger behavior                 |
| targets        | PositionProvider[] | []       | Array of objects to track (Object3D, Vector3, or rigid bodies) |
| debug          | boolean            | false    | Enable debug visualization and logging                         |
| onTriggerEvent | function           | -        | Callback for all trigger events                                |

### TriggerConfig Interface

The `TriggerConfig` interface is the core configuration object for defining trigger behavior. Here's a detailed breakdown of each property:

```typescript
interface TriggerConfig {
  id: string; // Unique identifier for the trigger
  enabled: boolean; // Whether the trigger is active
  triggerType: TriggerType; // CONTACT or SCHEDULE
  shape?: TriggerShape; // Shape for contact triggers
  conditions?: TriggerCondition[]; // Conditions that must be met
  actions?: TriggerAction[]; // Actions to execute
  schedule?: ScheduleConfig; // Configuration for scheduled triggers
}
```

#### Property Details

##### `id` (string, required)

A unique identifier for the trigger. Used for debugging and managing triggers.

```jsx
id: 'player-spawn-zone';
id: 'enemy-damage-area-01';
id: 'checkpoint-trigger-level-2';
```

##### `enabled` (boolean, required)

Controls whether the trigger is active. Disabled triggers won't check for collisions or execute actions.

```jsx
enabled: true; // Trigger is active
enabled: false; // Trigger is inactive (useful for temporary deactivation)
```

##### `triggerType` (TriggerType enum, required)

Specifies the type of trigger behavior:

```jsx
import { TriggerType } from 'vibe-starter-3d-environment';

// Contact trigger - activates when objects enter/exit/stay in zone
triggerType: TriggerType.CONTACT;

// Schedule trigger - activates at timed intervals
triggerType: TriggerType.SCHEDULE;
```

##### `shape` (TriggerShape, optional - required for CONTACT triggers)

Defines the physical area for contact triggers:

```jsx
// Box shape
shape: new BoxTriggerShape(
  new Vector3(10, 5, 10), // size (width, height, depth)
  new Vector3(0, 2.5, 0), // position offset
  new Vector3(0, Math.PI / 4, 0), // rotation (x, y, z in radians)
);

// Sphere shape
shape: new SphereTriggerShape(
  15, // radius
  new Vector3(0, 0, 0), // position offset
);
```

##### `conditions` (TriggerCondition[], optional)

Array of conditions that must ALL be true for the trigger to activate:

```jsx
conditions: [
  {
    evaluate: (event) => {
      // Return true/false based on your logic
      return event.target.userData?.health > 0;
    },
    description: 'Target must be alive',
  },
  {
    evaluate: (event) => {
      // Can access global state
      return gameState.currentLevel === 2;
    },
    description: 'Must be on level 2',
  },
  {
    evaluate: (event) => {
      // Can check target properties
      const target = event.target;
      return target.position.y > 10;
    },
    description: 'Target must be above height 10',
  },
];
```

##### `actions` (TriggerAction[], optional)

Array of actions to execute when trigger activates:

```jsx
actions: [
  {
    // For CONTACT triggers
    onEnter: (event) => {
      console.log('Object entered:', event.target);
      // event.target - the object that triggered
      // event.trigger - the trigger configuration
      // event.type - TriggerEventType.ENTER
    },
    onExit: (event) => {
      console.log('Object exited:', event.target);
    },
    onStay: (event) => {
      // Called every frame while object is inside
      console.log('Object still inside:', event.target);
    },

    // For SCHEDULE triggers or general actions
    onAction: (event) => {
      console.log('Trigger activated!');
      spawnEnemy();
    },
  },
];
```

##### `schedule` (ScheduleConfig, optional - required for SCHEDULE triggers)

Configuration for timed triggers:

```jsx
// Interval schedule - repeats at fixed intervals
schedule: {
  type: 'interval',
  interval: 5000,    // Trigger every 5 seconds
  delay: 1000        // Wait 1 second before first trigger
}

// Future schedule types (not yet implemented)
schedule: {
  type: 'cron',      // Cron-like scheduling
  pattern: '*/5 * * * *'  // Every 5 minutes
}
```

#### Complete Configuration Examples

##### Example 1: Damage Zone

```jsx
const damageZoneConfig: TriggerConfig = {
  id: 'lava-damage-zone',
  enabled: true,
  triggerType: TriggerType.CONTACT,
  shape: new BoxTriggerShape(new Vector3(20, 5, 20)),
  conditions: [
    {
      evaluate: (event) => {
        // Only damage living entities
        return event.target.userData?.type === 'player' ||
               event.target.userData?.type === 'enemy';
      },
      description: 'Only affects players and enemies'
    }
  ],
  actions: [
    {
      onStay: (event) => {
        // Apply damage while inside
        const target = event.target;
        if (target.userData?.takeDamage) {
          target.userData.takeDamage(5); // 5 damage per frame
        }
      },
      onEnter: (event) => {
        console.log('Entered damage zone!');
        // Play warning sound
        audioManager.play('danger-sound');
      }
    }
  ]
};
```

##### Example 2: Item Spawner

```jsx
const itemSpawnerConfig: TriggerConfig = {
  id: 'health-pack-spawner',
  enabled: true,
  triggerType: TriggerType.SCHEDULE,
  schedule: {
    type: 'interval',
    interval: 30000,  // Every 30 seconds
    delay: 5000       // Start after 5 seconds
  },
  conditions: [
    {
      evaluate: () => {
        // Only spawn if less than 3 health packs exist
        return scene.children.filter(
          child => child.userData?.type === 'health-pack'
        ).length < 3;
      },
      description: 'Less than 3 health packs in scene'
    }
  ],
  actions: [
    {
      onAction: () => {
        const position = new Vector3(
          Math.random() * 50 - 25,
          5,
          Math.random() * 50 - 25
        );
        spawnHealthPack(position);
      }
    }
  ]
};
```

##### Example 3: Checkpoint System

```jsx
const checkpointConfig: TriggerConfig = {
  id: 'checkpoint-01',
  enabled: true,
  triggerType: TriggerType.CONTACT,
  shape: new BoxTriggerShape(
    new Vector3(5, 10, 5),
    new Vector3(50, 5, 0)  // Position at x=50
  ),
  conditions: [
    {
      evaluate: (event) => {
        // Only trigger for player
        return event.target.userData?.type === 'player';
      },
      description: 'Only player can activate checkpoint'
    },
    {
      evaluate: (event) => {
        // Don't retrigger if already activated
        return !gameState.checkpoints['checkpoint-01'];
      },
      description: 'Checkpoint not already activated'
    }
  ],
  actions: [
    {
      onEnter: (event) => {
        // Save checkpoint
        gameState.checkpoints['checkpoint-01'] = true;
        gameState.lastCheckpoint = {
          position: event.target.position.clone(),
          time: Date.now()
        };

        // Visual feedback
        showMessage('Checkpoint Reached!');
        playSound('checkpoint-sound');

        // Disable this trigger
        event.trigger.enabled = false;
      }
    }
  ]
};
```

##### Example 4: Teleporter with Cooldown

```jsx
const teleporterConfig: TriggerConfig = {
  id: 'teleporter-a-to-b',
  enabled: true,
  triggerType: TriggerType.CONTACT,
  shape: new SphereTriggerShape(3),
  conditions: [
    {
      evaluate: (event) => {
        // Check cooldown per target
        const lastTeleport = event.target.userData?.lastTeleport || 0;
        return Date.now() - lastTeleport > 3000; // 3 second cooldown
      },
      description: 'Teleport cooldown expired'
    }
  ],
  actions: [
    {
      onEnter: (event) => {
        const target = event.target;

        // Teleport to destination
        if (target.position) {
          target.position.set(100, 5, 100);

          // Set cooldown
          target.userData = target.userData || {};
          target.userData.lastTeleport = Date.now();

          // Effects
          createTeleportEffect(target.position);
          playSound('teleport-sound');
        }
      }
    }
  ]
};
```

#### TriggerEvent Object Structure

When actions are executed, they receive a `TriggerEvent` object:

```typescript
interface TriggerEvent {
  type: TriggerEventType; // ENTER, EXIT, STAY, or ACTION
  target: PositionProvider | null;
  trigger: TriggerConfig;
  timestamp: number;
}
```

This allows actions to access both the triggering object and the trigger configuration itself, enabling dynamic behavior based on both.

### Trigger Types and Event Types

#### TriggerType Enum

The `TriggerType` enum defines the different types of triggers available:

```typescript
export enum TriggerType {
  CONTACT = 'contact', // Traditional contact-based trigger
  SCHEDULE = 'schedule', // Schedule-based trigger (no area)
  AREA_SCHEDULE = 'area_schedule', // Schedule-based trigger with area effect (not yet implemented)
}
```

- **CONTACT**: Triggers when objects enter, exit, or stay within a defined shape/area
- **SCHEDULE**: Triggers at timed intervals without requiring object contact
- **AREA_SCHEDULE**: (Future feature) Combines scheduled triggers with area detection

#### TriggerEventType Enum

The `TriggerEventType` enum defines the types of events that triggers can fire:

```typescript
export enum TriggerEventType {
  ENTER = 'enter', // Object enters the trigger zone
  EXIT = 'exit', // Object exits the trigger zone
  STAY = 'stay', // Object stays inside the trigger zone
  SCHEDULE = 'schedule', // Scheduled event fired
}
```

Usage example:

```jsx
import { TriggerType, TriggerEventType } from 'vibe-starter-3d-environment';

// In your trigger configuration
const myTrigger = {
  id: 'my-trigger',
  enabled: true,
  triggerType: TriggerType.CONTACT, // Using the enum
  shape: new BoxTriggerShape(new Vector3(10, 10, 10)),
  actions: [
    {
      onEnter: (event) => {
        if (event.type === TriggerEventType.ENTER) {
          // Checking event type
          console.log('Object entered!');
        }
      },
    },
  ],
};
```

### Contact Triggers vs Schedule Triggers

#### Contact Triggers

Contact triggers activate when objects physically enter, exit, or stay within a defined area:

```jsx
// Box-shaped trigger zone
const boxTrigger = {
  id: 'box-trigger',
  enabled: true,
  triggerType: TriggerType.CONTACT,
  shape: new BoxTriggerShape(new Vector3(10, 5, 10)),
  actions: [
    {
      onEnter: (event) => console.log('Entered!'),
      onExit: (event) => console.log('Exited!'),
      onStay: (event) => console.log('Still inside!'),
    },
  ],
};

// Sphere-shaped trigger zone
const sphereTrigger = {
  id: 'sphere-trigger',
  enabled: true,
  triggerType: TriggerType.CONTACT,
  shape: new SphereTriggerShape(15),
  actions: [
    {
      onEnter: (event) => {
        // Teleport the object
        if (event.target.position) {
          event.target.position.set(0, 10, 0);
        }
      },
    },
  ],
};
```

#### Schedule Triggers

Schedule triggers execute actions at specific time intervals without requiring object contact:

```jsx
// Simple interval trigger
const intervalTrigger = createScheduleTrigger(
  'interval-trigger',
  1000, // Execute every 1000ms
  [], // No conditions
  [
    {
      onAction: () => {
        console.log('Interval triggered!');
      },
    },
  ],
);

// Schedule trigger with conditions
const conditionalSchedule = {
  id: 'conditional-schedule',
  enabled: true,
  triggerType: TriggerType.SCHEDULE,
  schedule: {
    type: 'interval',
    interval: 2000,
    delay: 500,
  },
  conditions: [
    {
      evaluate: () => gameState.isPlaying,
      description: 'Game must be playing',
    },
  ],
  actions: [
    {
      onAction: () => spawnEnemy(),
    },
  ],
};
```

### Trigger Shapes

#### BoxTriggerShape

```jsx
import { BoxTriggerShape } from 'vibe-starter-3d-environment';
import { Vector3 } from 'three';

const boxShape = new BoxTriggerShape(
  new Vector3(10, 5, 10), // size
  new Vector3(0, 0, 0), // position offset
  new Vector3(0, 0, 0), // rotation
);
```

#### SphereTriggerShape

```jsx
import { SphereTriggerShape } from 'vibe-starter-3d-environment';
import { Vector3 } from 'three';

const sphereShape = new SphereTriggerShape(
  10, // radius
  new Vector3(0, 0, 0), // position offset
);
```

### Important Implementation Details

#### Position Provider Types

The TriggerZone accepts various types of objects as targets. Here's how each type is handled:

```jsx
// 1. Three.js Object3D (Mesh, Group, etc.)
const meshTarget = meshRef.current; // Must have .position property

// 2. Vector3 - Static position
const staticTarget = new Vector3(10, 0, 10); // Fixed position

// 3. Rigid Body (Rapier physics)
const rigidBodyTarget = rigidBodyRef.current; // Must have .translation() method

// Example with mixed types
<TriggerZone config={triggerConfig} targets={[playerMesh, enemyRigidBody, new Vector3(0, 0, 0)]} />;
```

#### Collision Detection Methods

The trigger system uses different collision detection based on the target type:

- **Object3D**: Uses the object's position for point-in-shape testing
- **RigidBody**: Uses bounding box intersection for more accurate collision
- **Vector3**: Simple point-in-shape testing

#### Trigger State Management

Each trigger maintains internal state for tracking objects:

```jsx
// The trigger automatically tracks:
// - Which objects are currently inside
// - When objects entered/exited
// - How long objects have been inside

// You can access trigger state through events:
onEnter: (event) => {
  console.log('Object entered at:', event.timestamp);
  console.log('Trigger config:', event.trigger);
  console.log('Target object:', event.target);
};
```

### Common Patterns and Best Practices

#### 1. One-Time Triggers

```jsx
const oneTimeTrigger = {
  id: 'one-time-bonus',
  enabled: true,
  triggerType: TriggerType.CONTACT,
  shape: new BoxTriggerShape(new Vector3(5, 5, 5)),
  actions: [
    {
      onEnter: (event) => {
        // Give bonus
        giveBonus(event.target);

        // Disable trigger after use
        event.trigger.enabled = false;
      },
    },
  ],
};
```

#### 2. Trigger with Visual Feedback

```jsx
function InteractiveTriggerZone({ config }) {
  const [isActive, setIsActive] = useState(false);

  const enhancedConfig = {
    ...config,
    actions: [
      ...config.actions,
      {
        onEnter: () => setIsActive(true),
        onExit: () => setIsActive(false),
      },
    ],
  };

  return (
    <>
      <TriggerZone config={enhancedConfig} targets={[playerRef]} />

      {/* Visual indicator */}
      <mesh position={config.shape.position}>
        <boxGeometry args={[5, 0.1, 5]} />
        <meshStandardMaterial color={isActive ? 'green' : 'red'} opacity={0.5} transparent />
      </mesh>
    </>
  );
}
```

#### 3. Dynamic Target Management

```jsx
function DynamicTargetExample() {
  const [targets, setTargets] = useState([]);

  // Add new target
  const addEnemy = (enemyRef) => {
    setTargets((prev) => [...prev, enemyRef]);
  };

  // Remove target
  const removeEnemy = (enemyRef) => {
    setTargets((prev) => prev.filter((t) => t !== enemyRef));
  };

  return <TriggerZone config={damageZoneConfig} targets={targets} />;
}
```

#### 4. Combining Multiple Triggers

```jsx
function MultiTriggerArea() {
  // Inner trigger - high damage
  const innerTrigger = {
    id: 'inner-damage',
    enabled: true,
    triggerType: TriggerType.CONTACT,
    shape: new SphereTriggerShape(5),
    actions: [
      {
        onStay: (event) => {
          applyDamage(event.target, 10); // High damage
        },
      },
    ],
  };

  // Outer trigger - low damage
  const outerTrigger = {
    id: 'outer-damage',
    enabled: true,
    triggerType: TriggerType.CONTACT,
    shape: new SphereTriggerShape(10),
    actions: [
      {
        onStay: (event) => {
          applyDamage(event.target, 2); // Low damage
        },
      },
    ],
  };

  return (
    <>
      <TriggerZone config={innerTrigger} targets={[playerRef]} />
      <TriggerZone config={outerTrigger} targets={[playerRef]} />
    </>
  );
}
```

### Required Imports

To use TriggerZone components, you need to import the necessary classes and types:

```jsx
// Core imports
import { TriggerZone, TriggerType, TriggerEventType, BoxTriggerShape, SphereTriggerShape } from 'vibe-starter-3d-environment';

// Utility functions
import { createBoxTrigger, createSphereTrigger, createScheduleTrigger, createBoxShape, createSphereShape } from 'vibe-starter-3d-environment';

// Three.js dependencies (required)
import { Vector3 } from 'three';
```

### Type Definitions

For TypeScript users, here are the key interfaces:

```typescript
// Position provider can be one of these types
type PositionProvider = Object3D | Vector3 | RigidBody;

// Trigger configuration
interface TriggerConfig {
  id: string;
  enabled: boolean;
  triggerType: TriggerType;
  shape?: TriggerShape;
  conditions?: TriggerCondition[];
  actions?: TriggerAction[];
  schedule?: ScheduleConfig;
}

// Trigger condition
interface TriggerCondition {
  evaluate: (event?: TriggerEvent) => boolean;
  description?: string;
}

// Trigger action
interface TriggerAction {
  onEnter?: (event: TriggerEvent) => void;
  onExit?: (event: TriggerEvent) => void;
  onStay?: (event: TriggerEvent) => void;
  onAction?: (event: TriggerEvent) => void;
}

// Schedule configuration
interface ScheduleConfig {
  type: 'interval' | 'cron';
  interval?: number;
  delay?: number;
  pattern?: string;
}

// Trigger event
interface TriggerEvent {
  type: TriggerEventType;
  target: PositionProvider | null;
  trigger: TriggerConfig;
  timestamp: number;
}
```

### Complete Working Examples

#### Example 1: Player Detection System

```jsx
import React, { useRef, useState } from 'react';
import { TriggerZone, TriggerType, SphereTriggerShape, createSphereTrigger } from 'vibe-starter-3d-environment';
import { Vector3 } from 'three';

function PlayerDetectionSystem({ playerRef }) {
  const [detectedEnemies, setDetectedEnemies] = useState([]);

  // Create detection trigger
  const detectionConfig = createSphereTrigger(
    20, // Detection radius
    [],
    [
      {
        onEnter: (event) => {
          if (event.target?.userData?.type === 'enemy') {
            setDetectedEnemies((prev) => [...prev, event.target]);
            console.log('Enemy detected!', event.target.userData.name);
          }
        },
        onExit: (event) => {
          if (event.target?.userData?.type === 'enemy') {
            setDetectedEnemies((prev) => prev.filter((enemy) => enemy !== event.target));
            console.log('Enemy left detection range');
          }
        },
      },
    ],
    {
      id: 'player-detection',
      position: new Vector3(0, 0, 0), // Follows player
    },
  );

  return (
    <>
      <TriggerZone
        config={detectionConfig}
        targets={[...enemyRefs]} // Array of enemy references
        debug={false}
      />

      {/* UI showing detected enemies */}
      <div style={{ position: 'absolute', top: 10, left: 10 }}>Detected Enemies: {detectedEnemies.length}</div>
    </>
  );
}
```

#### Example 2: Resource Collection System

```jsx
function ResourceCollectionGame() {
  const playerRef = useRef();
  const [resources, setResources] = useState([]);
  const [score, setScore] = useState(0);

  // Spawn resources periodically
  const resourceSpawnerConfig = {
    id: 'resource-spawner',
    enabled: true,
    triggerType: TriggerType.SCHEDULE,
    schedule: {
      type: 'interval',
      interval: 3000, // Spawn every 3 seconds
      delay: 0,
    },
    actions: [
      {
        onAction: () => {
          const newResource = {
            id: `resource-${Date.now()}`,
            position: new Vector3(Math.random() * 40 - 20, 1, Math.random() * 40 - 20),
            collected: false,
          };
          setResources((prev) => [...prev, newResource]);
        },
      },
    ],
  };

  // Collection trigger for each resource
  const createCollectionTrigger = (resource) => ({
    id: `collect-${resource.id}`,
    enabled: !resource.collected,
    triggerType: TriggerType.CONTACT,
    shape: new SphereTriggerShape(2, resource.position),
    conditions: [
      {
        evaluate: (event) => event.target === playerRef.current,
        description: 'Only player can collect',
      },
    ],
    actions: [
      {
        onEnter: () => {
          setScore((prev) => prev + 10);
          setResources((prev) => prev.map((r) => (r.id === resource.id ? { ...r, collected: true } : r)));
        },
      },
    ],
  });

  return (
    <>
      {/* Resource spawner */}
      <TriggerZone config={resourceSpawnerConfig} targets={[]} />

      {/* Collection triggers */}
      {resources.map(
        (resource) =>
          !resource.collected && (
            <React.Fragment key={resource.id}>
              <TriggerZone config={createCollectionTrigger(resource)} targets={[playerRef]} />
              {/* Resource visual */}
              <mesh position={resource.position}>
                <sphereGeometry args={[0.5]} />
                <meshStandardMaterial color="gold" />
              </mesh>
            </React.Fragment>
          ),
      )}

      {/* Score display */}
      <div>Score: {score}</div>
    </>
  );
}
```

#### Example 3: Area-Based Sound System

```jsx
function AreaSoundSystem() {
  const audioRefs = useRef({});

  // Define sound zones
  const soundZones = [
    {
      id: 'forest-ambience',
      position: new Vector3(0, 0, 0),
      radius: 30,
      soundFile: '/sounds/forest.mp3',
      volume: 0.5,
    },
    {
      id: 'cave-ambience',
      position: new Vector3(50, 0, 0),
      radius: 20,
      soundFile: '/sounds/cave.mp3',
      volume: 0.7,
    },
  ];

  // Create trigger for each sound zone
  const createSoundTrigger = (zone) => ({
    id: zone.id,
    enabled: true,
    triggerType: TriggerType.CONTACT,
    shape: new SphereTriggerShape(zone.radius, zone.position),
    actions: [
      {
        onEnter: () => {
          // Fade in sound
          const audio = audioRefs.current[zone.id];
          if (audio) {
            audio.play();
            audio.volume = 0;
            // Fade in
            const fadeIn = setInterval(() => {
              if (audio.volume < zone.volume) {
                audio.volume = Math.min(audio.volume + 0.1, zone.volume);
              } else {
                clearInterval(fadeIn);
              }
            }, 100);
          }
        },
        onExit: () => {
          // Fade out sound
          const audio = audioRefs.current[zone.id];
          if (audio) {
            const fadeOut = setInterval(() => {
              if (audio.volume > 0) {
                audio.volume = Math.max(audio.volume - 0.1, 0);
              } else {
                audio.pause();
                clearInterval(fadeOut);
              }
            }, 100);
          }
        },
      },
    ],
  });

  // Initialize audio elements
  useEffect(() => {
    soundZones.forEach((zone) => {
      const audio = new Audio(zone.soundFile);
      audio.loop = true;
      audioRefs.current[zone.id] = audio;
    });

    return () => {
      // Cleanup
      Object.values(audioRefs.current).forEach((audio) => {
        audio.pause();
        audio.src = '';
      });
    };
  }, []);

  return (
    <>
      {soundZones.map((zone) => (
        <TriggerZone key={zone.id} config={createSoundTrigger(zone)} targets={[playerRef]} debug={true} />
      ))}
    </>
  );
}
```
