# Bounty Hunter

`codex_bounty_hunter` adds **interactive bounty boards** + **hunt missions** + an optional **Bounty Hunter License (ID)** flow for RedM.

This documentation is written for **server owners and developers**.\
Because the main logic is protected by CFX escrow, you only edit what is intentionally exposed (the `Configs/*.lua` files and the optional `Configs/FunctionsConfig.lua` hooks).

### What players can do

* Walk up to a bounty board (Valentine / Blackwater / Saint Denis / Rhodes / Strawberry by default)
* Open the **Bounty Board UI**
* **Accept** a bounty (a configurable deposit is taken)
* Follow a **clue phase**, then a **hunt phase**
* Deliver the target **alive or dead** (some bounties can require alive)
* Get paid on success
* Officers can **issue a Bounty Hunter License (ID)**, which can be required to open the board

### What you (server owner) can configure

* Board locations / objects
* Spawn pools and clue locations per board
* Mission templates (NPC models, guard count, weapons, rewards multipliers)
* Framework type (VORP / RSG / TPZ)
* Deposit percentage and money type
* Time limit per mission
* Law job list & grade needed to issue IDs
* Enable/disable license requirement to open the board
* Optional integration hooks (money handling, open/close UI hooks, etc.)

Use the sidebar pages to configure, install and customize.

***

## How it works

This section explains the **runtime flow** in a way that matches what the resource actually does.

### 1) Board objects exist in the world

The script spawns (or reuses if already present) bounty board objects at the coordinates defined in **`Configs/ObjectsConfig.lua`**.

Each object entry binds a **board name** (example: `Valentine`) to a set of world coordinates.

### 2) Players open the board UI

When a player is close enough to a configured board object, they get a **Codex Prompt** (hold key) to open the board.

When opened:

1. The client sets NUI focus and shows the UI
2. The client notifies the server the UI is open for that board
3. The client requests the list of bounties for that board via a server callback
4. The UI renders the list and allows **Accept / Track / Clear**

#### License gate (optional)

If `CodexConfig.EnableCheckBountyIDs = true`, the client calls a server callback first to verify the player has a valid issued Bounty Hunter ID (issued by an allowed law job). If the player has no valid license, the board will not open.

### 3) Accepting a bounty → creates a live bounty row

When a player presses **Accept** in the UI:

* The server reads the selected bounty template row (from `codex_bounty_templates`)
* A **deposit** is charged: `deposit = floor(reward * CodexConfig.PercentageMoney)`
* A new active bounty row is inserted into `codex_bounties`
* A mission payload is built from your **spawn pool configs** (`Configs/BountySpawns.lua`) and **mission templates** (`Configs/MissionsConfig.lua`)
* The server starts the mission for that player by sending the payload to the client

Important: Only **one active mission per player** is allowed at a time.

### 4) Mission phases

#### Phase A — Clues

The mission starts in a **clue collection** phase:

* The client places blips for each clue location
* A clue object is spawned (prop model is configurable)
* The player must collect `requiredClues` clues (from your spawn entry)

#### Phase B — Hunt

Once enough clues are collected:

* The target NPC and guards are spawned at the configured spawn point
* The target gets a blip
* Guards and target attack the player

Some mission templates can allow the target to **attempt escape** (and in the “Drug Baron” template the target can spawn a horse and flee).

#### Phase C — Deliver

When the target is detected as either:

* **Dead** (killed), or
* **Captured alive** (hogtied / cuffed — best-effort detection)

…the player must go to the **drop-off** location for that board.

When they enter the drop-off radius, the client reports completion to the server and cleanup runs.

### 5) Time limit and failure

A mission time limit is enforced server-side using `CodexConfig.MissionTimeLimitSeconds`:

* If the timer expires, the server fails the mission (`time_expired`)
* If the player dies during an active mission, the client reports failure (`player_died`)

When a mission fails, the bounty row is marked inactive so it disappears from boards.

### 6) Payout

On completion:

