├── LICENSE
├── README.md
├── client.lua
├── colors.json
├── config.lua
├── credits.txt
├── fxmanifest.lua
├── html
├── app.js
├── index.html
└── style.css
├── server.lua
├── version
└── version.lua
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 finalLy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # fl-dispatch
2 |
3 | An Event-Based Advanced FiveM QBCore Job Dispatch / 10-System Script,
4 |
5 | Originally Made by NevoG,
6 | Link to old script [here](https://forum.cfx.re/t/release-fivem-advanced-active-officers/1798459).
7 |
8 | ## Support
9 |
10 |
11 |
12 |
Personal Account
13 |
14 |
15 |
Discord Server
16 |
17 |
18 |
19 |
20 | ## Requirements
21 |
22 | - qb-core
23 | - qb-policejob
24 | - pma-voice
25 |
26 | ## Code Changes Requirements
27 |
28 | Add this line at `qb-core/server/player.lua` line 272:
29 |
30 | ```lua
31 | TriggerEvent('QBCore:Server:OnMetaDataUpdate', self.PlayerData.source, meta, val)
32 | ```
33 |
34 | Your method should look like this:
35 |
36 | ```lua
37 | function self.Functions.SetMetaData(meta, val)
38 | if not meta or type(meta) ~= 'string' then return end
39 | if meta == 'hunger' or meta == 'thirst' then
40 | val = val > 100 and 100 or val
41 | end
42 | self.PlayerData.metadata[meta] = val
43 | self.Functions.UpdatePlayerData()
44 |
45 | -- Triggering our event:
46 | TriggerEvent('QBCore:Server:OnMetaDataUpdate', self.PlayerData.source, meta, val)
47 | end
48 | ```
49 |
50 | ## Keybinds
51 |
52 | - F3 - Open Active Officers List
53 |
54 | ## Commands
55 |
56 | - /dispatch 0 - Drag Menu
57 | - /dispatch - Toggle Menu
58 | - /callsign `[callsign]` - To set your callsign (QBCore Command)
59 |
60 | ## FAQ
61 |
62 | ### 1. How can I change the default keybinds?
63 | Head to **`config.lua`** and under `Config.ToggleKey` change it to the key you desire.
64 |
65 | Also, you can change it on the client-side (only for you) in the Game Settings.
66 |
67 | ### 2. How can I add another job?
68 | This is a frequently asked question, and the answer is simple. For this example, we'll add job support for `beanmachine`.
69 |
70 | ### Job Config Editor
71 |
72 | Head to our FL-Dispatch Config Editor Repository right [here](https://github.com/finalLy134/fl-dispatch-editor.github.io).
73 |
74 | ### Manually
75 |
76 | #### A. Head to `config.lua`
77 | Add your new job to the `Config.Jobs` table. It should look like this:
78 | ```lua
79 | Config.Jobs = { "police", "ambulance", "taxi", "beanmachine" }
80 | ```
81 |
82 | #### B. Head to `colors.json`
83 | Add the colors for your `beanmachine` job. Copy the format of the `ambulance` job or any other job and modify as needed.
84 | We would want to modify the job's **name**, **label** and **different colors**:
85 | ```lua
86 | "beanmachine": {
87 | "label": "Active Beanmachine Workers",
88 | "colors": {
89 | "backgroundColor": "#682900",
90 | "foregroundColor": "white"
91 | },
92 | "ranges": [
93 | {
94 | "start": 1,
95 | "end": 19,
96 | "colors": {
97 | "backgroundColor": "#003F68",
98 | "foregroundColor": "white"
99 | }
100 | },
101 | {
102 | "start": 31,
103 | "end": 39,
104 | "colors": {
105 | "backgroundColor": "#6F5900",
106 | "foregroundColor": "white"
107 | }
108 | }
109 | ],
110 | "special": [
111 | {
112 | "prefix": "W",
113 | "colors": {
114 | "backgroundColor": "#50008F",
115 | "foregroundColor": "white"
116 | }
117 | }
118 | ]
119 | }
120 | ```
121 | Feel free to edit until you get the desired result. If you encounter any issues, you can copy the original content from the source code or contact me for assistance.
122 |
123 | ### 3. It shows I have an update, how can I update the script without losing my config/colors modifications?
124 | Simply save your current `colors.json` and `config.lua` somewhere in your computer.
125 |
126 | Install the newer version of fl-dispatch, upload it to your server resources folder and drag the older config files into the newer version folder.
127 | But of course, check if there was an update to the config files and if so include the newer text content into your older version ones.
128 | And you're done, you have successfully updated fl-dispatch.
129 |
130 | ## Preview
131 |
132 | 
133 |
134 | ## License
135 |
136 | This project is under MIT license. See the file [LICENSE](LICENSE) for more details.
137 |
--------------------------------------------------------------------------------
/client.lua:
--------------------------------------------------------------------------------
1 | QBCore = exports[Config.Core]:GetCoreObject()
2 |
3 | local PlayerJob = {}
4 | local Enabled = false
5 |
6 | CreateThread(function()
7 | Wait(10)
8 | while not QBCore.Functions.GetPlayerData().job do
9 | Wait(10)
10 | end
11 |
12 | PlayerJob = QBCore.Functions.GetPlayerData().job
13 | TriggerServerEvent("fl-dispatch:server:refresh")
14 | end)
15 |
16 | RegisterNetEvent('QBCore:Client:OnPlayerLoaded')
17 | AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
18 | PlayerJob = QBCore.Functions.GetPlayerData().job
19 | TriggerServerEvent("fl-dispatch:server:refresh")
20 | end)
21 |
22 | RegisterNetEvent('QBCore:Client:OnJobUpdate')
23 | AddEventHandler('QBCore:Client:OnJobUpdate', function(jobInfo)
24 | PlayerJob = jobInfo
25 | if Enabled and not IsJobAllowed(PlayerJob.name) then
26 | SendNUIMessage({ action = "close" })
27 | end
28 |
29 | TriggerServerEvent("fl-dispatch:server:refresh")
30 | end)
31 |
32 | RegisterNetEvent("fl-dispatch:client:open")
33 | AddEventHandler("fl-dispatch:client:open", function(type)
34 | if type == 'toggle' then
35 | if Enabled then
36 | Enabled = false
37 | SendNUIMessage({ action = 'close' })
38 | else
39 | Enabled = true
40 | SendNUIMessage({ action = 'open' })
41 | end
42 | elseif type == 'drag' then
43 | SetNuiFocus(true, true)
44 | SendNUIMessage({ action = 'drag' })
45 | elseif type == 'force_exit' then
46 | Enabled = false
47 | SendNUIMessage({ action = 'close' })
48 | end
49 | end)
50 |
51 | RegisterNUICallback("Close", function()
52 | SetNuiFocus(false, false)
53 | end)
54 |
55 | RegisterNetEvent("fl-dispatch:client:refresh")
56 | AddEventHandler("fl-dispatch:client:refresh", function(data)
57 | local id = GetPlayerServerId(PlayerId())
58 | local filteredData = {}
59 |
60 | for _, jobData in pairs(data) do
61 | if jobData.job == PlayerJob.name then
62 | for i, v in ipairs(jobData.players) do
63 | if v.src == id then
64 | jobData.players[i].me = true
65 | end
66 | end
67 | table.insert(filteredData, jobData)
68 | end
69 | end
70 |
71 | SendNUIMessage({
72 | action = 'refresh',
73 | data = filteredData
74 | })
75 | end)
76 |
77 | RegisterNetEvent('fl-dispatch:client:removePlayer')
78 | AddEventHandler('fl-dispatch:client:removePlayer', function(src)
79 | SendNUIMessage({
80 | action = "removePlayer",
81 | data = {
82 | src = src,
83 | },
84 | })
85 | TriggerServerEvent("fl-dispatch:server:refresh")
86 | end)
87 |
88 | RegisterNetEvent("fl-dispatch:client:setTalkingOnRadio")
89 | AddEventHandler("fl-dispatch:client:setTalkingOnRadio", function(src, talking)
90 | SendNUIMessage({
91 | action = "setTalkingOnRadio",
92 | data = {
93 | src = src,
94 | talking = talking
95 | },
96 | })
97 | end)
98 |
99 | RegisterNetEvent("fl-dispatch:client:setPlayerRadio")
100 | AddEventHandler("fl-dispatch:client:setPlayerRadio", function(src, channel)
101 | SendNUIMessage({
102 | action = "setPlayerRadio",
103 | data = {
104 | src = src,
105 | channel = channel
106 | },
107 | })
108 | end)
109 |
110 | RegisterCommand("+dispatch", function()
111 | TriggerServerEvent('fl-dispatch:server:open', GetPlayerServerId(PlayerId()), {})
112 | end, false)
113 |
114 | RegisterKeyMapping('+dispatch', 'Opens Job Dispatch List', Config.ToggleKey['group'], Config.ToggleKey['key'])
115 |
116 | function IsJobAllowed(job)
117 | for _, allowedJob in ipairs(Config.Jobs) do
118 | if allowedJob == job then
119 | return true
120 | end
121 | end
122 | return false
123 | end
124 |
--------------------------------------------------------------------------------
/colors.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultColors": {
3 | "backgroundColor": "rgb(47, 69, 86)",
4 | "foregroundColor": "white"
5 | },
6 | "jobs": {
7 | "police": {
8 | "label": "Active Officers",
9 | "colors": {
10 | "backgroundColor": "rgb(47, 69, 86)",
11 | "foregroundColor": "white"
12 | },
13 | "special": [
14 | {
15 | "prefix": "S",
16 | "colors": {
17 | "backgroundColor": "red",
18 | "foregroundColor": "white"
19 | }
20 | },
21 | {
22 | "prefix": "F",
23 | "colors": {
24 | "backgroundColor": "green",
25 | "foregroundColor": "white"
26 | }
27 | },
28 | {
29 | "prefix": "V",
30 | "colors": {
31 | "backgroundColor": "#BA7D45",
32 | "foregroundColor": "white"
33 | }
34 | },
35 | {
36 | "prefix": "H",
37 | "colors": {
38 | "backgroundColor": "#636364",
39 | "foregroundColor": "white"
40 | }
41 | }
42 | ],
43 | "ranges": [
44 | {
45 | "start": 200,
46 | "end": 210,
47 | "colors": {
48 | "backgroundColor": "rgb(235, 1, 2)",
49 | "foregroundColor": "white"
50 | }
51 | },
52 | {
53 | "start": 300,
54 | "end": 330,
55 | "colors": {
56 | "backgroundColor": "rgb(20, 10, 11)",
57 | "foregroundColor": "white"
58 | }
59 | },
60 | {
61 | "start": 330,
62 | "end": 390,
63 | "colors": {
64 | "backgroundColor": "rgb(125, 121, 119)",
65 | "foregroundColor": "white"
66 | }
67 | },
68 | {
69 | "start": 400,
70 | "end": 440,
71 | "colors": {
72 | "backgroundColor": "rgb(210, 76, 3)",
73 | "foregroundColor": "white"
74 | }
75 | },
76 | {
77 | "start": 450,
78 | "end": 490,
79 | "colors": {
80 | "backgroundColor": "rgb(3, 61, 215)",
81 | "foregroundColor": "white"
82 | }
83 | },
84 | {
85 | "start": 500,
86 | "end": 550,
87 | "colors": {
88 | "backgroundColor": "rgb(3, 97, 0)",
89 | "foregroundColor": "white"
90 | }
91 | }
92 | ]
93 | },
94 | "ambulance": {
95 | "label": "Active EMS",
96 | "colors": {
97 | "backgroundColor": "red",
98 | "foregroundColor": "white"
99 | },
100 | "ranges": [
101 | {
102 | "start": 1,
103 | "end": 19,
104 | "colors": {
105 | "backgroundColor": "#2e54d1",
106 | "foregroundColor": "white"
107 | }
108 | },
109 | {
110 | "start": 31,
111 | "end": 39,
112 | "colors": {
113 | "backgroundColor": "#2e54d1",
114 | "foregroundColor": "white"
115 | }
116 | },
117 | {
118 | "start": 21,
119 | "end": 29,
120 | "colors": {
121 | "backgroundColor": "#10a9ef",
122 | "foregroundColor": "white"
123 | }
124 | },
125 | {
126 | "start": 91,
127 | "end": 99,
128 | "colors": {
129 | "backgroundColor": "#AB1150",
130 | "foregroundColor": "white"
131 | }
132 | }
133 | ],
134 | "special": [
135 | {
136 | "prefix": "A",
137 | "colors": {
138 | "backgroundColor": "#11ab6c",
139 | "foregroundColor": "white"
140 | }
141 | },
142 | {
143 | "prefix": "DELTA",
144 | "colors": {
145 | "backgroundColor": "#FF9700",
146 | "foregroundColor": "white"
147 | }
148 | },
149 | {
150 | "prefix": "OMEGA",
151 | "colors": {
152 | "backgroundColor": "#EF5610",
153 | "foregroundColor": "white"
154 | }
155 | },
156 | {
157 | "prefix": "PD",
158 | "colors": {
159 | "backgroundColor": "#DE212A",
160 | "foregroundColor": "white"
161 | }
162 | }
163 | ]
164 | },
165 | "taxi": {
166 | "label": "Active Drivers",
167 | "colors": {
168 | "backgroundColor": "yellow",
169 | "foregroundColor": "black"
170 | },
171 | "special": [
172 | {
173 | "prefix": "ALPHA",
174 | "colors": {
175 | "backgroundColor": "#FF6F00",
176 | "foregroundColor": "white"
177 | }
178 | },
179 | {
180 | "prefix": "METRO",
181 | "colors": {
182 | "backgroundColor": "#0090FF",
183 | "foregroundColor": "white"
184 | }
185 | }
186 | ]
187 | }
188 | }
189 | }
--------------------------------------------------------------------------------
/config.lua:
--------------------------------------------------------------------------------
1 | Config = {}
2 |
3 | Config.Core = 'qb-core'
4 |
5 | Config.ToggleKey = {
6 | ['group'] = 'keyboard',
7 | ['key'] = 'F3'
8 | }
9 |
10 | Config.Jobs = { "police", "ambulance", "taxi" }
11 |
--------------------------------------------------------------------------------
/credits.txt:
--------------------------------------------------------------------------------
1 | The original creator of the base of this script is NevoG,
2 | This script was converted from ESX to QBCore in 2022 by finalLy (me) and I continued to program it after that.
3 | I kept adding new features to the script and I just really loved the concept.
4 | This script is purely for the community and I have never done anything profitable with it.
--------------------------------------------------------------------------------
/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | fx_version 'bodacious'
2 | game "gta5"
3 | lua54 'yes'
4 |
5 | author "finalLy#1138"
6 | description "QB Advanced Dispatch System"
7 | repository "https://github.com/finalLy134/fl-dispatch"
8 |
9 | version "2.1.0"
10 |
11 | ui_page "html/index.html"
12 |
13 | client_script "client.lua"
14 | server_script { "version.lua", "server.lua" }
15 | shared_script "config.lua"
16 |
17 | files {
18 | "html/*.html",
19 | "html/*.css",
20 | "html/*.js",
21 | "colors.json"
22 | }
23 |
--------------------------------------------------------------------------------
/html/app.js:
--------------------------------------------------------------------------------
1 | let configData = null;
2 |
3 | $(document).ready(function () {
4 | fetch("../colors.json")
5 | .then((response) => {
6 | if (!response.ok) {
7 | throw new Error(`HTTP error! Status: ${response.status}`);
8 | }
9 | return response.json();
10 | })
11 | .then((data) => (configData = data))
12 | .catch((error) => {
13 | console.error("Error fetching colors.json:", error);
14 | });
15 |
16 | $("body").on("keyup", function (key) {
17 | if (key.which === 113 || key.which == 27 || key.which == 90) {
18 | $.post(`http://${GetParentResourceName()}/Close`);
19 | }
20 | });
21 | });
22 |
23 | window.addEventListener("message", function (event) {
24 | let action = event.data.action;
25 | let data = event.data.data;
26 |
27 | if (action == "open") {
28 | $(".app").fadeIn(500);
29 | } else if (action == "drag") {
30 | $(".app").fadeIn(500);
31 | $(".app").draggable({
32 | handle: ".title",
33 | containment: "window",
34 | });
35 | } else if (action == "close") {
36 | $(".app").fadeOut(500);
37 | } else if (action == "refresh") {
38 | $(".players").html("");
39 |
40 | for (var jobData of data) {
41 | $(".title").text(
42 | jobData.players.length + " " + configData.jobs[jobData.job].label
43 | );
44 |
45 | var sortedPlayers = jobData.players.sort((a, b) => {
46 | return a.callsign.localeCompare(b.callsign);
47 | });
48 |
49 | for (var player of sortedPlayers) {
50 | html = `
51 |