# :camera: Camera Kit
Documentation related to the jo_camera_kit script.

:::: tabs
::: tab BUY
[Buy it on the shop](https://jumpon-studios.com/redm/camera-kit)
:::

::: tab PREVIEW
<iframe width="560" height="315" src="https://www.youtube.com/embed/WBCINAeSkNQ?si=5SwlgLed77a8k1wE" title="RedM - Camera Kit" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
:::
::::

## 1. Installation
To install `jo_camera_kit` :
- Download the library: [jo_libs](https://github.com/Jump-On-Studios/RedM-jo_libs/releases/latest/download/jo_libs.zip)
- Unzip the folder and drop it in your resource folder
- Download `jo_camera_kit` from your [account](https://jumpon-studios.com/account)
- Unzip the folder and drop `jo_camera_kit` in your resource folder
- Add those two ensure in your server.cfg :
  - `ensure jo_libs`
  - `ensure jo_camera_kit`
- Add the item to your items table / file that will be used for the camera item (used in [`Config.items.camera`](#_3-config-lua))
- Add the item to be buyable in your shop script

Congratulation, the **Camera Kit** script is ready to be used!

## 2. Usage

### Get the camera item
- Buy the camera item from a shop that sells it.
- The item name must match `Config.items.camera` (default: `jo_camera`).

### Equip the camera
- Use the camera item in your inventory.
- You’ll equip the in-game camera and see bottom-right prompts.
- Default keys while equipped:
  - `ENTER` — hold to place the tripod
  - `BACKSPACE` — put the camera away

### Placing the tripod
- Hold `ENTER` to start placement.
- Position the preview with your mouse; adjust rotation with your look controls.
- Hold `ENTER` to confirm. A tripod and camera are placed, and the item is removed from your inventory.
- A short setup animation plays. The tripod is now ready to use.

### Using the camera
- Walk near a tripod (not in use) to see interaction prompts.
- Hold `ENTER` to enter photo mode. If `Config.restrictUseToOwner = true`, only the owner can use it.
- In photo mode (default controls):
  - Move: `W/S` and `A/D`
  - Rotate: mouse movement
  - Zoom: mouse wheel up/down
  - Filters: `LEFT/RIGHT` to cycle, `R` to reset
  - Autofocus: `F` to toggle
  - Take photo: left click
  - Open gallery: `G` (only if `Config.useSocialClubGallery = true`)
  - Exit: `ESC`
- When you take a photo, the UI hides and time pauses briefly. After `Config.screenshotTime` seconds, everything restores.

### Moving or removing the tripod
- Near a tripod:
  - Hold `M` to move it. You’ll re-enter placement mode to choose a new spot.
  - Hold `R` to remove it. The camera item is returned to your inventory.
- If `Config.restrictUseToOwner = true`, only the owner can move or remove the tripod.

:::tip Tips
- Prompts appear when within `Config.showCameraPromptsDistance` (default: `2.0`).
- For performance, the script spawns/despawns tripod entities based on `Config.cameraSpawnDistance`.
:::

## 3. Config.lua
The configuration file is `config.lua` in the resource root. **Do not edit this file directly** as your changes may be lost during updates. Instead, use `overwriteConfig.lua` to store your customizations.

- **`config.lua`** - Default configuration maintained by developers. **Do not modify this file.**
- **`overwriteConfig.lua`** - This is where you place only the values you want to override.

### How to customize the configuration

1. Open `jo_camera_kit/overwriteConfig.lua`
2. Find the value you want to change in `config.lua` (e.g., `Config.language`)
3. Copy only that line into `overwriteConfig.lua`
4. Edit the copied value to your liking

The script loads `config.lua` first, then `overwriteConfig.lua` overwrites only the values you redefine. This ensures your customizations are preserved when updating the script.

`jo_camera_kit/shared/config.lua`

```lua
Config = {}

-- Keybind configuration for various actions
-- Available key names: https://docs.jumpon-studios.com/jo_libs/modules/raw-keys/client#keyboard-keys-mapping
Config.keys = {
    useTripod = "ENTER",
    moveTripod = "M",
    removeTripod = "R"
}

-- Keybind configuration for camera controls (while using the camera)
-- Available control hashes: https://github.com/femga/rdr3_discoveries/tree/a63669efcfea34915c53dbd29724a2a7103f822f/Controls
Config.cameraKeys = {
    -- Movement
    moveX = `INPUT_SCRIPTED_FLY_LR`,     -- A/D keys
    moveZ = `INPUT_SCRIPTED_FLY_UD`,     -- W/S keys

    filterNext = `INPUT_FRONTEND_RIGHT`, -- Right arrow
    filterPrev = `INPUT_FRONTEND_LEFT`,  -- Left arrow
    filterOpDown = `INPUT_FRONTEND_DOWN`,
    resetFilter = `INPUT_DROP`,          -- R key

    -- Zoom adjustment
    zoom = `INPUT_SELECT_PREV_WEAPON`,     -- Scroll up
    dezoom = `INPUT_SELECT_NEXT_WEAPON`,   -- Scroll down

    takePhoto = `INPUT_CONTEXT_RT`,        -- LEFT CLICK
    openGallery = `INPUT_INTERACT_ANIMAL`, -- G
    toggleAutofocus = `INPUT_DIVE`,        -- F

    -- Exit
    exit = `INPUT_QUIT`, -- ESC
    hide = `INPUT_PC_FREE_LOOK`,

    -- Advanced
    advanced = `INPUT_CONTEXT_Y`,
    dofNear = { `INPUT_FRONTEND_LEFT`, `INPUT_FRONTEND_RIGHT` },
    dofFar = { `INPUT_FRONTEND_UP`, `INPUT_FRONTEND_DOWN` }
}

Config.items = {
    camera = "jo_camera_kit" -- Item name for camera item in your inventory
}

Config.showCameraPromptsDistance = 2.0 -- Distance to show prompts when near tripod

Config.cameraSpawnDistance = 100.0     -- Distance at which camera entities are spawned in the world

Config.screenshotTime = 2.0            -- Time in seconds to take a screenshot after taking a photo
Config.restrictUseToOwner = false      -- Whether placed camera can be used by other players. True for owner only, false for everyone

Config.useSocialClubGallery = true     -- If true, photos will be saved to Social Club gallery.

Config.cameraMovement = {
    maxHorizontalDistance = 0.3, -- Max distance left/right
    maxVerticalDistance = 0.3,   -- Max distance up/down
    minZoom = 10.0,              -- Maximum zoom in
    maxZoom = 100.0,             -- Maximum zoom out
    defaultZoom = 60.0,          -- Default baseline FOV
    zoomStep = 10.0,             -- FOV change per action
    minPitch = -30.0,            -- Minimum pitch
    maxPitch = 30.0,             -- Maximum pitch
    maxYawRotation = 30.0        -- Maximum yaw rotation left/right from initial position
}
```

`jo_camera_kit/shared/lang.lua`

```lua
Lang = {}
function __(name) return Lang[name] or ("#" .. name) end

-- * ==========================================
-- * PROMPTS
-- * ==========================================

Lang.advancedMode = "Advanced Menu"
Lang.autofocus = "Autofocus"
Lang.cameraSpeed = "Camera speed: x%.3f"
Lang.changeFilter = "Change Filter"
Lang.dofFar = "Far Focus"
Lang.dofNear = "Near Focus"
Lang.easyMode = "Easy Mode"
Lang.exit = "Exit Camera"
Lang.filter = "Filter"
Lang.hidePrompts = "Hide prompts"
Lang.moveCamera = "Move Camera"
Lang.moveTripod = "Move Camera"
Lang.none = "none"
Lang.OFF = "OFF"
Lang.ON = "ON"
Lang.openGallery = "Open Gallery"
Lang.removeTripod = "Remove Camera"
Lang.resetFilter = "Reset Filter"
Lang.setTripod = "Place Camera"
Lang.takePhoto = "Take Photo"
Lang.unequipe = "Unequipe"
Lang.useTripod = "Use Camera"
Lang.zoom = "Zoom +/-"

-- * ==========================================
-- * NOTIFICATIONS
-- * ==========================================

Lang.alreadyInUse = "The camera is already in use"
Lang.cantReachCameraError = "Can't reach camera"
Lang.notAllowed = "You are not allowed to do that."
Lang.notOwner = "You are not the owner of this camera"
Lang.dontHaveCameraItem = "You don't have the camera item"
```

## 4. For developers

### Filters

[Filters](/DeveloperResources/filters) are the new way to modify data used by the script added in the `v1.2.0`. These filters are fired at a specific point in time during the execution of the script. But contrary to events, filters are **synchronous**. 

#### `[Server]` canMoveCamera
Controls who can move a camera

```lua
-- @param canMoveCamera - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param camera - the camera object
exports.jo_camera_kit:registerFilter('canMoveCamera', function(canMoveCamera, source, camera)
    return canMoveCamera
end)
```
#### `[Server]` canPlaceCamera
Controls who can place a camera

```lua
-- @param canPlaceCamera - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param position - Coords of the camera
-- @param heading - Heading of the camera
exports.jo_camera_kit:registerFilter('canPlaceCamera', function(canPlaceCamera, source, position,heading)
    return canPlaceCamera
end)
```
#### `[Server]` canRemoveCamera
Controls who can remove a camera

```lua
-- @param canRemoveCamera - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param camera - the camera object
exports.jo_camera_kit:registerFilter('canRemoveCamera', function(canRemoveCamera, source, camera)
    return canRemoveCamera
end)
```

#### `[Server]` canUseCamera
Controls who can use a camera

```lua
-- @param canUseCamera - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param camera - the camera object
exports.jo_camera_kit:registerFilter('canUseCamera', function(canUseCamera, source, camera)
    return canUseCamera
end)
```

### Actions

Actions are one of the two types of Hooks. They provide a way for running a function at a specific point in the execution of scripts. Callback functions for an Action do not return anything back to the calling Action hook. They are the counterpart to Filters.
  
Below is a complete list of all available actions in the script.

#### `[Server]` cameraExited
Triggered when a camera is exited

```lua
-- @param source - serverID of the player
-- @param cameraId - the camera id
exports.jo_camera_kit:registerAction('cameraExited', function(source, cameraId)
    -- Your code here
end)
```

#### `[Server]` cameraMoved
Triggered when a camera is moved

```lua
-- @param source - serverID of the player
-- @param cameraId - the camera id
exports.jo_camera_kit:registerAction('cameraMoved', function(source, cameraId)
    -- Your code here
end)
```

#### `[Server]` cameraPlaced
Triggered when a camera is placed on the ground

```lua
-- @param source - serverID of the player
-- @param camera - the camera object
exports.jo_camera_kit:registerAction('cameraPlaced', function(source, camera)
    -- Your code here
end)
```

#### `[Server]` cameraRemoved
Triggered when a camera is removed

```lua
-- @param source - serverID of the player
-- @param cameraId - the camera id
exports.jo_camera_kit:registerAction('cameraRemoved', function(source, cameraId)
    -- Your code here
end)
```

#### `[Server]` cameraUsed
Triggered when a camera is used

```lua
-- @param source - serverID of the player
-- @param camera - the camera object
exports.jo_camera_kit:registerAction('cameraUsed', function(source, camera)
    -- Your code here
end)
```