* If `requiresAlive = true` and the target is delivered dead → the mission fails (`target_killed`) and pays nothing.
* Otherwise, payout is:

`finalReward = floor(rewardBase * multiplier)`

Where multiplier comes from the mission template:

* `rewardAliveMult` when delivered alive
* `rewardDeadMult` when delivered dead

Money is paid using the default Codex Core account functions, unless you enable hooks and override the money functions.

***

## Installation

### Dependencies

The resource declares these dependencies:

* `codex_core`
* `oxmysql`
* `ox_lib` (initialized via `@ox_lib/init.lua`)

Make sure all three are started **before** `codex_bounty_hunter`.

### Basic install steps

1. Put the resource folder in your server resources (example: `resources/[codex]/codex_bounty_hunter`)
2. Ensure dependencies start first:
   * `ensure oxmysql`
   * `ensure ox_lib`
   * `ensure codex_core`
3. Start this resource:
   * `ensure codex_bounty_hunter`
4. Import the SQL (see the **Database** page)
5. Configure board positions and mission settings in `Configs/*.lua`

### Common “first run” checklist

* Verify `oxmysql` connection string is correct
* Verify `codex_core` is working and exports are available
* Confirm your framework setting matches your server:
  * `CodexConfig.Framework = 'vorp' | 'rsg' | 'tpz'`
* Confirm you have at least one bounty template row in `codex_bounty_templates` for each board you want active

***

## Configuration

All user-editable settings for escrow-protected servers are in `Configs/*.lua`.

### `Configs/config-main.lua` (CodexConfig)

#### Core

* `DeveloperMode`\
  Debug printing. **Do not enable on live servers.**
* `Framework`\
  `'vorp'`, `'rsg'`, or `'tpz'`
* `EnableCodexPrompts`\
  Enables the “hold key” prompts for opening boards and collecting clues.
* `UseHooks`\
  When `true`, the resource will call functions you place in `Configs/FunctionsConfig.lua`.

#### Bounties & missions

* `EnableCheckBountyIDs`\
  When `true`, players must have a valid issued Bounty Hunter license (ID) to open bounty boards.
* `MaxBountiesPerPlayer`\
  Maximum bounties a player can have (the resource also enforces **one active mission at a time**).
* `PercentageMoney`\
  Deposit ratio required to accept a bounty.\
  Example: `0.5` means a bounty with reward `$1000` requires `$500` deposit.
* `MoneyType`\
  Account type used by CodexCore money functions:
  * `0` money
  * `1` gold
  * `2` roll
* `MissionTimeLimitSeconds`\
  Server-enforced time limit. Example default is `1800` (30 minutes).

#### Law / issuing IDs

* `JobGradeAccess`\
  Minimum job grade required to issue IDs with `/issueid`.
* `LawJobsAllowed`\
  A list of jobs that count as law enforcement (also used to validate ID issuers).
* `LicenseItem`\
  Item name given when a player completes license info (default: `bounty_license`).

#### Keys

`CodexConfig.Keys` contains key hashes. The script uses `CodexConfig.Keys["G"]` for prompts by default.

If you change a key value, use only valid key hashes for RedM.

### `Configs/ObjectsConfig.lua` (CodexDataObjects)

Defines bounty board object placements:

* `ModelName` object model (default `mp005_p_mp_bountyboard02x`)
* `ModelCoords` world position
* `ModelHeading` heading
* `DistanceToReact` how close the player must be
* `BoundBoardName` board label shown/used when opening the UI

The board name is later normalized to a lowercase internal name:

* Valentine → `valentine`
* Blackwater → `blackwater`
* Saint Denis → `saintdenis`
* Rhodes → `rhodes`
* Strawberry → `strawberry`

### `Configs/BountySpawns.lua` (CodexBountySpawns)

Defines the **spawn pool** for each board (must match the internal board name used in DB and normalization).

Each entry contains:

* `id` unique spawn ID
* `type` mission template key (must exist in `MissionsConfig.lua` templates)
* `spawn` coordinates + heading
* `searchRadius` used for clue area (payload)
* `requiredClues` number of clues needed to progress
* `clues` list of clue positions; each clue can include a `type` (campfire / footprints / witness, etc.) for your own organization.

