> For the complete documentation index, see [llms.txt](https://davedev.gitbook.io/wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://davedev.gitbook.io/wiki/immersive-flashlight/info/development-resources.md).

# Development Resources

## <i class="fa-brain">:brain:</i> Information

Use this page to wire the flashlight into frameworks, inventories, targeting/handcuff systems, notifications, and custom UI. It lists every export and event, clarifies payload shapes, and shows the safest extension points.

#### High-level architecture

* The client owns the local prop, animations, beam rendering, corona, player controls, restriction checks, and local state-bag values.
* The server validates configured item requirements, tracks active state, relays orientation/beam/tuning data to nearby players, and selects the active audio provider.
* Core adapters are resolved from `framework.core`; inventory adapters are resolved separately from `framework.inventory`.
* The simple `setup` block configures the common core, inventory, item, and consumption behavior before adapters are resolved.
* Public integrations should use the documented exports, trusted bridge event, and state bags. Orientation/beam network events are internal synchronization details and should not be treated as a stable public API.

### Payloads used by the resource

Most integrations use the same payload table. Fields are optional but recommended:

```lua
{
  item = 'flashlight',           -- item name consumed/used
  slot = 12,                     -- inventory slot (if available)
  metadata = { serial = 'A-1' }, -- passthrough metadata
  framework = 'ox'               -- label of the integration that fired the use
}
```

Missing fields are tolerated; the script treats payload as context, not authority.

***

### Server Surface

**Exports**

| Export                 | Signature                                                              | Purpose                                                                                                                              |
| ---------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `UseFlashlight`        | `exports['DaveDev_Flashlight']:UseFlashlight(source, payload?)`        | Requests a toggle and runs the configured item check unless the active flow bypasses it.                                             |
| `UseFlashlightTrusted` | `exports['DaveDev_Flashlight']:UseFlashlightTrusted(source, payload?)` | Toggles after an external server-side inventory/framework has already verified the player. Do not call from client-controlled input. |
| `GetFrameworkAdapter`  | `exports['DaveDev_Flashlight']:GetFrameworkAdapter()`                  | Returns the active inventory adapter name for backwards compatibility.                                                               |
| `GetAdapters`          | `exports['DaveDev_Flashlight']:GetAdapters()`                          | Returns `{ core, inventory }` for the currently resolved adapters.                                                                   |
| `GetBridgeInfo`        | `exports['DaveDev_Flashlight']:GetBridgeInfo()`                        | Returns resolved adapters, item names, item/consumption state, standalone status, and configured bridge event names.                 |

**Trusted server event**

The default trusted bridge event is server-only:

```lua
TriggerEvent('davedev_flashlight:server:UseTrusted', source, {
  item = 'flashlight',
  slot = slot,
  metadata = metadata,
  framework = 'my_inventory',
})
```

Its name can be changed with `FlashlightConfig.bridge.trustedServerEvent`.

{% hint style="danger" %}
Do not expose the trusted export/event through a client-triggered server event unless your handler performs its own server-side ownership and permission checks.
{% endhint %}

**Internal events**

`davedev_flashlight:server:Use`, orientation, beam-state, tuning, unmounted, and SFX-mode events are used internally by the resource. External resources should prefer the public exports, trusted bridge, client exports, and state bags documented on this page.

**Notification routing**

Configure `integrations.notify` in `config.lua`:

* `mode = 'auto' | 'ox_lib' | 'qbcore' | 'chat' | 'custom' | 'disabled'`
* `customEvent` receives `{ description = message, type = messageType }` on the client.
* `handler` is a server-side Lua function: `function(source, message, messageType) return true end`. Return true to suppress the default routes.

***

#### Client surface

**Client exports**

| Export                             | Returns                                   | Description                                                                                              |
| ---------------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `IsFlashlightActive()`             | `boolean`                                 | True while the local player's flashlight is equipped.                                                    |
| `IsFlashlightAiming()`             | `boolean`                                 | True while the local player is actively aiming the flashlight.                                           |
| `IsFlashlightLightOn()`            | `boolean`                                 | True when the local player's equipped flashlight beam is on.                                             |
| `GetBeamTransform()`               | `{ origin, dir, aiming, lightOn } \| nil` | Returns the local beam origin/direction and state while active, otherwise nil.                           |
| `GetConeParams()`                  | `{ angleDeg, distance }`                  | Returns the configured evidence cone values adjusted by the local tuning step.                           |
| `CanUseItem()`                     | `boolean`                                 | True when the local flashlight is not currently active.                                                  |
| `useItem(item?, slot?, metadata?)` | `false`                                   | Inventory callback that sends a normal server use request and therefore respects configured item checks. |
| `UseItem(item?, slot?, metadata?)` | `false`                                   | Alias of `useItem`.                                                                                      |

**State bag keys**

* `LocalPlayer.state.davedev_flashlight_active` - replicated boolean indicating that the local flashlight is equipped.
* `LocalPlayer.state.davedev_flashlight_light_on` - replicated boolean indicating that the local beam is on.
* `LocalPlayer.state.canUseWeapons` - temporarily changed while the flashlight is active and restored during cleanup.

{% hint style="info" %}
The synchronization net events are implementation details. Use exports and state bags for integrations so future internal networking changes do not break your resource.
{% endhint %}

### <i class="fa-desktop-arrow-down">:desktop-arrow-down:</i> Integration patterns

**Custom inventory bridge**

The safest integration point is the trusted server export inside an inventory callback that has already verified item ownership:

```lua
-- server-side, inside your inventory's usable-item callback
local function useFlashlight(source, itemName, slot, metadata)
  exports['DaveDev_Flashlight']:UseFlashlightTrusted(source, {
    item = itemName,
    slot = slot,
    metadata = metadata,
    framework = 'my_inventory',
  })
end
```

Use the untrusted export when you want DaveDev Flashlight to perform its configured item check:

```lua
exports['DaveDev_Flashlight']:UseFlashlight(source, {
  item = 'flashlight',
  framework = 'external_script',
})
```

Do not wrap `UseFlashlightTrusted` in an unrestricted `RegisterNetEvent`; that would allow clients to bypass the inventory verification the trusted bridge expects.

#### Notification override

```lua
FlashlightConfig.integrations.notify = {
  mode = 'custom',
  customEvent = 'my_ui:notify', -- client event
  handler = function(source, message, msgType)
    -- Return true to suppress built-in routing
    print(('[Flashlight] %s (%s)'):format(message, msgType or 'info'))
    return false
  end,
}
```

**Handcuff or interaction checks**

Automatic framework/state-bag detection:

```lua
FlashlightConfig.integrations.handcuffCheck = {
  enabled = true,
  framework = 'auto',
  metadataPaths = {},
  stateBagKeys = {},
  resource = '',
  export = '',
  expects = true,
}
```

Force a custom export check:

```lua
FlashlightConfig.integrations.handcuffCheck = {
  enabled = true,
  framework = 'export',
  resource = 'my_police',
  export = 'IsPlayerCuffed',
  expects = true,
}
```

Supported forced framework values are `qbcore`, `qbox`, `ox`, `esx`, `statebag`, `export`, and `disabled`.

For targeting resources, change `integrations.disableTarget.resource` and `integrations.disableTarget.export` to match the target API. The configured export is called with a boolean state while the flashlight equips and holsters.

#### Swapping audio providers at runtime

* Keep `sfx.xsound.prefer = true` if xsound should be favored when it is started.
* Keep `sfx.interactsound.prefer = true` if Interact-Sound should be favored when it is started.
* To force the bundled fallback, set `prefer = false` and leave `sfx.builtin.autoStart = true`.
* The server watches resource start/stop events for both names and rebroadcasts `davedev_flashlight:client:SfxMode` automatically.

#### Using beam data in other scripts

```lua
local data = exports.davedev_flashlight:GetBeamTransform()
if data and data.lightOn then
  -- Example: check if the beam hits a target point
  local rayLength = 20.0
  local target = data.origin + (data.dir * rayLength)
  -- do your hit test or debug draw here
end
```

#### Evidence/cone integration

```lua
local cone = exports.davedev_flashlight:GetConeParams()
-- cone.angleDeg, cone.distance reflect player tuning
MyEvidenceSystem.RegisterFlashlight(cone.angleDeg, cone.distance)
```

#### Framework adapter behavior

* **Qbox/QBCore core:** Uses the QB-compatible core adapter. Standard Qbox uses the `qbx_core` resource name; QBCore uses `qb-core`.
* **ox\_core:** Resolves the configured `ox_core` resource for core/notification behavior.
* **ESX core:** Resolves `es_extended` and exposes the available ESX player information.
* **ox\_inventory:** Does not register usable items. Items call `DaveDev_Flashlight.useItem` through `client.export`; ownership checks use the `Search` export.
* **QB-compatible inventory:** Registers `QBCore.Functions.CreateUseableItem`, reads `GetItemByName`, and optionally removes the item after use.
* **QS-Inventory:** Uses the available QS usable-item and ownership/removal exports, with an optional QB-compatible player fallback.
* **ESX inventory:** Registers `ESX.RegisterUsableItem`, checks item counts, and optionally removes one item.
* **Standalone:** Provides command/export use only. Ownership checks, usable-item registration, and consume-on-use are disabled.
* **Custom core:** Configure `framework.core.custom` and edit `framework/custom.lua`.
* **Custom inventory:** Configure `framework.inventory.custom` and edit `framework/inventory/custom.lua`, or use `UseFlashlightTrusted` directly from an existing inventory callback.

### Safety and rate limits

* Orientation updates are throttled by `net.shareIntervalMs` (default 80ms); beam state is throttled at 100ms to reduce spam.
* Remote visibility uses `net.maxShareDistance` (default 150.0). Keep this aligned with your average engagement distances to save bandwidth.
* Tuning (`davedev_flashlight:server:Tuning`) is clamped to plus/minus 20 steps and broadcast only within range.

#### Quick reference: when to use what

* **Existing server inventory already verified the item:** `exports['DaveDev_Flashlight']:UseFlashlightTrusted(source, payload)`.
* **External server script should use the configured item check:** `exports['DaveDev_Flashlight']:UseFlashlight(source, payload)`.
* **ox\_inventory item callback:** `client.export = 'DaveDev_Flashlight.useItem'`.
* **Inspect active adapters/configured bridge:** `GetAdapters()` and `GetBridgeInfo()`.
* **Drive local UI/evidence logic:** `GetBeamTransform()`, `GetConeParams()`, and the flashlight state bags.
* **Silence notifications:** `integrations.notify.mode = 'disabled'`.
* **Disable all flashlight audio:** `sfx.enabled = false`.
* **Replace bundled sounds:** update `sfx.sounds` and place matching files in the active audio provider.
