> For the complete documentation index, see [llms.txt](https://lscripts.gitbook.io/lscripts/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lscripts.gitbook.io/lscripts/scripts/l-multijob/installation.md).

# Installation

Multiple jobs per character for ESX Legacy, with an F4 menu to switch the active job.

### Dependencies

* es\_extended (ESX Legacy)
* ox\_lib
* oxmysql

{% hint style="warning" %}
Keep the folder named `l-multijob`. The script checks its own name on start and refuses to run when renamed, because exports, events and the update check depend on the name.
{% endhint %}

### Steps

1. Drop the `l-multijob` folder into your `resources` directory.
2. Run the contents of `sql/l_multijob.sql` in your database tool. It creates `l_multijob_jobs` for the job lists and `l_multijob_locks` for the faction locks. The resource never creates or alters tables on its own, it only checks on start and prints a hint when a table is missing. Skipping only the locks table leaves everything except the faction lock working.
3. Add it to your `server.cfg`, after es\_extended, ox\_lib and oxmysql:

   ```
   ensure l-multijob
   ```

   If the folder sits inside a bracket folder that is already ensured (for example `[lscripts]`), no extra line is needed.
4. For the admin commands, either give your admins one of the ESX groups listed in `server_config.lua`, or grant the ACE permission in your `server.cfg`:

   ```
   add_ace group.admin l-multijob.admin allow
   ```
5. Restart the server.

### Using it in game

Press **F4** (default) or type `/multijob`. The same key and ESC close the menu again. Clicking a row switches to that job, the trash icon removes it.

Players who already had a job before you installed the resource are picked up automatically. On every resource start their current job is written into their job list.

### Commands

| Command                                       | Description                                                                                |
| --------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `/addmultijob [id\|identifier] [job] [grade]` | Add a job to a player. Works with a server id (online) or an ESX identifier (also offline) |
| `/removemultijob [id\|identifier] [job]`      | Remove a job from a player, online or offline by identifier                                |

Both are gated by `ServerConfig.Admin` in `server_config.lua`.

### Works with /setjob and other job scripts

You do not have to change your admin workflow or your other scripts. l-multijob listens to the ESX `esx:setJob` event, so every job that is set through ESX itself lands in the player's job list:

* `/setjob [id] police 3` sets the active job as usual and stores police grade 3 in the player's F4 list, so it is kept when they switch to another job.
* Any script that calls `xPlayer.setJob(...)`, like boss menus, job centers or whitelist scripts, is picked up the same way.
* Promotions sync too, setting a job the player already holds at a new grade updates the grade in their list.
* Setting the standard job (default `unemployed`) is never stored, it just clocks the player out.

Two things to be aware of:

* Jobs added this way bypass `Config.MaxJobs`, because ESX applies the job before l-multijob hears about it. See the patch below if you want the limit enforced.
* `/setjob` never removes anything. Giving a player a different job keeps the old one in their list, that is the point of multijob. To take a job away use `/removemultijob` or the trash button in the F4 menu.

#### Making /setjob respect the job limit

The `esx:setJob` event fires after ESX has already applied the job, so the limit and the faction rules cannot be enforced from the outside. The clean way is a small patch in es\_extended so the command checks both before it sets the job.

Open `es_extended/server/modules/commands.lua`, find the `setjob` command and add this block between the job validation and the `setJob` call:

```lua
        if not ESX.DoesJobExist(args.job, args.grade) then
            return showError(TranslateCap("command_setjob_invalid"))
        end

        if GetResourceState("l-multijob") == "started" then
            local allowed, info = exports["l-multijob"]:CanAddMultijob(args.playerId.source, args.job)
            if not allowed then
                return showError(info and info.message or ("%s cannot take this job right now"):format(args.playerId.name))
            end
        end

        args.playerId.setJob(args.job, args.grade)
```

`CanAddMultijob` is the single check for all of it: the job limit, a running faction lock and conflicting categories. It hands back a finished message, so the command prints the same reason the player would see.

The `GetResourceState` guard keeps the command working if l-multijob is stopped or removed. Re-apply the patch after an es\_extended update.

This covers the `/setjob` command. Other scripts that call `xPlayer.setJob(...)` directly still bypass the checks, add the same block at those call sites if you need it there. Do not patch `xPlayer.setJob` itself, l-multijob and other resources rely on it.

### Which files you can edit

`config.lua`, `server_config.lua`, `locales/*.lua`, `shared/functions.lua` (notification wiring), `client/framework.lua` and `server/framework.lua` (framework adapters), `sql/*.sql` and the complete `html/` folder. The accent colour of the menu lives in `html/style.css`.