### `Configs/MissionsConfig.lua` (CodexBountyMissions)

#### Drop-offs

`DropOffs` defines the delivery location per board.

#### Clue prop

`CluePropModel` defines the prop model used as clue objects.

#### Templates

Templates define how targets and guards spawn and behave.

Included templates:

* `basic_outlaw`
* `gang_leader`
* `drug_baron`

Each template includes:

* `label`
* `targetModel`
* `guards` and `guardModels`
* `weapons`
* `spawnRadius`
* `escape` (target can flee)
* `baronEscapeOnHorse` (Drug Baron only)
* `escapeHealthPercent` (when to flee)
* reward multipliers:
  * `rewardAliveMult`
  * `rewardDeadMult`

***

## Bounty boards & locations

Boards are defined in `Configs/ObjectsConfig.lua` as world objects.

Default locations included:

* Valentine
* Blackwater
* Saint Denis
* Strawberry
* Rhodes

To add or move a board:

1. Add a new entry to `CodexDataObjects`
2. Choose the world coordinates and heading
3. Set a `BoundBoardName` (example `"Valentine"`)
4. Ensure your database has bounty templates for the corresponding internal board name

### Board name matching (important)

The system normalizes board names by:

* lowercasing
* removing spaces

Then matches known boards like:

* `"Saint Denis"` → `saintdenis`

Your `codex_bounty_templates.board` values must use the internal names (e.g. `saintdenis`, not `Saint Denis`).

Your `Configs/BountySpawns.lua` keys also must use the internal names.

***

## Missions

### Mission selection

When a bounty is accepted, the mission payload is built from:

* The newly created bounty row (`codex_bounties`)
* A random spawn entry from `Configs/BountySpawns.lua` for that board
* The mission template referenced by the spawn entry (`type`) in `Configs/MissionsConfig.lua`

### Clues phase

* Each spawn entry provides a list of clue positions.
* The player collects clues until `requiredClues` is reached.
* Clues show as map blips and spawn clue objects (prop model from `CodexBountyMissions.CluePropModel`).

### Hunt phase

After enough clues:

* Target ped spawns using the template `targetModel`
* Guards spawn using template `guardModels` and `guards` count
* Weapons are randomly selected from template `weapons`

#### Escape behavior

If a template has `escape = true`, the target can flee when:

* target health percentage drops below `escapeHealthPercent`, OR
* most guards are dead (>= 60% dead)

For `drug_baron`, if `baronEscapeOnHorse = true`, a horse is spawned and the target mounts and flees.

### Delivery

Each board has a drop-off coordinate in `CodexBountyMissions.DropOffs`.

Delivery is detected when the player comes close enough to the drop-off after the target is:

* dead, or
* captured alive (hogtied/cuffed best-effort)

### Success payout

Payout uses the reward multipliers from the template:

* Alive payout: `floor(rewardBase * rewardAliveMult)`
* Dead payout: `floor(rewardBase * rewardDeadMult)`

If the bounty requires alive delivery (`requiresAlive = true`) and the target is dead, the mission fails with no payout.

***

## Bounty Hunter License (ID)

This resource includes a **Bounty Hunter License** system.

### What it is

* Law enforcement can issue an ID card to a player.
* The ID is stored in the database table `codex_bounty_ids`.
* The player can fill out extra license information in the UI and receive the configured license item (`CodexConfig.LicenseItem`).

### Require license to open boards

If `CodexConfig.EnableCheckBountyIDs = true`, players must have a valid ID row:

* The server checks `codex_bounty_ids` for the player’s name.
* The issuer job must match one of the jobs in `CodexConfig.LawJobsAllowed`.

If the player does not have a valid ID, the board UI will not open and they will be notified.

### Issuing IDs (law officers)

Client command:

```
/issueid <serverId>
```

Rules:

* Your job must be in `CodexConfig.LawJobsAllowed`
* Your job grade must be >= `CodexConfig.JobGradeAccess`

When issued:

* The target player is shown an ID overlay (NUI)
* The server stores (or updates) the ID row in `codex_bounty_ids`

### Completing license info

When the ID overlay is shown for an issued ID (not view-only), the player can fill in:

* First name, last name
* Birth year (must be 1700–1899)
* Town of birth
* Father’s first and last names

On successful save:

* the DB row is updated and marked complete
* the player receives `CodexConfig.LicenseItem` (default `bounty_license`)

### Showing your license item to another player

The configured license item is registered as usable. When used:

* The script finds the nearest player
* It shows the license UI to that nearby player in **view-only** mode

***

## Hooks & integration

Because escrow hides most core logic, this resource exposes an optional hook file:

`Configs/FunctionsConfig.lua`

Enable hooks by setting:

```lua
CodexConfig.UseHooks = true
```

### Available hook functions

#### Client hooks

* `CodexFunctions.Client.OnOpenBoard(boardName)`\
  Called when a player tries to open the board UI.\
  Return `false` to block opening; return `true` or `nil` to allow.
* `CodexFunctions.Client.OnCloseBoard()`\
  Called when the board UI is closed.

#### Server hooks

* `CodexFunctions.Server.OnAcceptBounty(source, bountyId)`\
  Called when a bounty is accepted.
* `CodexFunctions.Server.OnTrackBounty(source, bountyId)`\
  Called when a bounty is tracked.
* `CodexFunctions.Server.OnClearBounty(source)`\
  Called when a player clears their active bounty.
* `CodexFunctions.Server.OnIssueID(issuerSource, targetPlayerId, targetName)`\
  Called when an officer issues an ID.

#### Money integration hooks

If you do **not** want to use CodexCore’s money functions, implement:

* `CodexFunctions.Server.TryTakeMoney(source, amount) -> boolean`

Optionally you can also implement:

* `CodexFunctions.Server.GiveMoney(source, amount) -> boolean`

If a money hook is enabled and returns `true`, it is considered successful. If it errors or returns `false`, the resource falls back to its default behavior.

### Example: integrating with another money system

```lua
CodexFunctions.Server.TryTakeMoney = function(source, amount)
  -- Check and remove money from your framework.
  -- Return true if removed; false if insufficient.
  return false
end

CodexFunctions.Server.GiveMoney = function(source, amount)
  -- Add money using your framework.
  return true
end
```

> Keep hook functions small and safe: the core calls them using protected `pcall`.

***

## Database

This resource uses **oxmysql** and expects specific tables.

### Tables used

#### `codex_bounty_templates`

Templates shown on the board UI. The Accept button reads from this table.

Required columns used by the resource:

* `id` (primary key)
* `board` (internal board name: `valentine`, `blackwater`, `saintdenis`, `rhodes`, `strawberry`, etc.)
* `name`
* `reward`
* `description`
* `location`
* `active` (1/0)

Optional column (read safely):

* `capture_alive` (1/0)\
  If present and set to 1, that bounty requires alive delivery.

#### `codex_bounties`

Live bounties created when a player accepts a bounty. The UI “Accepted” state is based on this table.

Columns used:

* `id`
* `name`
* `reward`
* `description`
* `location`
* `board`
* `active`
* `accepted_by`
* `accepted_at`
* `tracked_by`

When a mission finishes or fails, the row is set inactive (`active = 0`).

#### `codex_bounty_ids`

Stores issued bounty hunter IDs.

Columns used:

* `id` (format: `BH-00000`)
* `name` (player name)
* `issuer_job`
* `issued_by_officer` (1/0)
* `issued_at` (timestamp)
* `bh_info_completed` (1/0)
* License info fields saved on completion:
  * `bh_first_name`, `bh_last_name`
  * `bh_birth_year`, `bh_town_birth`
  * `bh_father_first`, `bh_father_last`

### Importing SQL

Use the SQL files provided with the resource package to create these tables.

If your package contains:

