# Exports and events

Koja Fishing exposes exports and events so other resources — a battle pass, a job center, a police MDT, a Discord logger — can read angler data and react to catches, level-ups, fines and more.
Exports use the resource name `koja-fishing`.
Server exports are listed first, client exports and events at the end.

:::hint{type="info" title="Player id or identifier"}
Most server exports take a `target`: an online player id, or a character identifier.
With an identifier they also work for offline players, unless noted otherwise.
:::

## Player data

### GetPlayerData

```lua
local data = exports['koja-fishing']:GetPlayerData(source)        -- online player id
local data = exports['koja-fishing']:GetPlayerData('char1:abc')   -- identifier, works offline
```

Returns `nil` when the player has no fishing data, otherwise a table:

| Field | Value |
|---|---|
| `identifier` | Character identifier |
| `online` | `true` when the player is connected |
| `source` | Player id (only when online) |
| `level`, `exp`, `expToNext` | Fishing level, XP and XP needed for the next level |
| `license` | `true` when the player has a fishing licence |
| `testAttempts`, `testCooldownUntil` | Licence exam attempts and cooldown end (unix time) |
| `skillPoints` | Unspent skill points |
| `tutorialDone` | `true` when the fishing guide is finished |

### GetLevel / SetLevel

```lua
local level = exports['koja-fishing']:GetLevel(source)
exports['koja-fishing']:SetLevel('char1:abc', 10) -- resets XP within the level, capped at Config.MaxLevel
```

Both accept a player id or an identifier.
`SetLevel` returns `true` on success.
A raised level counts like an earned one: the player gets the skill points for every level gained and, when online, the level-up screen and the `koja-fishing:server:levelUp` event.

### AddExp

Gives fishing XP. Level-ups and skill points are handled for you.

```lua
exports['koja-fishing']:AddExp({ playerId = source, exp = 50 })
exports['koja-fishing']:AddExp({ identifier = 'char1:abc', exp = 50 })
```

The player's data must be loaded (they are online).

### AddSkillPoints

```lua
exports['koja-fishing']:AddSkillPoints(source, 2)    -- negative values remove points (never below 0)
```

Returns `true` on success.

### GetSkillLevel

```lua
local level = exports['koja-fishing']:GetSkillLevel(identifier, 'wedkarz_luck')
```

Takes the character identifier and a skill id from `data/skills.json`. Returns the purchased level (0 when not bought).

### GetSkillEffect

```lua
local bonus = exports['koja-fishing']:GetSkillEffect(source, 'handlarz_sell_bonus')
```

Takes a player id or identifier. Returns `level × effect_per_level`.

## Licence

### HasFishingLicense

```lua
local hasLicense = exports['koja-fishing']:HasFishingLicense(source)
```

Returns `true` when the player passed the licence exam.

### SetFishingLicense

```lua
exports['koja-fishing']:SetFishingLicense(source, true)          -- grant
exports['koja-fishing']:SetFishingLicense('char1:abc', false)    -- revoke (offline is fine)
```

Returns `true` on success. An online player gets a notification, and `koja-fishing:server:licenseChanged` fires.

### ResetLicenseTest

```lua
exports['koja-fishing']:ResetLicenseTest(source)
```

Clears exam attempts and cooldown, so the player can take the exam again.

## Unlocks

Unlocks are per-character flags saved in the database.
Spots with `unlockFlag` need one to be fished, and hermit quests use them to remember that they are done.

### HasUnlock / GrantUnlock

```lua
local unlocked = exports['koja-fishing']:HasUnlock(source, 'hermit_cave')
exports['koja-fishing']:GrantUnlock('char1:abc', 'hermit_cave')
```

Both accept a player id or an identifier.
`GrantUnlock` returns `true` when the flag was saved.
The default flags are `hermit_cave` (Tom's reward, opens Hermit's Cave) and `quest:<questId>` for a finished hermit quest, for example `quest:tom_cave`.

## Quests

### TriggerQuestEvent

Counts an action towards every active quest whose objective matches.

```lua
exports['koja-fishing']:TriggerQuestEvent(source, 'catch', { item = 'pike', rarity = 'common', amount = 1 })
```

The second argument is the objective `type` (`catch`, `sell`, `dig`, `dive`, or a custom type you use in `config.quests.lua`). The table may contain `item`, `rarity`, `spot` and `amount`.

### AddQuestProgress

Adds progress to one specific quest, if it is the active quest of an unlocked board.

```lua
local ok, err = exports['koja-fishing']:AddQuestProgress(source, 1, 2, 1)
```

Arguments: player id, board id, quest id, amount.

## Content

### GetFishList / GetItemList / GetSpots

```lua
local fish = exports['koja-fishing']:GetFishList()   -- copy of Config.Fish
local items = exports['koja-fishing']:GetItemList()  -- copy of Config.Items
local spots = exports['koja-fishing']:GetSpots()     -- copy of Config.Spots
```

Read-only copies with names already translated to the server language and admin-panel changes applied.

## Diving and digging

### GetDivingGear / GetDivingStats

```lua
local gear = exports['koja-fishing']:GetDivingGear(source)
local stats = exports['koja-fishing']:GetDivingStats('char1:abc')
```

Both accept a player id or an identifier.

| `GetDivingGear` field | Value |
|---|---|
| `tank`, `fins`, `light`, `scanner` | Gear levels |
| `air`, `capacity` | Air left and tank capacity, in seconds |
| `ownsGear` | `true` when the player has scuba gear |

`GetDivingStats` returns `dives`, `timeUnderwater` (seconds), `deepest` (metres), `crates`, `contracts` and `earned`.

### GetDiggingGear

```lua
local gear = exports['koja-fishing']:GetDiggingGear(source) -- { shovel = 1, detector = 1, bag = 1 }
```

