# Boat Licenses — documentation

> A boating school with a theory test, a jet ski exam and a boat exam that ends with a scored docking.

- URL: https://xexstudio.com/docs/boat-licenses
- Version: 1.0.0
- Product page: https://xexstudio.com/scripts/boat-licenses
Boat Licenses: a boating school (Marine Academy in game) for ESX, QBCore and Qbox. Players take a theory test, then a watercraft exam and a
boat exam. The exams run through slow zones and a buoy course, and the boat exam ends by docking in a berth. The
docking gets a score and a grade, shown in a docking report.

The server checks everything. It spawns the boat and checks every buoy against the boat's real position. Once per
second it measures speed and damage, and it scores the docking from the boat's position, heading and health. No
license is granted without passing those checks.

## Features

- **Theory test:** random questions from a bank in English and Spanish, corrected on the server (the answers never
  reach the client). Fee charged on start, optional wait before retrying after a fail.
- **Watercraft exam:** jet ski through the harbor slow zone and the buoy course, then stop back at the start.
- **Boat exam:** same course in a boat, then dock in the berth. Docking score of 0–100 and a grade from S to F, from
  how centered the boat is, how well it lines up with the berth (bow or stern in) and any impact while docking.
- **Faults measured on the server:** speeding in a slow zone (one fault per breach, with a tolerance) and collisions.
  One fault over the limit ends the exam.
- **Exam HUD:** current step, distance, time left, speed in knots, the zone's speed limit and the fault count.
  Slow zones are drawn on the map during the exam.
- **Academy tablet:** exams with their fee and status, licenses, best docking score and exam history. Opens anywhere
  with `/marinepad`. Brand name and accent color configurable.
- **Instructor at the desk:** an NPC with an ox_target / qb-target option. Without a target resource (or without
  the NPC) the desk is a marker and an `[E]` prompt.
- **Admin tool:** `/marinetool desk | buoy | zone | berth | spawn` prints a line ready to paste into `config.lua`
  or `data/routes.lua`.
- **API:** exports to check, grant and revoke licenses, and events for logs, battle passes or anticheats.
- **Optimized:** nothing runs while nobody is taking an exam. During an exam the client checks 10 times per second
  and draws markers every frame only near the current buoy. The server checks once per second, and only while an
  exam is running.

## Requirements