* `create_codex_bounties.sql`
* `create_codex_bounty_ids.sql`

…import them with your preferred MySQL tool (phpMyAdmin, HeidiSQL, etc.) into the same database used by oxmysql.

***

## Commands, events & callbacks

This page lists what a developer/server owner can rely on.

### Commands

#### `/issueid <serverId>`

Client command for law officers to issue a Bounty Hunter ID to a player.

* Requires job in `CodexConfig.LawJobsAllowed`
* Requires grade >= `CodexConfig.JobGradeAccess`

#### `/codex_bounty_refresh [board]`

Server command to force-refresh open bounty board UIs (admin only).

* With a board name: refreshes only that board’s open UIs
* Without args: refreshes all open UIs

### Server callbacks (CodexCore)

* `codex_bounty:server:GetBounties(boardName)`\
  Returns board bounties from `codex_bounty_templates`.
* `codex_bounty:server:CheckPlayerBountyIDs()`\
  Checks if the player has a valid issued license in `codex_bounty_ids`.
* `codex_bounty_hunter:CheckCooldown()`\
  Queries `codex_bounty_ids` for the player’s name and returns `(true, id)` if found, otherwise `false`.

### Server events

* `codex_bounty:server:UIOpened(boardName)`
* `codex_bounty:server:UIClosed()`
* `codex_bounty:server:AcceptBounty(templateId)`
* `codex_bounty:server:TrackBounty(bountyId)`
* `codex_bounty:server:ClearBounty()`
* `codex_bounty:server:CompleteMission({ bountyId, alive })`
* `codex_bounty:server:FailMission({ reason })`
* `codex_bounty:server:FailMissionDirect(targetSrc, reason)`
* `codex_bounty:server:IssueID(targetPlayerId, targetName)`
* `codex_bounty:server:SaveBHInfo(data)`
* `codex_bounty:server:RegisterBH(data)` (generates an ID payload and shows it)

### Client events

* `codex_bounty:client:OpenBountyBoardUI(boardName)`
* `codex_bounty:client:PushBounties(bounties, playerServerId)`
* `codex_bounty:client:StartMission(payload)`
* `codex_bounty:client:MissionFinished({ paid })`
* `codex_bounty:client:MissionFailed({ reason })`
* `codex_bounty:client:ShowIssuedID(payload)`
* `codex_bounty:client:ShowBountyID(payload)`
* `codex_bounty:client:UseLicenseNearest`
* `codex_bounty:client:Notify(msg, time)`

***

## Troubleshooting

### The board prompt never appears

Checklist:

* `CodexConfig.EnableCodexPrompts = true`
* Your board object coords in `Configs/ObjectsConfig.lua` are correct
* The model name is valid (`mp005_p_mp_bountyboard02x` by default)
* You are within `DistanceToReact`

### The board UI opens but is empty

* Confirm you have rows in `codex_bounty_templates` where:
  * `board` matches the internal name (`valentine`, `blackwater`, etc.)
  * `active = 1`
* Confirm oxmysql is connected and working
* Use `/codex_bounty_refresh` to refresh open UIs (admin)

### Players cannot open boards (license error)

* Set `CodexConfig.EnableCheckBountyIDs = false` to disable license requirements, OR
* Ensure players have rows in `codex_bounty_ids` issued by a job in `CodexConfig.LawJobsAllowed`

### Accept button does nothing

* Ensure `oxmysql` is running
* Ensure the bounty template ID exists and is active
* Check server console for “\[Codex Bounty]” logs

### Mission fails instantly

Common causes:

* Another active mission exists for that player (only one active mission per player)
* The mission payload failed to build because there are no spawn entries for that board in `Configs/BountySpawns.lua`

### Payout doesn’t work

* If you enabled hooks, ensure your money hooks return `true` on success
* Otherwise, ensure `codex_core` account functions are working and `CodexConfig.MoneyType` matches your economy setup

### Developer debugging tips

* Enable `CodexConfig.DeveloperMode = true` temporarily to print helpful logs.
* Disable it again for production servers.

***