Takes an online player id. Returns the shovel, detector and bag levels, or `nil`.

## Poaching

### GetPoachingEvidence / ClearPoachingEvidence

```lua
local evidence = exports['koja-fishing']:GetPoachingEvidence(source)
exports['koja-fishing']:ClearPoachingEvidence(source)
```

Both accept a player id or an identifier.
`GetPoachingEvidence` returns the evidence from the last `Config.Poaching.EvidenceMinutes`, oldest first:

| Field | Value |
|---|---|
| `kind` | `protected_zone`, `protected_species` or `electric_fishing` |
| `fish` | Fish item name |
| `weight` | Weight in kg (not for electric fishing) |
| `spot` | Spot id (not for electric fishing) |
| `time` | Unix time of the catch |

Evidence is kept in memory and is cleared by a fine, by `ClearPoachingEvidence` and by a server restart.

## Items

### useItem

Used by ox_inventory for the `battery`, `shovel` and `fishpedia` items (`server = { export = 'koja-fishing.useItem' }`).
You do not call it yourself.

## Server events

Listen with `AddEventHandler` in another server resource.
They are local server events — clients cannot trigger them.

### `koja-fishing:server:fishCaught`

Fires when a player lands a fish, before they choose keep or release.

```lua
AddEventHandler('koja-fishing:server:fishCaught', function(source, data)
    print(source, data.fish, data.weight, data.rarity, data.spot)
end)
```

| Field | Value |
|---|---|
| `fish` | Fish item name |
| `weight` | Weight in kg |
| `rarity` | `common`, `uncommon`, `rare`, `legendary` |
| `spot` | Spot id from `config.spots.lua` |

### `koja-fishing:server:electricFishCaught`

Fires when a player grabs a fish during electric fishing.

```lua
AddEventHandler('koja-fishing:server:electricFishCaught', function(source, fishName)
end)
```

### `koja-fishing:server:levelUp`

Fires when an online player gains one or more levels — from XP or from `SetLevel`.

```lua
AddEventHandler('koja-fishing:server:levelUp', function(source, level, previousLevel)
end)
```

### `koja-fishing:server:licenseChanged`

Fires when a licence is granted or revoked through `SetFishingLicense` or the admin panel.

```lua
AddEventHandler('koja-fishing:server:licenseChanged', function(identifier, hasLicense, source, changedBy)
    -- source is nil when the player is offline; changedBy is the admin's player id (or nil from another resource)
end)
```

### `koja-fishing:server:fineIssued`

Fires after a police officer fines an angler, whatever `Config.Poaching.Billing` is.
With `okokBilling` or `custom` billing nothing is charged — create the invoice here.

```lua
AddEventHandler('koja-fishing:server:fineIssued', function(officer, target, amount, offenceIds, note)
    -- offenceIds: e.g. { 'no_license', 'protected_zone' }; note: officer's note (max 200 characters) or nil
end)
```

### `koja-fishing:server:poachingAlert`

Fires when a catch in a protected spot triggers a police alert.

```lua
AddEventHandler('koja-fishing:server:poachingAlert', function(source, alert)
    -- alert.coords = { x, y, z }, alert.spot = spot name, alert.duration = blip seconds
end)
```

Use it to forward the alert to your dispatch script.

### `koja-fishing:server:hermitQuestDone`

Fires when a player completes a hermit's quest.

```lua
AddEventHandler('koja-fishing:server:hermitQuestDone', function(source, pedKey, questId)
    -- pedKey = 'tom', questId = 'tom_cave'
end)
```

### `koja-fishing:server:tournamentFinished`

Fires once per tournament, when it is settled after its end time.

```lua
AddEventHandler('koja-fishing:server:tournamentFinished', function(tournament, winners)
    for _, w in ipairs(winners) do
        print(tournament.name, w.rank, w.name, w.points)
    end
end)
```

`winners` lists ranked participants up to the last rewarded place, each with `rank`, `identifier`, `name` and `points`.

### `koja-fishing:server:playerLoaded`

Fires after the script has loaded a player's fishing data, with the player id.

### `koja-fishing:server:configChanged`

Fires when a value is saved, reset, added or deleted in the admin panel, with the config path (for example `Minigame.HookWindowMs` or `Items.pearl`).

```lua
AddEventHandler('koja-fishing:server:configChanged', function(path)
end)
```

## Client exports

```lua
local fishing = exports['koja-fishing']:IsFishing()         -- true while a fishing session runs
local gear = exports['koja-fishing']:GetEquippedGear()      -- { rod, reel, line, bait } or nil
local done = exports['koja-fishing']:IsTutorialDone()       -- true after the guide
exports['koja-fishing']:StopFishing()                       -- stops the current session, returns true if one was running
```

## Client events

Local client events — listen with `AddEventHandler` in another client script.

| Event | Arguments | Fires when |
|---|---|---|
| `koja-fishing:client:fishingStarted` | — | A cast starts. |
| `koja-fishing:client:fishingStopped` | — | The fishing session ends, for any reason. |
| `koja-fishing:client:fishCaught` | `fish` | A fish is landed, with the data shown on the catch screen (`respname`, `name`, `statistics.weight`, `statistics.rarity`, ...). |
| `koja-fishing:client:promptEligible` | `eligible`, `spotId` | The player becomes able, or no longer able, to cast on a spot. |
| `koja-fishing:client:shopOpened` | — | The shop window opens. |
| `koja-fishing:client:skillUnlocked` | `skillId` | The player buys a skill. |
| `koja-fishing:client:digFinished` | `result`, `cacheRef` | A dig finishes successfully. `cacheRef` is set when it was a contract cache. |
| `koja-fishing:client:configChanged` | `path` | An admin-panel value is applied on this client — live changes, and saved values when the player joins. |