- [ox_lib](https://github.com/overextended/ox_lib) and [oxmysql](https://github.com/overextended/oxmysql)
- One framework: **ESX** (with `esx_license`), **QBCore** or **Qbox**
- OneSync (on by default on current servers)
- Optional: **ox_target** or **qb-target** (without one, the desk uses a marker and an `[E]` prompt)

## Install

1. Drop `xex_marine` into your resources folder.
2. **ESX only:** run `install/esx.sql` (adds the three license types). The exam history table is created automatically.
3. Add to `server.cfg` after your framework, ox_lib and oxmysql:
   ```cfg
   ensure xex_marine
   ```
4. Set the language with `setr ox:locale en` (or `es`).

## How it works

1. **Marine Academy** (blip at Paleto Cove): talk to the instructor to open the tablet. The theory test comes first.
2. **Practical exams:** each exam spawns its own boat at the dock and seats the player. The HUD shows the next buoy
   and the speed limit when inside a slow zone.
3. **Faults:** going over the limit in a slow zone for more than a moment, or hitting something, adds a fault.
   Too many faults, too much damage or running out of time fails the exam.
4. **Docking (boat exam):** stop inside the berth and hold still for two seconds. The server scores the docking and
   the report shows the grade.
5. **License:** granted on the spot when the exam is passed. Every attempt goes to the exam history.
6. **Back to the desk:** when the exam ends (passed, failed or cancelled) the player is taken back to where they
   started it, so nobody is left in the water.

The exam fee is not refunded when an exam is failed or cancelled. It is refunded if the boat cannot be spawned.

## Configure

| File | What it contains |
| --- | --- |
| `config.lua` | All settings (table below) |
| `data/routes.lua` | Spawn point, slow zones, buoys and finish (stop or berth) of each exam |
| `questions/<locale>.lua` | Theory questions (server only) |
| `locales/<locale>.json` | Every text: notifications, HUD and UI |
| `bridge/server.lua` | Framework, money, licenses, vehicle keys, fuel |
| `bridge/client.lua` | Client-side fuel resources, notifications |

| Setting | What it does |
| --- | --- |
| `Config.Framework` | `auto`, or force `esx` / `qb` / `qbox` |
| `Config.Target` | `auto` (ox_target, then qb-target), force one, or `none` for the marker and `[E]` prompt |
| `Config.Accounts.school` | Account the exam fees are taken from |
| `Config.Licenses` | License names. Change them to match your existing license system |
| `Config.UI` | Brand name, accent color, tablet command and optional key |
| `Config.School` | Location and heading, instructor NPC (model, animation, spawn and prompt distance), blip, theory test (price, questions, correct answers needed, retry wait), and for each exam: on/off, price, boat model, route, faults allowed, damage allowed, time limit and (boat) minimum docking score |
| `Config.Faults` | Speed tolerance in knots, seconds over the limit before it counts, damage that counts as a collision |
| `Config.Docking` | Offset and angle that still score full marks, angle that scores 0, weight of the alignment, bow or stern in, impact penalty and crash threshold, seconds to hold still |
| `Config.Grades` | Score needed for each grade |
| `Config.Session` | Time allowed outside the boat, maximum plausible speed, plate prefix, history size |

### Existing licenses

If your server already has boat licenses, put their names in `Config.Licenses` and players keep them:

```lua
Config.Licenses = { theory = 'theoretical_boat', watercraft = 'jetski', boat = 'practical_boat' }
```

Only want one practical exam? Set `enabled = false` on the other in `Config.School.practical`.

### Moving the school or adding routes

Admins can run `/marinetool` in game, on the water or sitting in a boat:

| Command | Prints |
| --- | --- |
| `/marinetool desk` | Where the instructor stands and faces (`Config.School.coords`), from your own position |
| `/marinetool spawn` | Where the exam boat appears (position and heading) |
| `/marinetool buoy` | One buoy of the course |
| `/marinetool zone` | A slow zone centered here |
| `/marinetool berth` | The berth for the docking, with the heading the boat should face |

The line is printed in F8 and copied to the clipboard, ready to paste into `config.lua` or `data/routes.lua`.

### Keys and fuel

`bridge/server.lua` gives keys with `qbx_vehiclekeys` or `qb-vehiclekeys` and sets fuel for `ox_fuel`.
`bridge/client.lua` covers LegacyFuel, cdn-fuel and ps-fuel. Add your own resource there if it isn't listed.
Notifications go through `Bridge.Notify` in `bridge/client.lua`: swap `lib.notify` there for your own resource.

## For developers

```lua
-- key = 'theory' | 'watercraft' | 'boat'
exports.xex_marine:HasLicense(source, key)
exports.xex_marine:GrantLicense(source, key)   -- e.g. from a staff command
exports.xex_marine:RevokeLicense(source, key)  -- e.g. after a court ruling or too many fines
exports.xex_marine:IsInExam(source)
```

| Event (server, local) | Arguments |
| --- | --- |
| `xex_marine:examStarted` | `source, exam` |
| `xex_marine:examFinished` | `source, { exam, passed, reason, score, grade, faults, duration }` |
| `xex_marine:licenseGranted` | `source, licenseKey, licenseName` |
| `xex_marine:suspicious` | `source, reason, details` (e.g. impossible speed between two checks) |

Use them for battle passes, logs or your anticheat without editing the resource.

## Security

- The server spawns the exam boat, keeps the route and accepts each buoy only if the boat is really there and the
  player is driving it.
- Speed in slow zones, collisions, the time limit and the docking are measured on the server, not reported by the client.
- A boat that moves faster than `Config.Session.maxSpeed` between two checks ends the exam and fires `xex_marine:suspicious`.
- The theory answers stay on the server, and each test can be submitted once. Closing the test halfway uses up the attempt.
- Fees are charged on the server before the exam starts.

## FAQ

**Can players use their own boat?**
No. Exams use a boat spawned by the server, so the route, the speed and the damage can be checked.

**Why does an exam say the spot is taken?**
One exam boat fits at the spawn point at a time. The next player can start as soon as the boat leaves.

**Does it work with my license item or ID card?**
Licenses are stored the framework's way (`esx_license` or player metadata). ID card and vehicle shop resources that
read those licenses see them. Use the license names they expect in `Config.Licenses`.

**Can I translate it?**
Copy `locales/en.json` and `questions/en.lua` to your language and set `setr ox:locale <code>`.
