├── ps-mdt ├── ui │ └── img │ │ ├── court.png │ │ ├── male.png │ │ ├── val1.webp │ │ ├── val2.webp │ │ ├── val3.webp │ │ ├── val4.webp │ │ ├── 247Back.webp │ │ ├── female.png │ │ ├── fleeca.webp │ │ ├── liquor.webp │ │ ├── paleto.webp │ │ ├── ems_badge.png │ │ ├── not-found.webp │ │ ├── pacific1.webp │ │ ├── pacific2.webp │ │ ├── pacific3.webp │ │ ├── sasp_badge.png │ │ ├── 247SmallBack.webp │ │ ├── profile_pic.png │ │ ├── warrant_pfp.png │ │ ├── 247SmallFront.webp │ │ ├── ems_badge_zonah.png │ │ └── Screenshot 2022-07-16 093012.png ├── fxmanifest.lua ├── server │ ├── utils.lua │ ├── dbm.lua │ └── main.lua ├── client │ ├── cl_impound.lua │ └── main.lua ├── mdt.sql └── shared │ └── config.lua ├── README.md └── LICENSE /ps-mdt/ui/img/court.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/court.png -------------------------------------------------------------------------------- /ps-mdt/ui/img/male.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/male.png -------------------------------------------------------------------------------- /ps-mdt/ui/img/val1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/val1.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/val2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/val2.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/val3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/val3.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/val4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/val4.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/247Back.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/247Back.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/female.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/female.png -------------------------------------------------------------------------------- /ps-mdt/ui/img/fleeca.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/fleeca.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/liquor.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/liquor.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/paleto.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/paleto.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/ems_badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/ems_badge.png -------------------------------------------------------------------------------- /ps-mdt/ui/img/not-found.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/not-found.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/pacific1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/pacific1.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/pacific2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/pacific2.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/pacific3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/pacific3.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/sasp_badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/sasp_badge.png -------------------------------------------------------------------------------- /ps-mdt/ui/img/247SmallBack.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/247SmallBack.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/profile_pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/profile_pic.png -------------------------------------------------------------------------------- /ps-mdt/ui/img/warrant_pfp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/warrant_pfp.png -------------------------------------------------------------------------------- /ps-mdt/ui/img/247SmallFront.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/247SmallFront.webp -------------------------------------------------------------------------------- /ps-mdt/ui/img/ems_badge_zonah.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/ems_badge_zonah.png -------------------------------------------------------------------------------- /ps-mdt/ui/img/Screenshot 2022-07-16 093012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-India/pi-mdt/HEAD/ps-mdt/ui/img/Screenshot 2022-07-16 093012.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pi-mdt 2 | 3 | Css Redesign Of ps-mdt 4 | 5 | # Installation 6 | 7 | Just Drag And Drop The ps-mdt Folder 8 | 9 | # Preview 10 | 11 | (https://youtu.be/1S1-H15wSZ4) 12 | 13 | # Credits 14 | 15 | Scorpion.#5940 , Gabbar Singh#5258 , Team Project India , Project Sloth For MDT And EchoRP For Original MDT 16 | 17 | # Support 18 | https://discord.gg/Kd5B3KPnTv 19 | -------------------------------------------------------------------------------- /ps-mdt/fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | game 'gta5' 3 | 4 | author 'Flawws, Flakey, Idris and the Project Sloth team' 5 | description 'EchoRP MDT Rewrite for QBCore' 6 | version '0.9.9' 7 | 8 | lua54 'yes' 9 | 10 | shared_script 'shared/config.lua' 11 | 12 | server_scripts { 13 | '@oxmysql/lib/MySQL.lua', 14 | 'server/utils.lua', 15 | 'server/dbm.lua', 16 | 'server/main.lua' 17 | } 18 | client_scripts{ 19 | 'client/main.lua', 20 | 'client/cl_impound.lua' 21 | } 22 | 23 | ui_page 'ui/dashboard.html' 24 | 25 | files { 26 | 'ui/img/*.png', 27 | 'ui/img/*.webp', 28 | 'ui/dashboard.html', 29 | 'ui/dmv.html', 30 | 'ui/bolos.html', 31 | 'ui/incidents.html', 32 | 'ui/penalcode.html', 33 | 'ui/reports.html', 34 | 'ui/warrants.html', 35 | 'ui/app.js', 36 | 'ui/style.css', 37 | } -------------------------------------------------------------------------------- /ps-mdt/server/utils.lua: -------------------------------------------------------------------------------- 1 | local QBCore = exports['qb-core']:GetCoreObject() 2 | 3 | function GetPlayerData(source) 4 | local Player = QBCore.Functions.GetPlayer(source) 5 | return Player.PlayerData 6 | end 7 | 8 | function UnpackJob(data) 9 | local job = { 10 | name = data.name, 11 | label = data.label 12 | } 13 | local grade = { 14 | name = data.grade.name, 15 | } 16 | 17 | return job, grade 18 | end 19 | 20 | function PermCheck(src, PlayerData) 21 | local result = true 22 | 23 | if not Config.AllowedJobs[PlayerData.job.name] then 24 | print(("UserId: %s(%d) tried to access the mdt even though they are not authorised (server direct)"):format(GetPlayerName(src), src)) 25 | result = false 26 | end 27 | 28 | return result 29 | end 30 | 31 | function ProfPic(gender, profilepic) 32 | if profilepic then return profilepic end; 33 | if gender == "f" then return "img/female.png" end; 34 | return "img/male.png" 35 | end 36 | 37 | function IsJobAllowedToMDT(job) 38 | if Config.PoliceJobs[job] then 39 | return true 40 | elseif Config.AmbulanceJobs[job] then 41 | return true 42 | elseif Config.DojJobs[job] then 43 | return true 44 | else 45 | return false 46 | end 47 | end 48 | 49 | function GetNameFromPlayerData(PlayerData) 50 | return ('%s %s'):format(PlayerData.charinfo.firstname, PlayerData.charinfo.lastname) 51 | end 52 | -------------------------------------------------------------------------------- /ps-mdt/client/cl_impound.lua: -------------------------------------------------------------------------------- 1 | local currentGarage = 1 2 | 3 | local function doCarDamage(currentVehicle, veh) 4 | local smash = false 5 | local damageOutside = false 6 | local damageOutside2 = false 7 | local engine = veh.engine + 0.0 8 | local body = veh.body + 0.0 9 | 10 | if engine < 200.0 then engine = 200.0 end 11 | if engine > 1000.0 then engine = 950.0 end 12 | if body < 150.0 then body = 150.0 end 13 | if body < 950.0 then smash = true end 14 | if body < 920.0 then damageOutside = true end 15 | if body < 920.0 then damageOutside2 = true end 16 | 17 | Citizen.Wait(100) 18 | SetVehicleEngineHealth(currentVehicle, engine) 19 | 20 | if smash then 21 | SmashVehicleWindow(currentVehicle, 0) 22 | SmashVehicleWindow(currentVehicle, 1) 23 | SmashVehicleWindow(currentVehicle, 2) 24 | SmashVehicleWindow(currentVehicle, 3) 25 | SmashVehicleWindow(currentVehicle, 4) 26 | end 27 | 28 | if damageOutside then 29 | SetVehicleDoorBroken(currentVehicle, 1, true) 30 | SetVehicleDoorBroken(currentVehicle, 6, true) 31 | SetVehicleDoorBroken(currentVehicle, 4, true) 32 | end 33 | 34 | if damageOutside2 then 35 | SetVehicleTyreBurst(currentVehicle, 1, false, 990.0) 36 | SetVehicleTyreBurst(currentVehicle, 2, false, 990.0) 37 | SetVehicleTyreBurst(currentVehicle, 3, false, 990.0) 38 | SetVehicleTyreBurst(currentVehicle, 4, false, 990.0) 39 | end 40 | 41 | if body < 1000 then 42 | SetVehicleBodyHealth(currentVehicle, 985.1) 43 | end 44 | end 45 | 46 | local function TakeOutImpound(vehicle) 47 | local coords = Config.ImpoundLocations[currentGarage] 48 | if coords then 49 | QBCore.Functions.SpawnVehicle(vehicle.vehicle, function(veh) 50 | QBCore.Functions.TriggerCallback('qb-garage:server:GetVehicleProperties', function(properties) 51 | QBCore.Functions.SetVehicleProperties(veh, properties) 52 | SetVehicleNumberPlateText(veh, vehicle.plate) 53 | SetEntityHeading(veh, coords.w) 54 | exports[Config.Fuel]:SetFuel(veh, vehicle.fuel) 55 | doCarDamage(veh, vehicle) 56 | TriggerServerEvent('police:server:TakeOutImpound',vehicle.plate) 57 | TriggerEvent("vehiclekeys:client:SetOwner", QBCore.Functions.GetPlate(veh)) 58 | SetVehicleEngineOn(veh, true, true) 59 | end, vehicle.plate) 60 | end, coords, true) 61 | end 62 | end 63 | 64 | RegisterNetEvent('ps-mdt:client:TakeOutImpound', function(data) 65 | local pos = GetEntityCoords(PlayerPedId()) 66 | currentGarage = data.currentSelection 67 | local takeDist = Config.ImpoundLocations[data.currentSelection] 68 | takeDist = vector3(takeDist.x, takeDist.y, takeDist.z) 69 | if #(pos - takeDist) <= 15.0 then 70 | local vehicle = data.vehicle 71 | TakeOutImpound(data) 72 | else 73 | QBCore.Functions.Notify("You are too far away from the impound location!") 74 | end 75 | end) -------------------------------------------------------------------------------- /ps-mdt/mdt.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `mdt_data` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT, 3 | `cid` VARCHAR(20) DEFAULT NULL, 4 | `information` MEDIUMTEXT DEFAULT NULL, 5 | `tags` TEXT NOT NULL, 6 | `gallery` TEXT NOT NULL, 7 | `jobtype` VARCHAR(25) DEFAULT 'police', 8 | `pfp` TEXT DEFAULT NULL, 9 | `fingerprint` VARCHAR(50) DEFAULT NULL, 10 | PRIMARY KEY (`cid`), 11 | KEY `id` (`id`) 12 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 13 | 14 | CREATE TABLE IF NOT EXISTS `mdt_bulletin` ( 15 | `id` int(11) NOT NULL AUTO_INCREMENT, 16 | `title` TEXT NOT NULL, 17 | `desc` TEXT NOT NULL, 18 | `author` varchar(50) NOT NULL, 19 | `time` varchar(20) NOT NULL, 20 | `jobtype` VARCHAR(25) DEFAULT 'police', 21 | PRIMARY KEY (`id`) 22 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 23 | 24 | CREATE TABLE IF NOT EXISTS `mdt_reports` ( 25 | `id` int(11) NOT NULL AUTO_INCREMENT, 26 | `author` varchar(50) DEFAULT NULL, 27 | `title` varchar(255) DEFAULT NULL, 28 | `type` varchar(50) DEFAULT NULL, 29 | `details` text DEFAULT NULL, 30 | `tags` text DEFAULT NULL, 31 | `officersinvolved` text DEFAULT NULL, 32 | `civsinvolved` text DEFAULT NULL, 33 | `gallery` text DEFAULT NULL, 34 | `time` varchar(20) DEFAULT NULL, 35 | `jobtype` varchar(25) DEFAULT 'police', 36 | PRIMARY KEY (`id`) 37 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 38 | 39 | CREATE TABLE IF NOT EXISTS `mdt_bolos` ( 40 | `id` int(11) NOT NULL AUTO_INCREMENT, 41 | `author` varchar(50) DEFAULT NULL, 42 | `title` varchar(50) DEFAULT NULL, 43 | `plate` varchar(50) DEFAULT NULL, 44 | `owner` varchar(50) DEFAULT NULL, 45 | `individual` varchar(50) DEFAULT NULL, 46 | `detail` text DEFAULT NULL, 47 | `tags` text DEFAULT NULL, 48 | `gallery` text DEFAULT NULL, 49 | `officersinvolved` text DEFAULT NULL, 50 | `time` varchar(20) DEFAULT NULL, 51 | `jobtype` varchar(25) NOT NULL DEFAULT 'police', 52 | PRIMARY KEY (`id`) 53 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 54 | 55 | CREATE TABLE IF NOT EXISTS `mdt_convictions` ( 56 | `id` int(11) NOT NULL AUTO_INCREMENT, 57 | `cid` varchar(50) DEFAULT NULL, 58 | `linkedincident` int(11) NOT NULL DEFAULT 0, 59 | `warrant` varchar(50) DEFAULT NULL, 60 | `guilty` varchar(50) DEFAULT NULL, 61 | `processed` varchar(50) DEFAULT NULL, 62 | `associated` varchar(50) DEFAULT '0', 63 | `charges` text DEFAULT NULL, 64 | `fine` int(11) DEFAULT 0, 65 | `sentence` int(11) DEFAULT 0, 66 | `recfine` int(11) DEFAULT 0, 67 | `recsentence` int(11) DEFAULT 0, 68 | `time` varchar(20) DEFAULT NULL, 69 | PRIMARY KEY (`id`) 70 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 71 | 72 | CREATE TABLE IF NOT EXISTS `mdt_incidents` ( 73 | `id` int(11) NOT NULL AUTO_INCREMENT, 74 | `author` varchar(50) NOT NULL DEFAULT '', 75 | `title` varchar(50) NOT NULL DEFAULT '0', 76 | `details` text NOT NULL, 77 | `tags` text NOT NULL, 78 | `officersinvolved` text NOT NULL, 79 | `civsinvolved` text NOT NULL, 80 | `evidence` text NOT NULL, 81 | `time` varchar(20) DEFAULT NULL, 82 | `jobtype` varchar(25) NOT NULL DEFAULT 'police', 83 | PRIMARY KEY (`id`) 84 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 85 | 86 | CREATE TABLE IF NOT EXISTS `mdt_logs` ( 87 | `id` int(11) NOT NULL AUTO_INCREMENT, 88 | `text` text NOT NULL, 89 | `time` varchar(20) DEFAULT NULL, 90 | `jobtype` varchar(25) DEFAULT 'police', 91 | PRIMARY KEY (`id`) 92 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 93 | 94 | CREATE TABLE IF NOT EXISTS `mdt_vehicleinfo` ( 95 | `id` int(11) NOT NULL AUTO_INCREMENT, 96 | `plate` varchar(50) DEFAULT NULL, 97 | `information` text NOT NULL DEFAULT '', 98 | `stolen` tinyint(1) NOT NULL DEFAULT 0, 99 | `code5` tinyint(1) NOT NULL DEFAULT 0, 100 | `image` text NOT NULL DEFAULT '', 101 | PRIMARY KEY (`id`) 102 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 103 | 104 | CREATE TABLE IF NOT EXISTS `mdt_impound` ( 105 | `id` int(11) NOT NULL AUTO_INCREMENT, 106 | `vehicleid` int(11) NOT NULL, 107 | `linkedreport` int(11) NOT NULL, 108 | `fee` int(11) DEFAULT NULL, 109 | `time` varchar(255) NOT NULL, 110 | PRIMARY KEY (`id`) 111 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -------------------------------------------------------------------------------- /ps-mdt/server/dbm.lua: -------------------------------------------------------------------------------- 1 | local QBCore = exports['qb-core']:GetCoreObject() 2 | 3 | -- Get CitizenIDs from Player License 4 | function GetCitizenID(license) 5 | local result = MySQL.query.await("SELECT citizenid FROM players WHERE license = ?", {license,}) 6 | if result ~= nil then 7 | return result 8 | else 9 | print("Cannot find a CitizenID for License: "..license) 10 | return nil 11 | end 12 | end 13 | 14 | -- (Start) Opening the MDT and sending data 15 | function AddLog(text) 16 | --print(text) 17 | return MySQL.insert.await('INSERT INTO `mdt_logs` (`text`, `time`) VALUES (?,?)', {text, os.time() * 1000}) 18 | -- return exports.oxmysql:execute('INSERT INTO `mdt_logs` (`text`, `time`) VALUES (:text, :time)', { text = text, time = os.time() * 1000 }) 19 | end 20 | 21 | function GetNameFromId(cid) 22 | -- Should be a scalar? 23 | local result = MySQL.scalar.await('SELECT charinfo FROM players WHERE citizenid = @citizenid', { ['@citizenid'] = cid }) 24 | if result ~= nil then 25 | local charinfo = json.decode(result) 26 | local fullname = charinfo['firstname']..' '..charinfo['lastname'] 27 | return fullname 28 | else 29 | --print('Player does not exist') 30 | return nil 31 | end 32 | -- return exports.oxmysql:executeSync('SELECT firstname, lastname FROM `users` WHERE id = :id LIMIT 1', { id = cid }) 33 | end 34 | 35 | -- idk what this is used for either 36 | function GetPersonInformation(cid, jobtype) 37 | local result = MySQL.query.await('SELECT information, tags, gallery, pfp, fingerprint FROM mdt_data WHERE cid = ? and jobtype = ?', { cid, jobtype}) 38 | return result[1] 39 | -- return exports.oxmysql:executeSync('SELECT information, tags, gallery FROM mdt WHERE cid= ? and type = ?', { cid, jobtype }) 40 | end 41 | 42 | function GetPfpFingerPrintInformation(cid) 43 | local result = MySQL.query.await('SELECT pfp, fingerprint FROM mdt_data WHERE cid = ?', { cid }) 44 | return result[1] 45 | end 46 | 47 | -- idk but I guess sure? 48 | function GetIncidentName(id) 49 | -- Should also be a scalar 50 | return MySQL.query.await('SELECT title FROM `mdt_incidents` WHERE id = :id LIMIT 1', { id = id }) 51 | -- return exports.oxmysql:executeSync('SELECT title FROM `mdt_incidents` WHERE id = :id LIMIT 1', { id = id }) 52 | end 53 | 54 | function GetConvictions(cids) 55 | return MySQL.query.await('SELECT * FROM `mdt_convictions` WHERE `cid` IN(?)', { cids }) 56 | -- return exports.oxmysql:executeSync('SELECT * FROM `mdt_convictions` WHERE `cid` IN(?)', { cids }) 57 | end 58 | 59 | function GetLicenseInfo(cid) 60 | local result = MySQL.query.await('SELECT * FROM `licenses` WHERE `cid` = ?', { cid }) 61 | return result 62 | -- return exports.oxmysql:executeSync('SELECT * FROM `licenses` WHERE `cid`=:cid', { cid = cid }) 63 | end 64 | 65 | function CreateUser(cid, tableName) 66 | AddLog("A user was created with the CID: "..cid) 67 | -- return exports.oxmysql:insert("INSERT INTO `"..dbname.."` (cid) VALUES (:cid)", { cid = cid }) 68 | return MySQL.insert.await("INSERT INTO `"..tableName.."` (cid) VALUES (:cid)", { cid = cid }) 69 | end 70 | 71 | function GetPlayerVehicles(cid, cb) 72 | return MySQL.query.await('SELECT id, plate, vehicle FROM player_vehicles WHERE citizenid=:cid', { cid = cid }) 73 | end 74 | 75 | function GetBulletins(JobType) 76 | return MySQL.query.await('SELECT * FROM `mdt_bulletin` WHERE `jobtype` = ? LIMIT 10', { JobType }) 77 | -- return exports.oxmysql:executeSync('SELECT * FROM `mdt_bulletin` WHERE `type`= ? LIMIT 10', { JobType }) 78 | end 79 | 80 | function GetPlayerProperties(cid, cb) 81 | local result = MySQL.query.await('SELECT houselocations.label, houselocations.coords FROM player_houses INNER JOIN houselocations ON player_houses.house = houselocations.name where player_houses.citizenid = ?', {cid}) 82 | return result 83 | end 84 | 85 | function GetPlayerDataById(id) 86 | local Player = QBCore.Functions.GetPlayerByCitizenId(id) 87 | if Player ~= nil then 88 | local response = {citizenid = Player.PlayerData.citizenid, charinfo = Player.PlayerData.charinfo, metadata = Player.PlayerData.metadata, job = Player.PlayerData.job} 89 | return response 90 | else 91 | return MySQL.single.await('SELECT citizenid, charinfo, job, metadata FROM players WHERE citizenid = ? LIMIT 1', { id }) 92 | end 93 | 94 | -- return exports.oxmysql:executeSync('SELECT citizenid, charinfo, job FROM players WHERE citizenid = ? LIMIT 1', { id }) 95 | end 96 | 97 | -- Probs also best not to use 98 | --[[ function GetImpoundStatus(vehicleid, cb) 99 | cb( #(exports.oxmysql:executeSync('SELECT id FROM `impound` WHERE `vehicleid`=:vehicleid', {['vehicleid'] = vehicleid })) > 0 ) 100 | end ]] 101 | 102 | function GetBoloStatus(plate) 103 | local result = MySQL.scalar.await('SELECT id FROM `mdt_bolos` WHERE LOWER(`plate`)=:plate', { plate = string.lower(plate)}) 104 | return result 105 | -- return exports.oxmysql:scalarSync('SELECT id FROM `mdt_bolos` WHERE LOWER(`plate`)=:plate', { plate = string.lower(plate)}) 106 | end 107 | 108 | function GetOwnerName(cid) 109 | local result = MySQL.scalar.await('SELECT charinfo FROM `players` WHERE LOWER(`citizenid`) = ? LIMIT 1', {cid}) 110 | return result 111 | -- return exports.oxmysql:scalarSync('SELECT charinfo FROM `players` WHERE id=:cid LIMIT 1', { cid = cid}) 112 | end 113 | 114 | function GetVehicleInformation(plate, cb) 115 | local result = MySQL.query.await('SELECT id, information FROM `mdt_vehicleinfo` WHERE plate=:plate', { plate = plate}) 116 | cb(result) 117 | end 118 | 119 | function GetPlayerLicenses(identifier) 120 | local response = false 121 | local Player = QBCore.Functions.GetPlayerByCitizenId(identifier) 122 | if Player ~= nil then 123 | return Player.PlayerData.metadata.licences 124 | else 125 | local result = MySQL.scalar.await('SELECT metadata FROM players WHERE citizenid = @identifier', {['@identifier'] = identifier}) 126 | if result ~= nil then 127 | local metadata = json.decode(result) 128 | if metadata["licences"] ~= nil and metadata["licences"] then 129 | return metadata["licences"] 130 | else 131 | return { 132 | ['driver'] = false, 133 | ['business'] = false, 134 | ['weapon'] = false, 135 | ['pilot'] = false 136 | } 137 | end 138 | end 139 | end 140 | end 141 | 142 | function ManageLicense(identifier, type, status) 143 | local Player = QBCore.Functions.GetPlayerByCitizenId(identifier) 144 | local licenseStatus = nil 145 | if status == "give" then licenseStatus = true elseif status == "revoke" then licenseStatus = false end 146 | if Player ~= nil then 147 | local licences = Player.PlayerData.metadata["licences"] 148 | local newLicenses = {} 149 | for k, v in pairs(licences) do 150 | local status = v 151 | if k == type then 152 | status = licenseStatus 153 | end 154 | newLicenses[k] = status 155 | end 156 | Player.Functions.SetMetaData("licences", newLicenses) 157 | else 158 | local licenseType = '$.licences.'..type 159 | local result = MySQL.query.await('UPDATE `players` SET `metadata` = JSON_REPLACE(`metadata`, ?, ?) WHERE `citizenid` = ?', {licenseType, licenseStatus, identifier}) --seems to not work on older MYSQL versions, think about alternative 160 | end 161 | end 162 | 163 | function ManageLicenses(identifier, incomingLicenses) 164 | local Player = QBCore.Functions.GetPlayerByCitizenId(identifier) 165 | if Player ~= nil then 166 | Player.Functions.SetMetaData("licences", incomingLicenses) 167 | 168 | else 169 | local result = MySQL.scalar.await('SELECT metadata FROM players WHERE citizenid = @identifier', {['@identifier'] = identifier}) 170 | result = json.decode(result) 171 | 172 | result.licences = result.licences or { 173 | ['driver'] = true, 174 | ['business'] = false, 175 | ['weapon'] = false, 176 | ['pilot'] = false 177 | } 178 | 179 | for k, _ in pairs(incomingLicenses) do 180 | result.licences[k] = incomingLicenses[k] 181 | end 182 | MySQL.query.await('UPDATE `players` SET `metadata` = @metadata WHERE citizenid = @citizenid', {['@metadata'] = json.encode(result), ['@citizenid'] = identifier}) 183 | end 184 | end 185 | -------------------------------------------------------------------------------- /ps-mdt/client/main.lua: -------------------------------------------------------------------------------- 1 | QBCore = exports['qb-core']:GetCoreObject() 2 | local PlayerData = {} 3 | local CurrentCops = 0 4 | local isOpen = false 5 | local callSign = "" 6 | local tablet = 0 7 | local tabletDict = "amb@code_human_in_bus_passenger_idles@female@tablet@base" 8 | local tabletAnim = "base" 9 | local tabletProp = `prop_cs_tablet` 10 | local tabletBone = 60309 11 | local tabletOffset = vector3(0.03, 0.002, -0.0) 12 | local tabletRot = vector3(10.0, 160.0, 0.0) 13 | 14 | -- Events from qbcore 15 | RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() 16 | PlayerData = QBCore.Functions.GetPlayerData() 17 | callSign = PlayerData.metadata.callsign 18 | end) 19 | 20 | RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() 21 | TriggerServerEvent("ps-mdt:server:OnPlayerUnload") 22 | PlayerData = {} 23 | end) 24 | 25 | RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo) 26 | PlayerData.job = JobInfo 27 | end) 28 | 29 | RegisterNetEvent('QBCore:Client:OnGangUpdate', function(GangInfo) 30 | PlayerData.gang = GangInfo 31 | end) 32 | 33 | RegisterNetEvent("QBCore:Client:SetDuty", function(job, state) 34 | if AllowedJob(job) then 35 | TriggerServerEvent("ps-mdt:server:ToggleDuty") 36 | end 37 | end) 38 | 39 | RegisterNetEvent('police:SetCopCount', function(amount) 40 | CurrentCops = amount 41 | end) 42 | 43 | RegisterNetEvent('QBCore:Player:SetPlayerData', function(val) 44 | PlayerData = val 45 | end) 46 | 47 | AddEventHandler('onResourceStart', function(resourceName) 48 | if GetCurrentResourceName() ~= resourceName then return end 49 | Wait(150) 50 | PlayerData = QBCore.Functions.GetPlayerData() 51 | callSign = PlayerData.metadata.callsign 52 | end) 53 | 54 | --==================================================================================== 55 | ------------------------------------------ 56 | -- Functions -- 57 | ------------------------------------------ 58 | --====================================================================================\ 59 | 60 | RegisterKeyMapping('mdt', 'Open Police MDT', 'keyboard', 'k') 61 | 62 | RegisterCommand('mdt', function() 63 | local plyPed = PlayerPedId() 64 | PlayerData = QBCore.Functions.GetPlayerData() 65 | if not PlayerData.metadata["isdead"] and not PlayerData.metadata["inlaststand"] and not PlayerData.metadata["ishandcuffed"] and not IsPauseMenuActive() then 66 | if GetJobType(PlayerData.job.name) ~= nil then 67 | TriggerServerEvent('mdt:server:openMDT') 68 | end 69 | else 70 | QBCore.Functions.Notify("Can't do that!", "error") 71 | end 72 | end, false) 73 | 74 | Citizen.CreateThread(function() 75 | TriggerEvent('chat:addSuggestion', '/mdt', 'Open the emergency services MDT', {}) 76 | end) 77 | 78 | local function doAnimation() 79 | if not isOpen then return end 80 | -- Animation 81 | RequestAnimDict(tabletDict) 82 | while not HasAnimDictLoaded(tabletDict) do Citizen.Wait(100) end 83 | -- Model 84 | RequestModel(tabletProp) 85 | while not HasModelLoaded(tabletProp) do Citizen.Wait(100) end 86 | 87 | local plyPed = PlayerPedId() 88 | local tabletObj = CreateObject(tabletProp, 0.0, 0.0, 0.0, true, true, false) 89 | local tabletBoneIndex = GetPedBoneIndex(plyPed, tabletBone) 90 | 91 | AttachEntityToEntity(tabletObj, plyPed, tabletBoneIndex, tabletOffset.x, tabletOffset.y, tabletOffset.z, tabletRot.x, tabletRot.y, tabletRot.z, true, false, false, false, 2, true) 92 | SetModelAsNoLongerNeeded(tabletProp) 93 | 94 | CreateThread(function() 95 | while isOpen do 96 | Wait(0) 97 | if not IsEntityPlayingAnim(plyPed, tabletDict, tabletAnim, 3) then 98 | TaskPlayAnim(plyPed, tabletDict, tabletAnim, 3.0, 3.0, -1, 49, 0, 0, 0, 0) 99 | end 100 | end 101 | 102 | 103 | ClearPedSecondaryTask(plyPed) 104 | Citizen.Wait(250) 105 | DetachEntity(tabletObj, true, false) 106 | DeleteEntity(tabletObj) 107 | end) 108 | end 109 | 110 | 111 | local function CurrentDuty(duty) 112 | if duty == 1 then 113 | return "10-41" 114 | end 115 | return "10-42" 116 | end 117 | 118 | local function EnableGUI(enable) 119 | SetNuiFocus(enable, enable) 120 | SendNUIMessage({ type = "show", enable = enable, job = PlayerData.job.name, rosterLink = Config.RosterLink[PlayerData.job.name] }) 121 | isOpen = enable 122 | doAnimation() 123 | end 124 | 125 | local function RefreshGUI() 126 | SetNuiFocus(false, false) 127 | SendNUIMessage({ type = "show", enable = false, job = PlayerData.job.name, rosterLink = Config.RosterLink[PlayerData.job.name] }) 128 | isOpen = false 129 | end 130 | 131 | --// Non local function so above EHs can utilise 132 | function AllowedJob(job) 133 | for key, _ in pairs(Config.AllowedJobs) do 134 | if key == job then 135 | return true 136 | end 137 | end 138 | --// Return false if current job is not in allowed list 139 | return false 140 | end 141 | 142 | 143 | --==================================================================================== 144 | ------------------------------------------ 145 | -- MAIN PAGE -- 146 | ------------------------------------------ 147 | --==================================================================================== 148 | 149 | 150 | RegisterCommand("restartmdt", function(source, args, rawCommand) 151 | RefreshGUI() 152 | end, false) 153 | 154 | RegisterNUICallback("deleteBulletin", function(data, cb) 155 | local id = data.id 156 | TriggerServerEvent('mdt:server:deleteBulletin', id, data.title) 157 | cb(true) 158 | end) 159 | 160 | RegisterNUICallback("newBulletin", function(data, cb) 161 | local title = data.title 162 | local info = data.info 163 | local time = data.time 164 | TriggerServerEvent('mdt:server:NewBulletin', title, info, time) 165 | cb(true) 166 | end) 167 | 168 | RegisterNUICallback('escape', function(data, cb) 169 | EnableGUI(false) 170 | cb(true) 171 | end) 172 | 173 | RegisterNetEvent('mdt:client:dashboardbulletin', function(sentData) 174 | SendNUIMessage({ type = "bulletin", data = sentData }) 175 | end) 176 | 177 | RegisterNetEvent('mdt:client:dashboardWarrants', function() 178 | QBCore.Functions.TriggerCallback("mdt:server:getWarrants", function(data) 179 | if data then 180 | SendNUIMessage({ type = "warrants", data = data }) 181 | end 182 | end) 183 | -- SendNUIMessage({ type = "warrants",}) 184 | end) 185 | 186 | RegisterNUICallback("getAllDashboardData", function(data, cb) 187 | TriggerEvent("mdt:client:dashboardWarrants") 188 | cb(true) 189 | end) 190 | 191 | 192 | RegisterNetEvent('mdt:client:dashboardReports', function(sentData) 193 | SendNUIMessage({ type = "reports", data = sentData }) 194 | end) 195 | 196 | RegisterNetEvent('mdt:client:dashboardCalls', function(sentData) 197 | SendNUIMessage({ type = "calls", data = sentData }) 198 | end) 199 | 200 | RegisterNetEvent('mdt:client:newBulletin', function(ignoreId, sentData, job) 201 | if ignoreId == GetPlayerServerId(PlayerId()) then return end; 202 | if AllowedJob(PlayerData.job.name) then 203 | SendNUIMessage({ type = "newBulletin", data = sentData }) 204 | end 205 | end) 206 | 207 | RegisterNetEvent('mdt:client:deleteBulletin', function(ignoreId, sentData, job) 208 | if ignoreId == GetPlayerServerId(PlayerId()) then return end; 209 | if AllowedJob(PlayerData.job.name) then 210 | SendNUIMessage({ type = "deleteBulletin", data = sentData }) 211 | end 212 | end) 213 | 214 | RegisterNetEvent('mdt:client:open', function(bulletin, activeUnits, calls, cid) 215 | EnableGUI(true) 216 | local x, y, z = table.unpack(GetEntityCoords(PlayerPedId())) 217 | 218 | local currentStreetHash, intersectStreetHash = GetStreetNameAtCoord(x, y, z) 219 | local currentStreetName = GetStreetNameFromHashKey(currentStreetHash) 220 | local intersectStreetName = GetStreetNameFromHashKey(intersectStreetHash) 221 | local zone = tostring(GetNameOfZone(x, y, z)) 222 | local area = GetLabelText(zone) 223 | local playerStreetsLocation = area 224 | 225 | if not zone then zone = "UNKNOWN" end; 226 | 227 | if intersectStreetName ~= nil and intersectStreetName ~= "" then playerStreetsLocation = currentStreetName .. ", " .. intersectStreetName .. ", " .. area 228 | elseif currentStreetName ~= nil and currentStreetName ~= "" then playerStreetsLocation = currentStreetName .. ", " .. area 229 | else playerStreetsLocation = area end 230 | 231 | -- local grade = PlayerData.job.grade.name 232 | 233 | SendNUIMessage({ type = "data", activeUnits = activeUnits, citizenid = cid, ondutyonly = Config.OnlyShowOnDuty, name = "Welcome, " ..PlayerData.job.grade.name..' '..PlayerData.charinfo.lastname:sub(1,1):upper()..PlayerData.charinfo.lastname:sub(2), location = playerStreetsLocation, fullname = PlayerData.charinfo.firstname..' '..PlayerData.charinfo.lastname, bulletin = bulletin }) 234 | SendNUIMessage({ type = "calls", data = calls }) 235 | TriggerEvent("mdt:client:dashboardWarrants") 236 | end) 237 | 238 | RegisterNetEvent('mdt:client:exitMDT', function() 239 | EnableGUI(false) 240 | end) 241 | 242 | --==================================================================================== 243 | ------------------------------------------ 244 | -- PROFILE PAGE -- 245 | ------------------------------------------ 246 | --==================================================================================== 247 | 248 | RegisterNUICallback("searchProfiles", function(data, cb) 249 | local p = promise.new() 250 | 251 | QBCore.Functions.TriggerCallback('mdt:server:SearchProfile', function(result) 252 | p:resolve(result) 253 | end, data.name) 254 | 255 | local data = Citizen.Await(p) 256 | 257 | cb(data) 258 | end) 259 | 260 | 261 | RegisterNetEvent('mdt:client:searchProfile', function(sentData, isLimited) 262 | SendNUIMessage({ type = "profiles", data = sentData, isLimited = isLimited }) 263 | end) 264 | 265 | RegisterNUICallback("saveProfile", function(data, cb) 266 | local profilepic = data.pfp 267 | local information = data.description 268 | local cid = data.id 269 | local fName = data.fName 270 | local sName = data.sName 271 | local tags = data.tags 272 | local gallery = data.gallery 273 | local fingerprint = data.fingerprint 274 | local licenses = data.licenses 275 | 276 | TriggerServerEvent("mdt:server:saveProfile", profilepic, information, cid, fName, sName, tags, gallery, fingerprint, licenses) 277 | cb(true) 278 | end) 279 | 280 | RegisterNUICallback("getProfileData", function(data, cb) 281 | local id = data.id 282 | local p = nil 283 | local getProfileDataPromise = function(data) 284 | if p then return end 285 | p = promise.new() 286 | QBCore.Functions.TriggerCallback('mdt:server:GetProfileData', function(result) 287 | p:resolve(result) 288 | end, data) 289 | return Citizen.Await(p) 290 | end 291 | local pP = nil 292 | local result = getProfileDataPromise(id) 293 | 294 | --[[ local getProfileProperties = function(data) 295 | if pP then return end 296 | pP = promise.new() 297 | QBCore.Functions.TriggerCallback('qb-phone:server:MeosGetPlayerHouses', function(result) 298 | pP:resolve(result) 299 | end, data) 300 | return Citizen.Await(pP) 301 | end 302 | local propertiesResult = getProfileProperties(id) 303 | result.properties = propertiesResult 304 | ]] 305 | local vehicles=result.vehicles 306 | for i=1,#vehicles do 307 | local vehicle=result.vehicles[i] 308 | result.vehicles[i]['model'] = GetLabelText(GetDisplayNameFromVehicleModel(vehicle['vehicle'])) 309 | end 310 | p = nil 311 | return cb(result) 312 | end) 313 | 314 | RegisterNUICallback("newTag", function(data, cb) 315 | if data.id ~= "" and data.tag ~= "" then 316 | TriggerServerEvent('mdt:server:newTag', data.id, data.tag) 317 | end 318 | cb(true) 319 | end) 320 | 321 | RegisterNUICallback("removeProfileTag", function(data, cb) 322 | local cid = data.cid 323 | local tagtext = data.text 324 | TriggerServerEvent('mdt:server:removeProfileTag', cid, tagtext) 325 | cb(true) 326 | end) 327 | 328 | RegisterNUICallback("updateLicence", function(data, cb) 329 | local type = data.type 330 | local status = data.status 331 | local cid = data.cid 332 | TriggerServerEvent('mdt:server:updateLicense', cid, type, status) 333 | cb(true) 334 | end) 335 | 336 | RegisterNUICallback("searchIncidents", function(data, cb) 337 | local incident = data.incident 338 | TriggerServerEvent('mdt:server:searchIncidents', incident) 339 | cb(true) 340 | end) 341 | 342 | RegisterNUICallback("getIncidentData", function(data, cb) 343 | local id = data.id 344 | TriggerServerEvent('mdt:server:getIncidentData', id) 345 | cb(true) 346 | end) 347 | 348 | RegisterNUICallback("incidentSearchPerson", function(data, cb) 349 | local name = data.name 350 | TriggerServerEvent('mdt:server:incidentSearchPerson', name ) 351 | cb(true) 352 | end) 353 | 354 | RegisterNetEvent('mdt:client:getProfileData', function(sentData, isLimited) 355 | if not isLimited then 356 | local vehicles = sentData['vehicles'] 357 | for i=1, #vehicles do 358 | sentData['vehicles'][i]['plate'] = string.upper(sentData['vehicles'][i]['plate']) 359 | local tempModel = vehicles[i]['model'] 360 | if tempModel and tempModel ~= "Unknown" then 361 | local DisplayNameModel = GetDisplayNameFromVehicleModel(tempModel) 362 | local LabelText = GetLabelText(DisplayNameModel) 363 | if LabelText == "NULL" then LabelText = DisplayNameModel end 364 | sentData['vehicles'][i]['model'] = LabelText 365 | end 366 | end 367 | end 368 | SendNUIMessage({ type = "profileData", data = sentData, isLimited = isLimited }) 369 | end) 370 | 371 | RegisterNetEvent('mdt:client:getIncidents', function(sentData) 372 | SendNUIMessage({ type = "incidents", data = sentData }) 373 | end) 374 | 375 | RegisterNetEvent('mdt:client:getIncidentData', function(sentData, sentConvictions) 376 | SendNUIMessage({ type = "incidentData", data = sentData, convictions = sentConvictions }) 377 | end) 378 | 379 | RegisterNetEvent('mdt:client:incidentSearchPerson', function(sentData) 380 | SendNUIMessage({ type = "incidentSearchPerson", data = sentData }) 381 | end) 382 | 383 | 384 | RegisterNUICallback('SetHouseLocation', function(data, cb) 385 | local coords = {} 386 | for word in data.coord[1]:gmatch('[^,%s]+') do 387 | coords[#coords+1] = tonumber(word) 388 | end 389 | SetNewWaypoint(coords[1], coords[2]) 390 | QBCore.Functions.Notify('GPS has been set!', 'success') 391 | end) 392 | 393 | --==================================================================================== 394 | ------------------------------------------ 395 | -- BOLO PAGE -- 396 | ------------------------------------------ 397 | --==================================================================================== 398 | 399 | RegisterNUICallback("searchBolos", function(data, cb) 400 | local searchVal = data.searchVal 401 | TriggerServerEvent('mdt:server:searchBolos', searchVal) 402 | cb(true) 403 | end) 404 | 405 | RegisterNUICallback("getAllBolos", function(data, cb) 406 | TriggerServerEvent('mdt:server:getAllBolos') 407 | cb(true) 408 | end) 409 | 410 | RegisterNUICallback("getAllIncidents", function(data, cb) 411 | TriggerServerEvent('mdt:server:getAllIncidents') 412 | cb(true) 413 | end) 414 | 415 | RegisterNUICallback("getBoloData", function(data, cb) 416 | local id = data.id 417 | TriggerServerEvent('mdt:server:getBoloData', id) 418 | cb(true) 419 | end) 420 | 421 | RegisterNUICallback("newBolo", function(data, cb) 422 | local existing = data.existing 423 | local id = data.id 424 | local title = data.title 425 | local plate = data.plate 426 | local owner = data.owner 427 | local individual = data.individual 428 | local detail = data.detail 429 | local tags = data.tags 430 | local gallery = data.gallery 431 | local officers = data.officers 432 | local time = data.time 433 | TriggerServerEvent('mdt:server:newBolo', existing, id, title, plate, owner, individual, detail, tags, gallery, officers, time) 434 | cb(true) 435 | end) 436 | 437 | RegisterNUICallback("deleteBolo", function(data, cb) 438 | local id = data.id 439 | TriggerServerEvent('mdt:server:deleteBolo', id) 440 | cb(true) 441 | end) 442 | 443 | RegisterNUICallback("deleteICU", function(data, cb) 444 | local id = data.id 445 | TriggerServerEvent('mdt:server:deleteICU', id) 446 | cb(true) 447 | end) 448 | 449 | RegisterNetEvent('mdt:client:getBolos', function(sentData) 450 | SendNUIMessage({ type = "bolos", data = sentData }) 451 | end) 452 | 453 | RegisterNetEvent('mdt:client:getAllIncidents', function(sentData) 454 | SendNUIMessage({ type = "incidents", data = sentData }) 455 | end) 456 | 457 | RegisterNetEvent('mdt:client:getAllBolos', function(sentData) 458 | SendNUIMessage({ type = "bolos", data = sentData }) 459 | end) 460 | 461 | RegisterNetEvent('mdt:client:getBoloData', function(sentData) 462 | SendNUIMessage({ type = "boloData", data = sentData }) 463 | end) 464 | 465 | RegisterNetEvent('mdt:client:boloComplete', function(sentData) 466 | SendNUIMessage({ type = "boloComplete", data = sentData }) 467 | end) 468 | 469 | --==================================================================================== 470 | ------------------------------------------ 471 | -- REPORTS PAGE -- 472 | ------------------------------------------ 473 | --==================================================================================== 474 | 475 | RegisterNUICallback("getAllReports", function(data, cb) 476 | TriggerServerEvent('mdt:server:getAllReports') 477 | cb(true) 478 | end) 479 | 480 | RegisterNUICallback("getReportData", function(data, cb) 481 | local id = data.id 482 | TriggerServerEvent('mdt:server:getReportData', id) 483 | cb(true) 484 | end) 485 | 486 | RegisterNUICallback("searchReports", function(data, cb) 487 | local name = data.name 488 | TriggerServerEvent('mdt:server:searchReports', name) 489 | cb(true) 490 | end) 491 | 492 | RegisterNUICallback("newReport", function(data, cb) 493 | local existing = data.existing 494 | local id = data.id 495 | local title = data.title 496 | local reporttype = data.type 497 | local details = data.details 498 | local tags = data.tags 499 | local gallery = data.gallery 500 | local officers = data.officers 501 | local civilians = data.civilians 502 | local time = data.time 503 | 504 | TriggerServerEvent('mdt:server:newReport', existing, id, title, reporttype, details, tags, gallery, officers, civilians, time) 505 | cb(true) 506 | end) 507 | 508 | RegisterNetEvent('mdt:client:getAllReports', function(sentData) 509 | SendNUIMessage({ type = "reports", data = sentData }) 510 | end) 511 | 512 | RegisterNetEvent('mdt:client:getReportData', function(sentData) 513 | SendNUIMessage({ type = "reportData", data = sentData }) 514 | end) 515 | 516 | RegisterNetEvent('mdt:client:reportComplete', function(sentData) 517 | SendNUIMessage({ type = "reportComplete", data = sentData }) 518 | end) 519 | 520 | --==================================================================================== 521 | ------------------------------------------ 522 | -- DMV PAGE -- 523 | ------------------------------------------ 524 | --==================================================================================== 525 | RegisterNUICallback("searchVehicles", function(data, cb) 526 | 527 | local p = promise.new() 528 | 529 | QBCore.Functions.TriggerCallback('mdt:server:SearchVehicles', function(result) 530 | p:resolve(result) 531 | end, data.name) 532 | 533 | local result = Citizen.Await(p) 534 | for i=1, #result do 535 | local vehicle = result[i] 536 | local mods = json.decode(result[i].mods) 537 | result[i]['plate'] = string.upper(result[i]['plate']) 538 | result[i]['color'] = Config.ColorInformation[mods['color1']] 539 | result[i]['colorName'] = Config.ColorNames[mods['color1']] 540 | result[i]['model'] = GetLabelText(GetDisplayNameFromVehicleModel(vehicle['vehicle'])) 541 | end 542 | cb(result) 543 | 544 | end) 545 | 546 | RegisterNUICallback("getVehicleData", function(data, cb) 547 | local plate = data.plate 548 | TriggerServerEvent('mdt:server:getVehicleData', plate) 549 | cb(true) 550 | end) 551 | 552 | RegisterNUICallback("saveVehicleInfo", function(data, cb) 553 | local dbid = data.dbid 554 | local plate = data.plate 555 | local imageurl = data.imageurl 556 | local notes = data.notes 557 | local stolen = data.stolen 558 | local code5 = data.code5 559 | local impound = data.impound 560 | local JobType = GetJobType(PlayerData.job.name) 561 | if JobType == 'police' and impound.impoundChanged == true then 562 | if impound.impoundActive then 563 | local found = 0 564 | local plate = string.upper(string.gsub(data['plate'], "^%s*(.-)%s*$", "%1")) 565 | local vehicles = GetGamePool('CVehicle') 566 | 567 | for k,v in pairs(vehicles) do 568 | local plt = string.upper(string.gsub(GetVehicleNumberPlateText(v), "^%s*(.-)%s*$", "%1")) 569 | if plt == plate then 570 | local dist = #(GetEntityCoords(PlayerPedId()) - GetEntityCoords(v)) 571 | if dist < 5.0 then 572 | found = VehToNet(v) 573 | SendNUIMessage({ type = "greenImpound" }) 574 | TriggerServerEvent('mdt:server:saveVehicleInfo', dbid, plate, imageurl, notes, stolen, code5, impound) 575 | end 576 | break 577 | end 578 | end 579 | 580 | if found == 0 then 581 | QBCore.Functions.Notify('Vehicle not found!', 'error') 582 | SendNUIMessage({ type = "redImpound" }) 583 | end 584 | else 585 | local ped = PlayerPedId() 586 | local playerPos = GetEntityCoords(ped) 587 | for k, v in pairs(Config.ImpoundLocations) do 588 | if (#(playerPos - vector3(v.x, v.y, v.z)) < 20.0) then 589 | impound.CurrentSelection = k 590 | TriggerServerEvent('mdt:server:saveVehicleInfo', dbid, plate, imageurl, notes, stolen, code5, impound) 591 | break 592 | end 593 | end 594 | end 595 | else 596 | TriggerServerEvent('mdt:server:saveVehicleInfo', dbid, plate, imageurl, notes, stolen, code5, impound) 597 | end 598 | cb(true) 599 | end) 600 | 601 | RegisterNUICallback("getAllLogs", function(data, cb) 602 | TriggerServerEvent('mdt:server:getAllLogs') 603 | cb(true) 604 | end) 605 | 606 | RegisterNUICallback("getPenalCode", function(data, cb) 607 | TriggerServerEvent('mdt:server:getPenalCode') 608 | cb(true) 609 | end) 610 | 611 | RegisterNUICallback("toggleDuty", function(data, cb) 612 | TriggerServerEvent('QBCore:ToggleDuty') 613 | cb(true) 614 | end) 615 | 616 | RegisterNUICallback("setCallsign", function(data, cb) 617 | TriggerServerEvent('mdt:server:setCallsign', data.cid, data.newcallsign) 618 | cb(true) 619 | end) 620 | 621 | RegisterNUICallback("setRadio", function(data, cb) 622 | TriggerServerEvent('mdt:server:setRadio', data.cid, data.newradio) 623 | cb(true) 624 | end) 625 | 626 | RegisterNUICallback("saveIncident", function(data, cb) 627 | TriggerServerEvent('mdt:server:saveIncident', data.ID, data.title, data.information, data.tags, data.officers, data.civilians, data.evidence, data.associated, data.time) 628 | cb(true) 629 | end) 630 | 631 | RegisterNUICallback("removeIncidentCriminal", function(data, cb) 632 | TriggerServerEvent('mdt:server:removeIncidentCriminal', data.cid, data.incidentId) 633 | cb(true) 634 | end) 635 | 636 | RegisterNetEvent('mdt:client:getVehicleData', function(sentData) 637 | if sentData and sentData[1] then 638 | local vehicle = sentData[1] 639 | local vehData = json.decode(vehicle['vehicle']) 640 | vehicle['color'] = Config.ColorInformation[vehicle['color1']] 641 | vehicle['colorName'] = Config.ColorNames[vehicle['color1']] 642 | vehicle['model'] = GetLabelText(GetDisplayNameFromVehicleModel(vehicle['vehicle'])) 643 | vehicle['class'] = Config.ClassList[GetVehicleClassFromName(vehicle['vehicle'])] 644 | vehicle['vehicle'] = nil 645 | SendNUIMessage({ type = "getVehicleData", data = vehicle }) 646 | end 647 | end) 648 | 649 | RegisterNetEvent('mdt:client:updateVehicleDbId', function(sentData) 650 | SendNUIMessage({ type = "updateVehicleDbId", data = tonumber(sentData) }) 651 | end) 652 | 653 | RegisterNetEvent('mdt:client:getAllLogs', function(sentData) 654 | SendNUIMessage({ type = "getAllLogs", data = sentData }) 655 | end) 656 | 657 | RegisterNetEvent('mdt:client:getPenalCode', function(titles, penalcode) 658 | SendNUIMessage({ type = "getPenalCode", titles = titles, penalcode = penalcode }) 659 | end) 660 | 661 | RegisterNetEvent('mdt:client:setRadio', function(radio) 662 | if type(tonumber(radio)) == "number" then 663 | exports["pma-voice"]:setVoiceProperty("radioEnabled", true) 664 | exports["pma-voice"]:setRadioChannel(tonumber(radio)) 665 | QBCore.Functions.Notify("You have set your radio frequency to "..radio..".", "success") 666 | else 667 | QBCore.Functions.Notify("Invalid Station(Please enter a number)", "error") 668 | end 669 | end) 670 | 671 | RegisterNetEvent('mdt:client:sig100', function(radio, type) 672 | local job = PlayerData.job.name 673 | local duty = PlayerData.job.onduty 674 | if AllowedJob(job) and duty == 1 then 675 | if type == true then 676 | exports['erp_notifications']:PersistentAlert("START", "signall100-"..radio, "inform", "Radio "..radio.." is currently signal 100!") 677 | end 678 | end 679 | if not type then 680 | exports['erp_notifications']:PersistentAlert("END", "signall100-"..radio) 681 | end 682 | end) 683 | 684 | RegisterNetEvent('mdt:client:updateCallsign', function(callsign) 685 | callSign = tostring(callsign) 686 | end) 687 | 688 | RegisterNetEvent('mdt:client:updateIncidentDbId', function(sentData) 689 | SendNUIMessage({ type = "updateIncidentDbId", data = tonumber(sentData) }) 690 | end) 691 | 692 | 693 | --==================================================================================== 694 | ------------------------------------------ 695 | -- DISPATCH PAGE -- 696 | ------------------------------------------ 697 | --==================================================================================== 698 | 699 | RegisterNetEvent('dispatch:clNotify', function(sNotificationData, sNotificationId) 700 | if LocalPlayer.state.isLoggedIn then 701 | sNotificationData.playerJob = PlayerData.job.name 702 | SendNUIMessage({ type = "call", data = sNotificationData }) 703 | end 704 | end) 705 | 706 | RegisterNUICallback("setWaypoint", function(data, cb) 707 | TriggerServerEvent('mdt:server:setWaypoint', data.callid) 708 | cb(true) 709 | end) 710 | 711 | RegisterNUICallback("callDetach", function(data, cb) 712 | TriggerServerEvent('mdt:server:callDetach', data.callid) 713 | cb(true) 714 | end) 715 | 716 | RegisterNUICallback("removeCallBlip", function(data, cb) 717 | TriggerEvent('ps-dispatch:client:removeCallBlip', data.callid) 718 | cb(true) 719 | end) 720 | 721 | RegisterNUICallback("callAttach", function(data, cb) 722 | TriggerServerEvent('mdt:server:callAttach', data.callid) 723 | cb(true) 724 | end) 725 | 726 | RegisterNUICallback("attachedUnits", function(data, cb) 727 | TriggerServerEvent('mdt:server:attachedUnits', data.callid) 728 | cb(true) 729 | end) 730 | 731 | RegisterNUICallback("callDispatchDetach", function(data, cb) 732 | TriggerServerEvent('mdt:server:callDispatchDetach', data.callid, data.cid) 733 | cb(true) 734 | end) 735 | 736 | RegisterNUICallback("setDispatchWaypoint", function(data, cb) 737 | TriggerServerEvent('mdt:server:setDispatchWaypoint', data.callid, data.cid) 738 | cb(true) 739 | end) 740 | 741 | RegisterNUICallback("callDragAttach", function(data, cb) 742 | TriggerServerEvent('mdt:server:callDragAttach', data.callid, data.cid) 743 | cb(true) 744 | end) 745 | 746 | RegisterNUICallback("setWaypointU", function(data, cb) 747 | TriggerServerEvent('mdt:server:setWaypoint:unit', data.cid) 748 | cb(true) 749 | end) 750 | 751 | RegisterNUICallback("dispatchMessage", function(data, cb) 752 | TriggerServerEvent('mdt:server:sendMessage', data.message, data.time) 753 | cb(true) 754 | end) 755 | 756 | RegisterNUICallback("refreshDispatchMsgs", function(data, cb) 757 | TriggerServerEvent('mdt:server:refreshDispatchMsgs') 758 | cb(true) 759 | end) 760 | 761 | RegisterNUICallback("dispatchNotif", function(data, cb) 762 | local info = data['data'] 763 | local mentioned = false 764 | if callSign ~= "" then if string.find(string.lower(info['message']),string.lower(string.gsub(callSign,'-','%%-'))) then mentioned = true end end 765 | if mentioned then 766 | 767 | -- Send notification to phone?? 768 | TriggerEvent('erp_phone:sendNotification', {img = info['profilepic'], title = "Dispatch (Mention)", content = info['message'], time = 7500, customPic = true }) 769 | 770 | PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false) 771 | PlaySoundFrontend(-1, "Event_Start_Text", "GTAO_FM_Events_Soundset", 0) 772 | else 773 | TriggerEvent('erp_phone:sendNotification', {img = info['profilepic'], title = "Dispatch ("..info['name']..")", content = info['message'], time = 5000, customPic = true }) 774 | end 775 | cb(true) 776 | end) 777 | 778 | RegisterNUICallback("getCallResponses", function(data, cb) 779 | TriggerServerEvent('mdt:server:getCallResponses', data.callid) 780 | cb(true) 781 | end) 782 | 783 | RegisterNUICallback("sendCallResponse", function(data, cb) 784 | TriggerServerEvent('mdt:server:sendCallResponse', data.message, data.time, data.callid) 785 | cb(true) 786 | end) 787 | 788 | --[[ RegisterNUICallback("impoundVehicle", function(data, cb) 789 | local JobType = GetJobType(PlayerData.job.name) 790 | if JobType == 'police' then 791 | local found = 0 792 | local plate = string.upper(string.gsub(data['plate'], "^%s*(.-)%s*$", "%1")) 793 | local vehicles = GetGamePool('CVehicle') 794 | 795 | for k,v in pairs(vehicles) do 796 | local plt = string.upper(string.gsub(GetVehicleNumberPlateText(v), "^%s*(.-)%s*$", "%1")) 797 | if plt == plate then 798 | local dist = #(GetEntityCoords(PlayerPedId()) - GetEntityCoords(v)) 799 | if dist < 5.0 then 800 | found = VehToNet(v) 801 | end 802 | break 803 | end 804 | end 805 | 806 | if found == 0 then 807 | QBCore.Functions.Notify('Vehicle not found!', 'error') 808 | return 809 | end 810 | 811 | SendNUIMessage({ type = "greenShit" }) 812 | TriggerServerEvent('mdt:server:impoundVehicle', data, found) 813 | cb('ok') 814 | end 815 | end) ]] 816 | 817 | RegisterNUICallback("removeImpound", function(data, cb) 818 | local ped = PlayerPedId() 819 | local playerPos = GetEntityCoords(ped) 820 | for k, v in pairs(Config.ImpoundLocations) do 821 | if (#(playerPos - vector3(v.x, v.y, v.z)) < 20.0) then 822 | TriggerServerEvent('mdt:server:removeImpound', data['plate'], k) 823 | break 824 | end 825 | end 826 | cb('ok') 827 | end) 828 | 829 | RegisterNUICallback("statusImpound", function(data, cb) 830 | TriggerServerEvent('mdt:server:statusImpound', data['plate']) 831 | cb('ok') 832 | end) 833 | 834 | RegisterNUICallback('openCamera', function(data) 835 | local camId = tonumber(data.cam) 836 | TriggerEvent('police:client:ActiveCamera', camId) 837 | end) 838 | 839 | RegisterNetEvent('mdt:client:attachedUnits', function(sentData, callid) 840 | SendNUIMessage({ type = "attachedUnits", data = sentData, callid = callid }) 841 | end) 842 | 843 | RegisterNetEvent('mdt:client:setWaypoint', function(callInformation) 844 | SetNewWaypoint(callInformation['origin']['x'], callInformation['origin']['y']) 845 | end) 846 | 847 | RegisterNetEvent('mdt:client:callDetach', function(callid, sentData) 848 | local job = PlayerData.job.name 849 | if AllowedJob(job) then 850 | SendNUIMessage({ type = "callDetach", callid = callid, data = tonumber(sentData) }) 851 | end 852 | end) 853 | RegisterNetEvent('mdt:client:callAttach', function(callid, sentData) 854 | local job = PlayerData.job.name 855 | if AllowedJob(job) then 856 | SendNUIMessage({ type = "callAttach", callid = callid, data = tonumber(sentData) }) 857 | end 858 | end) 859 | 860 | RegisterNetEvent('mdt:client:setWaypoint:unit', function(sentData) 861 | SetNewWaypoint(sentData.x, sentData.y) 862 | end) 863 | 864 | RegisterNetEvent('mdt:client:dashboardMessage', function(sentData) 865 | local job = PlayerData.job.name 866 | if AllowedJob(job) then 867 | SendNUIMessage({ type = "dispatchmessage", data = sentData }) 868 | end 869 | end) 870 | 871 | RegisterNetEvent('mdt:client:dashboardMessages', function(sentData) 872 | SendNUIMessage({ type = "dispatchmessages", data = sentData }) 873 | end) 874 | 875 | RegisterNetEvent('mdt:client:getCallResponses', function(sentData, sentCallId) 876 | SendNUIMessage({ type = "getCallResponses", data = sentData, callid = sentCallId }) 877 | end) 878 | 879 | RegisterNetEvent('mdt:client:sendCallResponse', function(message, time, callid, name) 880 | SendNUIMessage({ type = "sendCallResponse", message = message, time = time, callid = callid, name = name }) 881 | end) 882 | 883 | RegisterNetEvent('mdt:client:notifyMechanics', function(sentData) 884 | --[[if exports["erp-jobsystem"]:CanTow() then 885 | TriggerServerEvent('erp-sounds:PlayWithinDistance', 1.5, 'beep', 0.4) 886 | TriggerEvent('erp_phone:sendNotification', {img = 'vehiclenotif.png', title = "Impound", content = "New vehicle is ready to be impounded!", time = 5000 }) 887 | end]] 888 | end) 889 | 890 | RegisterNetEvent('mdt:client:statusImpound', function(data, plate) 891 | SendNUIMessage({ type = "statusImpound", data = data, plate = plate }) 892 | end) 893 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /ps-mdt/shared/config.lua: -------------------------------------------------------------------------------- 1 | Config = Config or {} 2 | 3 | Config.RosterLink = { 4 | ['police'] = '', 5 | ['ambulance'] = '', 6 | ['bcso'] = '', 7 | ['doj'] = '', 8 | } 9 | 10 | 11 | Config.UseWolfknightRadar = false 12 | --[[ 13 | Adds support for Wraith ARS 2X by WolfKnight98 (wk_wars2x) 14 | https://github.com/WolfKnight98/wk_wars2x 15 | To save on unnecessary database queries, in wk_wars2x/config.lua set 'CONFIG.use_sonorancad = true' 16 | This will only check plates of vehicles that have been occupied by a player 17 | --]] 18 | 19 | Config.OnlyShowOnDuty = true 20 | 21 | Config.Fuel = "lj-fuel" -- "LegacyFuel", "lj-fuel" 22 | 23 | Config.PenalCodeTitles = { 24 | [1] = 'OFFENSES AGAINST PERSONS', 25 | [2] = 'OFFENSES INVOLVING THEFT', 26 | [3] = 'OFFENSES INVOLVING FRAUD', 27 | [4] = 'OFFENSES INVOLVING DAMAGE TO PROPERTY', 28 | [5] = 'OFFENSES AGAINST PUBLIC ADMINISTRATION', 29 | [6] = 'OFFENSES AGAINST PUBLIC ORDER', 30 | [7] = 'OFFENSES AGAINST HEALTH AND MORALS', 31 | [8] = 'OFFENSES AGAINST PUBLIC SAFETY', 32 | [9] = 'OFFENSES INVOLVING THE OPERATION OF A VEHICLE', 33 | [10] = 'OFFENSES INVOLVING THE WELL-BEING OF WILDLIFE', 34 | } 35 | 36 | Config.PenalCode = { 37 | [1] = { 38 | [1] = {title = 'Simple Assault', class = 'Misdemeanor', id = 'P.C. 1001', months = 7, fine = 500, color = 'green'}, 39 | [2] = {title = 'Assault', class = 'Misdemeanor', id = 'P.C. 1002', months = 15, fine = 850, color = 'orange'}, 40 | [3] = {title = 'Aggravated Assault', class = 'Felony', id = 'P.C. 1003', months = 20, fine = 1250, color = 'orange'}, 41 | [4] = {title = 'Assault with a Deadly Weapon', class = 'Felony', id = 'P.C. 1004', months = 30, fine = 3750, color = 'red'}, 42 | [5] = {title = 'Involuntary Manslaughter', class = 'Felony', id = 'P.C. 1005', months = 60, fine = 7500, color = 'red'}, 43 | [6] = {title = 'Vehicular Manslaughter', class = 'Felony', id = 'P.C. 1006', months = 75, fine = 7500, color = 'red'}, 44 | [7] = {title = 'Attempted Murder of a Civilian', class = 'Felony', id = 'P.C. 1007', months = 50, fine = 7500, color = 'red'}, 45 | [8] = {title = 'Second Degree Murder', class = 'Felony', id = 'P.C. 1008', months = 100, fine = 15000, color = 'red'}, 46 | [9] = {title = 'Accessory to Second Degree Murder', class = 'Felony', id = 'P.C. 1009', months = 50, fine = 5000, color = 'red'}, 47 | [10] = {title = 'First Degree Murder', class = 'Felony', id = 'P.C. 1010', months = 0, fine = 0, color = 'red'}, 48 | [11] = {title = 'Accessory to First Degree Murder', class = 'Felony', id = 'P.C. 1011', months = 0, fine = 0, color = 'red'}, 49 | [12] = {title = 'Murder of a Public Servant or Peace Officer', class = 'Felony', id = 'P.C. 1012', months = 0, fine = 0, color = 'red'}, 50 | [13] = {title = 'Attempted Murder of a Public Servant or Peace Officer', class = 'Felony', id = 'P.C. 1013', months = 65, fine = 10000, color = 'red'}, 51 | [14] = {title = 'Accessory to the Murder of a Public Servant or Peace Officer', class = 'Felony', id = 'P.C. 1014', months = 0, fine = 0, color = 'red'}, 52 | [15] = {title = 'Unlawful Imprisonment', class = 'Misdemeanor', id = 'P.C. 1015', months = 10, fine = 600, color = 'green'}, 53 | [16] = {title = 'Kidnapping', class = 'Felony', id = 'P.C. 1016', months = 15, fine = 900, color = 'orange'}, 54 | [17] = {title = 'Accessory to Kidnapping', class = 'Felony', id = 'P.C. 1017', months = 7, fine = 450, color = 'orange'}, 55 | [18] = {title = 'Attempted Kidnapping', class = 'Felony', id = 'P.C. 1018', months = 10, fine = 450, color = 'orange'}, 56 | [19] = {title = 'Hostage Taking', class = 'Felony', id = 'P.C. 1019', months = 20, fine = 1200, color = 'orange'}, 57 | [20] = {title = 'Accessory to Hostage Taking', class = 'Felony', id = 'P.C. 1020', months = 10, fine = 600, color = 'orange'}, 58 | [21] = {title = 'Unlawful Imprisonment of a Public Servant or Peace Officer.', class = 'Felony', id = 'P.C. 1021', months = 25, fine = 4000, color = 'orange'}, 59 | [22] = {title = 'Criminal Threats', class = 'Misdemeanor', id = 'P.C. 1022', months = 5, fine = 500, color = 'orange'}, 60 | [23] = {title = 'Reckless Endangerment', class = 'Misdemeanor', id = 'P.C. 1023', months = 10, fine = 1000, color = 'orange'}, 61 | [24] = {title = 'Gang Related Shooting', class = 'Felony', id = 'P.C. 1024', months = 30, fine = 2500, color = 'red'}, 62 | [25] = {title = 'Cannibalism', class = 'Felony', id = 'P.C. 1025', months = 0, fine = 0, color = 'red'}, 63 | [26] = {title = 'Torture', class = 'Felony', id = 'P.C. 1026', months = 40, fine = 4500, color = 'red'}, 64 | }, 65 | [2] = { 66 | [1] = {title = 'Petty Theft', class = 'Infraction', id = 'P.C. 2001', months = 0, fine = 250, color = 'green'}, 67 | [2] = {title = 'Grand Theft', class = 'Misdemeanor', id = 'P.C. 2002', months = 10, fine = 600, color = 'green'}, 68 | [3] = {title = 'Grand Theft Auto A', class = 'Felony', id = 'P.C. 2003', months = 15, fine = 900, color = 'green'}, 69 | [4] = {title = 'Grand Theft Auto B', class = 'Felony', id = 'P.C. 2004', months = 35, fine = 3500, color = 'green'}, 70 | [5] = {title = 'Carjacking', class = 'Felony', id = 'P.C. 2005', months = 30, fine = 2000, color = 'orange'}, 71 | [6] = {title = 'Burglary', class = 'Misdemeanor', id = 'P.C. 2006', months = 10, fine = 500, color = 'green'}, 72 | [7] = {title = 'Robbery', class = 'Felony', id = 'P.C. 2007', months = 25, fine = 2000, color = 'green'}, 73 | [8] = {title = 'Accessory to Robbery', class = 'Felony', id = 'P.C. 2008', months = 12, fine = 1000, color = 'green'}, 74 | [9] = {title = 'Attempted Robbery', class = 'Felony', id = 'P.C. 2009', months = 20, fine = 1000, color = 'green'}, 75 | [10] = {title = 'Armed Robbery', class = 'Felony', id = 'P.C. 2010', months = 30, fine = 3000, color = 'orange'}, 76 | [11] = {title = 'Accessory to Armed Robbery', class = 'Felony', id = 'P.C. 2011', months = 15, fine = 1500, color = 'orange'}, 77 | [12] = {title = 'Attempted Armed Robbery', class = 'Felony', id = 'P.C. 2012', months = 25, fine = 1500, color = 'orange'}, 78 | [13] = {title = 'Grand Larceny', class = 'Felony', id = 'P.C. 2013', months = 45, fine = 7500, color = 'orange'}, 79 | [14] = {title = 'Leaving Without Paying', class = 'Infraction', id = 'P.C. 2014', months = 0, fine = 500, color = 'green'}, 80 | [15] = {title = 'Possession of Nonlegal Currency', class = 'Misdemeanor', id = 'P.C. 2015', months = 10, fine = 750, color = 'green'}, 81 | [16] = {title = 'Possession of Government-Issued Items', class = 'Misdemeanor', id = 'P.C. 2016', months = 15, fine = 1000, color = 'green'}, 82 | [17] = {title = 'Possession of Items Used in the Commission of a Crime', class = 'Misdemeanor', id = 'P.C. 2017', months = 10, fine = 500, color = 'green'}, 83 | [18] = {title = 'Sale of Items Used in the Commission of a Crime', class = 'Felony', id = 'P.C. 2018', months = 15, fine = 1000, color = 'orange'}, 84 | [19] = {title = 'Theft of an Aircraft', class = 'Felony', id = 'P.C. 2019', months = 20, fine = 1000, color = 'green'}, 85 | }, 86 | [3] = { 87 | [1] = {title = 'Impersonating', class = 'Misdemeanor', id = 'P.C. 3001', months = 15, fine = 1250, color = 'green'}, 88 | [2] = {title = 'Impersonating a Peace Officer or Public Servant', class = 'Felony', id = 'P.C. 3002', months = 25, fine = 2750, color = 'green'}, 89 | [3] = {title = 'Impersonating a Judge', class = 'Felony', id = 'P.C. 3003', months = 0, fine = 0, color = 'green'}, 90 | [4] = {title = 'Possession of Stolen Identification', class = 'Misdemeanor', id = 'P.C. 3004', months = 10, fine = 750, color = 'green'}, 91 | [5] = {title = 'Possession of Stolen Government Identification', class = 'Misdemeanor', id = 'P.C. 3005', months = 20, fine = 2000, color = 'green'}, 92 | [6] = {title = 'Extortion', class = 'Felony', id = 'P.C. 3006', months = 20, fine = 900, color = 'orange'}, 93 | [7] = {title = 'Fraud', class = 'Misdemeanor', id = 'P.C. 3007', months = 10, fine = 450, color = 'green'}, 94 | [8] = {title = 'Forgery', class = 'Misdemeanor', id = 'P.C. 3008', months = 15, fine = 750, color = 'green'}, 95 | [9] = {title = 'Money Laundering', class = 'Felony', id = 'P.C. 3009', months = 0, fine = 0, color = 'red'}, 96 | }, 97 | [4] = { 98 | [1] = {title = 'Trespassing', class = 'Misdemeanor', id = 'P.C. 4001', months = 10, fine = 450, color = 'green'}, 99 | [2] = {title = 'Felony Trespassing', class = 'Felony', id = 'P.C. 4002', months = 15, fine = 1500, color = 'green'}, 100 | [3] = {title = 'Arson', class = 'Felony', id = 'P.C. 4003', months = 15, fine = 1500, color = 'orange'}, 101 | [4] = {title = 'Vandalism', class = 'Infraction', id = 'P.C. 4004', months = 0, fine = 300, color = 'green'}, 102 | [5] = {title = 'Vandalism of Government Property', class = 'Felony', id = 'P.C. 4005', months = 20, fine = 1500, color = 'green'}, 103 | [6] = {title = 'Littering', class = 'Infraction', id = 'P.C. 4006', months = 0, fine = 200, color = 'green'}, 104 | }, 105 | [5] = { 106 | [1] = {title = 'Bribery of a Government Official', class = 'Felony', id = 'P.C. 5001', months = 20, fine = 3500, color = 'green'}, 107 | [2] = {title = 'Anti-Mask Law', class = 'Infraction', id = 'P.C. 5002', months = 0, fine = 750, color = 'green'}, 108 | [3] = {title = 'Possession of Contraband in a Government Facility', class = 'Felony', id = 'P.C. 5003', months = 25, fine = 1000, color = 'green'}, 109 | [4] = {title = 'Criminal Possession of Stolen Property', class = 'Misdemeanor', id = 'P.C. 5004', months = 10, fine = 500, color = 'green'}, 110 | [5] = {title = 'Escaping', class = 'Felony', id = 'P.C. 5005', months = 10, fine = 450, color = 'green'}, 111 | [6] = {title = 'Jailbreak', class = 'Felony', id = 'P.C. 5006', months = 30, fine = 2500, color = 'orange'}, 112 | [7] = {title = 'Accessory to Jailbreak', class = 'Felony', id = 'P.C. 5007', months = 25, fine = 2000, color = 'orange'}, 113 | [8] = {title = 'Attempted Jailbreak', class = 'Felony', id = 'P.C. 5008', months = 20, fine = 1500, color = 'orange'}, 114 | [9] = {title = 'Perjury', class = 'Felony', id = 'P.C. 5009', months = 0, fine = 0, color = 'green'}, 115 | [10] = {title = 'Violation of a Restraining Order', class = 'Felony', id = 'P.C. 5010', months = 20, fine = 2250, color = 'green'}, 116 | [11] = {title = 'Embezzlement', class = 'Felony', id = 'P.C. 5011', months = 45, fine = 10000, color = 'green'}, 117 | [12] = {title = 'Unlawful Practice', class = 'Felony', id = 'P.C. 5012', months = 15, fine = 1500, color = 'orange'}, 118 | [13] = {title = 'Misuse of Emergency Systems', class = 'Infraction', id = 'P.C. 5013', months = 0, fine = 600, color = 'orange'}, 119 | [14] = {title = 'Conspiracy', class = 'Misdemeanor', id = 'P.C. 5014', months = 10, fine = 450, color = 'green'}, 120 | [15] = {title = 'Violating a Court Order', class = 'Misdemeanor', id = 'P.C. 5015', months = 0, fine = 0, color = 'orange'}, 121 | [16] = {title = 'Failure to Appear', class = 'Misdemeanor', id = 'P.C. 5016', months = 0, fine = 0, color = 'orange'}, 122 | [17] = {title = 'Contempt of Court', class = 'Felony', id = 'P.C. 5017', months = 0, fine = 0, color = 'orange'}, 123 | [18] = {title = 'Resisting Arrest', class = 'Misdemeanor', id = 'P.C. 5018', months = 5, fine = 300, color = 'orange'}, 124 | }, 125 | [6] = { 126 | [1] = {title = 'Disobeying a Peace Officer', class = 'infraction', id = 'P.C. 6001', months = 0, fine = 750, color = 'green'}, 127 | [2] = {title = 'Disorderly Conduct', class = 'Infraction', id = 'P.C. 6002', months = 0, fine = 250, color = 'green'}, 128 | [3] = {title = 'Disturbing the Peace', class = 'infraction', id = 'P.C. 6003', months = 0, fine = 350, color = 'green'}, 129 | [4] = {title = 'False Reporting', class = 'Misdemeanor', id = 'P.C. 6004', months = 10, fine = 750, color = 'green'}, 130 | [5] = {title = 'Harassment', class = 'Misdemeanor', id = 'P.C. 6005', months = 10, fine = 500, color = 'orange'}, 131 | [6] = {title = 'Misdemeanor Obstruction of Justice', class = 'Misdemeanor', id = 'P.C. 6006', months = 10, fine = 500, color = 'green'}, 132 | [7] = {title = 'Felony Obstruction of Justice', class = 'Felony', id = 'P.C. 6007', months = 15, fine = 900, color = 'green'}, 133 | [8] = {title = 'Inciting a Riot', class = 'Felony', id = 'P.C. 6008', months = 25, fine = 1000, color = 'orange'}, 134 | [9] = {title = 'Loitering on Government Properties', class = 'Infraction', id = 'P.C. 6009', months = 0, fine = 500, color = 'green'}, 135 | [10] = {title = 'Tampering', class = 'Misdemeanor', id = 'P.C. 6010', months = 10, fine = 500, color = 'green'}, 136 | [11] = {title = 'Vehicle Tampering', class = 'Misdemeanor', id = 'P.C. 6011', months = 15, fine = 750, color = 'green'}, 137 | [12] = {title = 'Evidence Tampering', class = 'Felony', id = 'P.C. 6012', months = 20, fine = 1000, color = 'green'}, 138 | [13] = {title = 'Witness Tampering', class = 'Felony', id = 'P.C. 6013', months = 0, fine = 0, color = 'green'}, 139 | [14] = {title = 'Failure to Provide Identification', class = 'Misdemeanor', id = 'P.C. 6014', months = 15, fine = 1500, color = 'green'}, 140 | [15] = {title = 'Vigilantism', class = 'Felony', id = 'P.C. 6015', months = 30, fine = 1500, color = 'orange'}, 141 | [16] = {title = 'Unlawful Assembly', class = 'Misdemeanor', id = 'P.C. 6016', months = 10, fine = 750, color = 'orange'}, 142 | [17] = {title = 'Government Corruption', class = 'Felony', id = 'P.C. 6017', months = 0, fine = 0, color = 'red'}, 143 | [18] = {title = 'Stalking', class = 'Felony', id = 'P.C. 6018', months = 40, fine = 1500, color = 'orange'}, 144 | [19] = {title = 'Aiding and Abetting', class = 'Misdemeanor', id = 'P.C. 6019', months = 15, fine = 450, color = 'orange'}, 145 | [20] = {title = 'Harboring a Fugitive', class = 'Misdemeanor', id = 'P.C. 6020', months = 10, fine = 1000, color = 'green'}, 146 | }, 147 | [7] = { 148 | [1] = {title = 'Misdemeanor Possession of Marijuana', class = 'Mask', id = 'P.C. 7001', months = 5, fine = 250, color = 'green'}, 149 | [2] = {title = 'Felony Possession of Marijuana', class = 'Felony', id = 'P.C. 7002', months = 15, fine = 1000, color = 'green'}, 150 | [3] = {title = 'Cultivation of Marijuana A', class = 'Misdemeanor', id = 'P.C. 7003', months = 10, fine = 750, color = 'green'}, 151 | [4] = {title = 'Cultivation of Marijuana B', class = 'Felony', id = 'P.C. 7004', months = 30, fine = 1500, color = 'orange'}, 152 | [5] = {title = 'Possession of Marijuana with Intent to Distribute', class = 'Felony', id = 'P.C. 7005', months = 30, fine = 3000, color = 'orange'}, 153 | [6] = {title = 'Misdemeanor Possession of Cocaine', class = 'Misdemeanor', id = 'P.C. 7006', months = 7, fine = 500, color = 'green'}, 154 | [7] = {title = 'Felony Possession of Cocaine', class = 'Felony', id = 'P.C. 7007', months = 25, fine = 1500, color = 'green'}, 155 | [8] = {title = 'Possession of Cocaine with Intent to Distribute', class = 'Felony', id = 'P.C. 7008', months = 35, fine = 4500, color = 'orange'}, 156 | [9] = {title = 'Misdemeanor Possession of Methamphetamine', class = 'Misdemeanor', id = 'P.C. 7009', months = 7, fine = 500, color = 'green'}, 157 | [10] = {title = 'Felony Possession of Methamphetamine', class = 'Felony', id = 'P.C. 7010', months = 25, fine = 1500, color = 'green'}, 158 | [11] = {title = 'Possession of Methamphetamine with Intent to Distribute', class = 'Felony', id = 'P.C. 7011', months = 35, fine = 4500, color = 'orange'}, 159 | [12] = {title = 'Misdemeanor Possession of Oxy / Vicodin', class = 'Felony', id = 'P.C. 7012', months = 7, fine = 500, color = 'green'}, 160 | [13] = {title = 'Felony Possession of Oxy / Vicodin', class = 'Felony', id = 'P.C. 7013', months = 25, fine = 1500, color = 'green'}, 161 | [14] = {title = 'Felony Possession of Oxy / Vicodin with Intent to Distribute', class = 'Felony', id = 'P.C. 7014', months = 35, fine = 4500, color = 'orange'}, 162 | [15] = {title = 'Misdemeanor Possession of Ecstasy', class = 'Misdemeanor', id = 'P.C. 7015', months = 7, fine = 500, color = 'green'}, 163 | [16] = {title = 'Felony Possession of Ecstasy', class = 'Felony', id = 'P.C. 7016', months = 25, fine = 1500, color = 'green'}, 164 | [17] = {title = 'Possession of Ecstasy with Intent to Distribute', class = 'Felony', id = 'P.C. 7017', months = 35, fine = 4500, color = 'orange'}, 165 | [18] = {title = 'Misdemeanor Possession of Opium', class = 'Misdemeanor', id = 'P.C. 7018', months = 7, fine = 500, color = 'green'}, 166 | [19] = {title = 'Felony Possession of Opium', class = 'Felony', id = 'P.C. 7019', months = 25, fine = 1500, color = 'green'}, 167 | [20] = {title = 'Possession of Opium with Intent to Distribute', class = 'Felony', id = 'P.C. 7020', months = 35, fine = 4500, color = 'orange'}, 168 | [21] = {title = 'Misdemeanor Possession of Adderall', class = 'Misdemeanor', id = 'P.C. 7021', months = 7, fine = 500, color = 'green'}, 169 | [22] = {title = 'Felony Possession of Adderall', class = 'Felony', id = 'P.C. 7022', months = 25, fine = 1500, color = 'green'}, 170 | [23] = {title = 'Possession of Adderall with Intent to Distribute', class = 'Felony', id = 'P.C. 7023', months = 35, fine = 4500, color = 'orange'}, 171 | [24] = {title = 'Misdemeanor Possession of Xanax', class = 'Misdemeanor', id = 'P.C. 7024', months = 7, fine = 500, color = 'green'}, 172 | [25] = {title = 'Felony Possession of Xanax', class = 'Felony', id = 'P.C. 7025', months = 25, fine = 1500, color = 'green'}, 173 | [26] = {title = 'Possession of Xanax with Intent to Distribute', class = 'Felony', id = 'P.C. 7026', months = 35, fine = 4500, color = 'orange'}, 174 | [27] = {title = 'Misdemeanor Possession of Shrooms', class = 'Misdemeanor', id = 'P.C. 7027', months = 7, fine = 500, color = 'green'}, 175 | [28] = {title = 'Felony Possession of Shrooms', class = 'Felony', id = 'P.C. 7028', months = 25, fine = 1500, color = 'green'}, 176 | [29] = {title = 'Possession of Shrooms with Intent to Distribute', class = 'Felony', id = 'P.C. 7029', months = 35, fine = 4500, color = 'orange'}, 177 | [30] = {title = 'Misdemeanor Possession of Lean', class = 'Misdemeanor', id = 'P.C. 7030', months = 7, fine = 500, color = 'green'}, 178 | [31] = {title = 'Felony Possession of Lean', class = 'Felony', id = 'P.C. 7031', months = 25, fine = 1500, color = 'green'}, 179 | [32] = {title = 'Possession of Lean with Intent to Distribute', class = 'Felony', id = 'P.C. 7032', months = 35, fine = 4500, color = 'orange'}, 180 | [33] = {title = 'Sale of a controlled substance', class = 'Misdemeanor', id = 'P.C. 7033', months = 10, fine = 1000, color = 'green'}, 181 | [34] = {title = 'Drug Trafficking', class = 'Felony', id = 'P.C. 7034', months = 0, fine = 0, color = 'red'}, 182 | [35] = {title = 'Desecration of a Human Corpse', class = 'Felony', id = 'P.C. 7035', months = 20, fine = 1500, color = 'orange'}, 183 | [36] = {title = 'Public Intoxication', class = 'Infraction', id = 'P.C. 7036', months = 0, fine = 500, color = 'green'}, 184 | [37] = {title = 'Public Indecency', class = 'Misdemeanor', id = 'P.C. 7037', months = 10, fine = 750, color = 'green'}, 185 | }, 186 | [8] = { 187 | [1] = {title = 'Criminal Possession of Weapon Class A', class = 'Felony', id = 'P.C. 8001', months = 10, fine = 500, color = 'green'}, 188 | [2] = {title = 'Criminal Possession of Weapon Class B', class = 'Felony', id = 'P.C. 8002', months = 15, fine = 1000, color = 'green'}, 189 | [3] = {title = 'Criminal Possession of Weapon Class C', class = 'Felony', id = 'P.C. 8003', months = 30, fine = 3500, color = 'green'}, 190 | [4] = {title = 'Criminal Possession of Weapon Class D', class = 'Felony', id = 'P.C. 8004', months = 25, fine = 1500, color = 'green'}, 191 | [5] = {title = 'Criminal Sale of Weapon Class A', class = 'Felony', id = 'P.C. 8005', months = 15, fine = 1000, color = 'orange'}, 192 | [6] = {title = 'Criminal Sale of Weapon Class B', class = 'Felony', id = 'P.C. 8006', months = 20, fine = 2000, color = 'orange'}, 193 | [7] = {title = 'Criminal Sale of Weapon Class C', class = 'Felony', id = 'P.C. 8007', months = 35, fine = 7000, color = 'orange'}, 194 | [8] = {title = 'Criminal Sale of Weapon Class D', class = 'Felony', id = 'P.C. 8008', months = 30, fine = 3000, color = 'orange'}, 195 | [9] = {title = 'Criminal Use of Weapon', class = 'Misdemeanor', id = 'P.C. 8009', months = 10, fine = 450, color = 'orange'}, 196 | [10] = {title = 'Possession of Illegal Firearm Modifications', class = 'Misdemeanor', id = 'P.C. 8010', months = 10, fine = 300, color = 'green'}, 197 | [11] = {title = 'Weapon Trafficking', class = 'Felony', id = 'P.C. 8011', months = 0, fine = 0, color = 'red'}, 198 | [12] = {title = 'Brandishing a Weapon', class = 'Misdemeanor', id = 'P.C. 8012', months = 15, fine = 500, color = 'orange'}, 199 | [13] = {title = 'Insurrection', class = 'Felony', id = 'P.C. 8013', months = 0, fine = 0, color = 'red'}, 200 | [14] = {title = 'Flying into Restricted Airspace', class = 'Felony', id = 'P.C. 8014', months = 20, fine = 1500, color = 'green'}, 201 | [15] = {title = 'Jaywalking', class = 'Infraction', id = 'P.C. 8015', months = 0, fine = 150, color = 'green'}, 202 | [16] = {title = 'Criminal Use of Explosives', class = 'Felony', id = 'P.C. 8016', months = 30, fine = 2500, color = 'orange'}, 203 | }, 204 | [9] = { 205 | [1] = {title = 'Driving While Intoxicated', class = 'Misdemeanor', id = 'P.C. 9001', months = 5, fine = 300, color = 'green'}, 206 | [2] = {title = 'Evading', class = 'Misdemeanor', id = 'P.C. 9002', months = 5, fine = 400, color = 'green'}, 207 | [3] = {title = 'Reckless Evading', class = 'Felony', id = 'P.C. 9003', months = 10, fine = 800, color = 'orange'}, 208 | [4] = {title = 'Failure to Yield to Emergency Vehicle', class = 'Infraction', id = 'P.C. 9004', months = 0, fine = 600, color = 'green'}, 209 | [5] = {title = 'Failure to Obey Traffic Control Device', class = 'Infraction', id = 'P.C. 9005', months = 0, fine = 150, color = 'green'}, 210 | [6] = {title = 'Nonfunctional Vehicle', class = 'Infraction', id = 'P.C. 9006', months = 0, fine = 75, color = 'green'}, 211 | [7] = {title = 'Negligent Driving', class = 'Infraction', id = 'P.C. 9007', months = 0, fine = 300, color = 'green'}, 212 | [8] = {title = 'Reckless Driving', class = 'Misdemeanor', id = 'P.C. 9008', months = 10, fine = 750, color = 'orange'}, 213 | [9] = {title = 'Third Degree Speeding', class = 'Infraction', id = 'P.C. 9009', months = 0, fine = 225, color = 'green'}, 214 | [10] = {title = 'Second Degree Speeding', class = 'Infraction', id = 'P.C. 9010', months = 0, fine = 450, color = 'green'}, 215 | [11] = {title = 'First Degree Speeding', class = 'Infraction', id = 'P.C. 9011', months = 0, fine = 750, color = 'green'}, 216 | [12] = {title = 'Unlicensed Operation of Vehicle', class = 'Infraction', id = 'P.C. 9012', months = 0, fine = 500, color = 'green'}, 217 | [13] = {title = 'Illegal U-Turn', class = 'Infraction', id = 'P.C. 9013', months = 0, fine = 75, color = 'green'}, 218 | [14] = {title = 'Illegal Passing', class = 'Infraction', id = 'P.C. 9014', months = 0, fine = 300, color = 'green'}, 219 | [15] = {title = 'Failure to Maintain Lane', class = 'Infraction', id = 'P.C. 9015', months = 0, fine = 300, color = 'green'}, 220 | [16] = {title = 'Illegal Turn', class = 'Infraction', id = 'P.C. 9016', months = 0, fine = 150, color = 'green'}, 221 | [17] = {title = 'Failure to Stop', class = 'Infraction', id = 'P.C. 9017', months = 0, fine = 600, color = 'green'}, 222 | [18] = {title = 'Unauthorized Parking', class = 'Infraction', id = 'P.C. 9018', months = 0, fine = 300, color = 'green'}, 223 | [19] = {title = 'Hit and Run', class = 'Misdemeanor', id = 'P.C. 9019', months = 10, fine = 500, color = 'green'}, 224 | [20] = {title = 'Driving without Headlights or Signals', class = 'Infraction', id = 'P.C. 9020', months = 0, fine = 300, color = 'green'}, 225 | [21] = {title = 'Street Racing', class = 'Felony', id = 'P.C. 9021', months = 15, fine = 1500, color = 'green'}, 226 | [22] = {title = 'Piloting without Proper Licensing', class = 'Felony', id = 'P.C. 9022', months = 20, fine = 1500, color = 'orange'}, 227 | [23] = {title = 'Unlawful Use of a Motorvehicle', class = 'Misdemeanor', id = 'P.C. 9023', months = 10, fine = 750, color = 'green'}, 228 | }, 229 | [10] = { 230 | [1] = {title = 'Hunting in Restricted Areas', class = 'Infraction', id = 'P.C. 10001', months = 0, fine = 450, color = 'green'}, 231 | [2] = {title = 'Unlicensed Hunting', class = 'Infraction', id = 'P.C. 10002', months = 0, fine = 450, color = 'green'}, 232 | [3] = {title = 'Animal Cruelty', class = 'Misdemeanor', id = 'P.C. 10003', months = 10, fine = 450, color = 'green'}, 233 | [4] = {title = 'Hunting with a Non-Hunting Weapon', class = 'Misdemeanor', id = 'P.C. 10004', months = 10, fine = 750, color = 'green'}, 234 | [5] = {title = 'Hunting outside of hunting hours', class = 'Infraction', id = 'P.C. 10005', months = 0, fine = 750, color = 'green'}, 235 | [6] = {title = 'Overhunting', class = 'Misdemeanor', id = 'P.C. 10006', months = 10, fine = 1000, color = 'green'}, 236 | [7] = {title = 'Poaching', class = 'Felony', id = 'P.C. 10007', months = 20, fine = 1250, color = 'red'}, 237 | } 238 | } 239 | 240 | Config.PoliceJobs = { 241 | ['police'] = true, 242 | ['lspd'] = true, 243 | ['bcso'] = true, 244 | ['sast'] = true, 245 | ['sasp'] = true, 246 | ['doc'] = true, 247 | ['sapr'] = true, 248 | ['pa'] = true -- yucky 249 | } 250 | 251 | Config.AmbulanceJobs = { 252 | ['ambulance'] = true, 253 | ['doctor'] = true 254 | } 255 | 256 | Config.DojJobs = { 257 | ['lawyer'] = true, 258 | } 259 | 260 | -- Leave my hacky code alone ya goblins 261 | Config.AllowedJobs = {} 262 | for index, value in pairs(Config.PoliceJobs) do 263 | Config.AllowedJobs[index] = value 264 | end 265 | for index, value in pairs(Config.AmbulanceJobs) do 266 | Config.AllowedJobs[index] = value 267 | end 268 | for index, value in pairs(Config.DojJobs) do 269 | Config.AllowedJobs[index] = value 270 | end 271 | -- Leave my hacky code alone ya goblins 272 | 273 | Config.LogPerms = { 274 | ['ambulance'] = { 275 | [4] = true, 276 | }, 277 | ['police'] = { 278 | [4] = true, 279 | }, 280 | } 281 | 282 | Config.ColorNames = { 283 | [0] = "Metallic Black", 284 | [1] = "Metallic Graphite Black", 285 | [2] = "Metallic Black Steel", 286 | [3] = "Metallic Dark Silver", 287 | [4] = "Metallic Silver", 288 | [5] = "Metallic Blue Silver", 289 | [6] = "Metallic Steel Gray", 290 | [7] = "Metallic Shadow Silver", 291 | [8] = "Metallic Stone Silver", 292 | [9] = "Metallic Midnight Silver", 293 | [10] = "Metallic Gun Metal", 294 | [11] = "Metallic Anthracite Grey", 295 | [12] = "Matte Black", 296 | [13] = "Matte Gray", 297 | [14] = "Matte Light Grey", 298 | [15] = "Util Black", 299 | [16] = "Util Black Poly", 300 | [17] = "Util Dark silver", 301 | [18] = "Util Silver", 302 | [19] = "Util Gun Metal", 303 | [20] = "Util Shadow Silver", 304 | [21] = "Worn Black", 305 | [22] = "Worn Graphite", 306 | [23] = "Worn Silver Grey", 307 | [24] = "Worn Silver", 308 | [25] = "Worn Blue Silver", 309 | [26] = "Worn Shadow Silver", 310 | [27] = "Metallic Red", 311 | [28] = "Metallic Torino Red", 312 | [29] = "Metallic Formula Red", 313 | [30] = "Metallic Blaze Red", 314 | [31] = "Metallic Graceful Red", 315 | [32] = "Metallic Garnet Red", 316 | [33] = "Metallic Desert Red", 317 | [34] = "Metallic Cabernet Red", 318 | [35] = "Metallic Candy Red", 319 | [36] = "Metallic Sunrise Orange", 320 | [37] = "Metallic Classic Gold", 321 | [38] = "Metallic Orange", 322 | [39] = "Matte Red", 323 | [40] = "Matte Dark Red", 324 | [41] = "Matte Orange", 325 | [42] = "Matte Yellow", 326 | [43] = "Util Red", 327 | [44] = "Util Bright Red", 328 | [45] = "Util Garnet Red", 329 | [46] = "Worn Red", 330 | [47] = "Worn Golden Red", 331 | [48] = "Worn Dark Red", 332 | [49] = "Metallic Dark Green", 333 | [50] = "Metallic Racing Green", 334 | [51] = "Metallic Sea Green", 335 | [52] = "Metallic Olive Green", 336 | [53] = "Metallic Green", 337 | [54] = "Metallic Gasoline Blue Green", 338 | [55] = "Matte Lime Green", 339 | [56] = "Util Dark Green", 340 | [57] = "Util Green", 341 | [58] = "Worn Dark Green", 342 | [59] = "Worn Green", 343 | [60] = "Worn Sea Wash", 344 | [61] = "Metallic Midnight Blue", 345 | [62] = "Metallic Dark Blue", 346 | [63] = "Metallic Saxony Blue", 347 | [64] = "Metallic Blue", 348 | [65] = "Metallic Mariner Blue", 349 | [66] = "Metallic Harbor Blue", 350 | [67] = "Metallic Diamond Blue", 351 | [68] = "Metallic Surf Blue", 352 | [69] = "Metallic Nautical Blue", 353 | [70] = "Metallic Bright Blue", 354 | [71] = "Metallic Purple Blue", 355 | [72] = "Metallic Spinnaker Blue", 356 | [73] = "Metallic Ultra Blue", 357 | [74] = "Metallic Bright Blue", 358 | [75] = "Util Dark Blue", 359 | [76] = "Util Midnight Blue", 360 | [77] = "Util Blue", 361 | [78] = "Util Sea Foam Blue", 362 | [79] = "Uil Lightning blue", 363 | [80] = "Util Maui Blue Poly", 364 | [81] = "Util Bright Blue", 365 | [82] = "Matte Dark Blue", 366 | [83] = "Matte Blue", 367 | [84] = "Matte Midnight Blue", 368 | [85] = "Worn Dark blue", 369 | [86] = "Worn Blue", 370 | [87] = "Worn Light blue", 371 | [88] = "Metallic Taxi Yellow", 372 | [89] = "Metallic Race Yellow", 373 | [90] = "Metallic Bronze", 374 | [91] = "Metallic Yellow Bird", 375 | [92] = "Metallic Lime", 376 | [93] = "Metallic Champagne", 377 | [94] = "Metallic Pueblo Beige", 378 | [95] = "Metallic Dark Ivory", 379 | [96] = "Metallic Choco Brown", 380 | [97] = "Metallic Golden Brown", 381 | [98] = "Metallic Light Brown", 382 | [99] = "Metallic Straw Beige", 383 | [100] = "Metallic Moss Brown", 384 | [101] = "Metallic Biston Brown", 385 | [102] = "Metallic Beechwood", 386 | [103] = "Metallic Dark Beechwood", 387 | [104] = "Metallic Choco Orange", 388 | [105] = "Metallic Beach Sand", 389 | [106] = "Metallic Sun Bleeched Sand", 390 | [107] = "Metallic Cream", 391 | [108] = "Util Brown", 392 | [109] = "Util Medium Brown", 393 | [110] = "Util Light Brown", 394 | [111] = "Metallic White", 395 | [112] = "Metallic Frost White", 396 | [113] = "Worn Honey Beige", 397 | [114] = "Worn Brown", 398 | [115] = "Worn Dark Brown", 399 | [116] = "Worn straw beige", 400 | [117] = "Brushed Steel", 401 | [118] = "Brushed Black steel", 402 | [119] = "Brushed Aluminium", 403 | [120] = "Chrome", 404 | [121] = "Worn Off White", 405 | [122] = "Util Off White", 406 | [123] = "Worn Orange", 407 | [124] = "Worn Light Orange", 408 | [125] = "Metallic Securicor Green", 409 | [126] = "Worn Taxi Yellow", 410 | [127] = "police car blue", 411 | [128] = "Matte Green", 412 | [129] = "Matte Brown", 413 | [130] = "Worn Orange", 414 | [131] = "Matte White", 415 | [132] = "Worn White", 416 | [133] = "Worn Olive Army Green", 417 | [134] = "Pure White", 418 | [135] = "Hot Pink", 419 | [136] = "Salmon pink", 420 | [137] = "Metallic Vermillion Pink", 421 | [138] = "Orange", 422 | [139] = "Green", 423 | [140] = "Blue", 424 | [141] = "Mettalic Black Blue", 425 | [142] = "Metallic Black Purple", 426 | [143] = "Metallic Black Red", 427 | [144] = "Hunter Green", 428 | [145] = "Metallic Purple", 429 | [146] = "Metaillic V Dark Blue", 430 | [147] = "MODSHOP BLACK1", 431 | [148] = "Matte Purple", 432 | [149] = "Matte Dark Purple", 433 | [150] = "Metallic Lava Red", 434 | [151] = "Matte Forest Green", 435 | [152] = "Matte Olive Drab", 436 | [153] = "Matte Desert Brown", 437 | [154] = "Matte Desert Tan", 438 | [155] = "Matte Foilage Green", 439 | [156] = "DEFAULT ALLOY COLOR", 440 | [157] = "Epsilon Blue", 441 | [158] = "Unknown", 442 | } 443 | 444 | Config.ColorInformation = { 445 | [0] = "black", 446 | [1] = "black", 447 | [2] = "black", 448 | [3] = "darksilver", 449 | [4] = "silver", 450 | [5] = "bluesilver", 451 | [6] = "silver", 452 | [7] = "darksilver", 453 | [8] = "silver", 454 | [9] = "bluesilver", 455 | [10] = "darksilver", 456 | [11] = "darksilver", 457 | [12] = "matteblack", 458 | [13] = "gray", 459 | [14] = "lightgray", 460 | [15] = "black", 461 | [16] = "black", 462 | [17] = "darksilver", 463 | [18] = "silver", 464 | [19] = "utilgunmetal", 465 | [20] = "silver", 466 | [21] = "black", 467 | [22] = "black", 468 | [23] = "darksilver", 469 | [24] = "silver", 470 | [25] = "bluesilver", 471 | [26] = "darksilver", 472 | [27] = "red", 473 | [28] = "torinored", 474 | [29] = "formulared", 475 | [30] = "blazered", 476 | [31] = "gracefulred", 477 | [32] = "garnetred", 478 | [33] = "desertred", 479 | [34] = "cabernetred", 480 | [35] = "candyred", 481 | [36] = "orange", 482 | [37] = "gold", 483 | [38] = "orange", 484 | [39] = "red", 485 | [40] = "mattedarkred", 486 | [41] = "orange", 487 | [42] = "matteyellow", 488 | [43] = "red", 489 | [44] = "brightred", 490 | [45] = "garnetred", 491 | [46] = "red", 492 | [47] = "red", 493 | [48] = "darkred", 494 | [49] = "darkgreen", 495 | [50] = "racingreen", 496 | [51] = "seagreen", 497 | [52] = "olivegreen", 498 | [53] = "green", 499 | [54] = "gasolinebluegreen", 500 | [55] = "mattelimegreen", 501 | [56] = "darkgreen", 502 | [57] = "green", 503 | [58] = "darkgreen", 504 | [59] = "green", 505 | [60] = "seawash", 506 | [61] = "midnightblue", 507 | [62] = "darkblue", 508 | [63] = "saxonyblue", 509 | [64] = "blue", 510 | [65] = "blue", 511 | [66] = "blue", 512 | [67] = "diamondblue", 513 | [68] = "blue", 514 | [69] = "blue", 515 | [70] = "brightblue", 516 | [71] = "purpleblue", 517 | [72] = "blue", 518 | [73] = "ultrablue", 519 | [74] = "brightblue", 520 | [75] = "darkblue", 521 | [76] = "midnightblue", 522 | [77] = "blue", 523 | [78] = "blue", 524 | [79] = "lightningblue", 525 | [80] = "blue", 526 | [81] = "brightblue", 527 | [82] = "mattedarkblue", 528 | [83] = "matteblue", 529 | [84] = "matteblue", 530 | [85] = "darkblue", 531 | [86] = "blue", 532 | [87] = "lightningblue", 533 | [88] = "yellow", 534 | [89] = "yellow", 535 | [90] = "bronze", 536 | [91] = "yellow", 537 | [92] = "lime", 538 | [93] = "champagne", 539 | [94] = "beige", 540 | [95] = "darkivory", 541 | [96] = "brown", 542 | [97] = "brown", 543 | [98] = "lightbrown", 544 | [99] = "beige", 545 | [100] = "brown", 546 | [101] = "brown", 547 | [102] = "beechwood", 548 | [103] = "beechwood", 549 | [104] = "chocoorange", 550 | [105] = "yellow", 551 | [106] = "yellow", 552 | [107] = "cream", 553 | [108] = "brown", 554 | [109] = "brown", 555 | [110] = "brown", 556 | [111] = "white", 557 | [112] = "white", 558 | [113] = "beige", 559 | [114] = "brown", 560 | [115] = "brown", 561 | [116] = "beige", 562 | [117] = "steel", 563 | [118] = "blacksteel", 564 | [119] = "aluminium", 565 | [120] = "chrome", 566 | [121] = "wornwhite", 567 | [122] = "offwhite", 568 | [123] = "orange", 569 | [124] = "lightorange", 570 | [125] = "green", 571 | [126] = "yellow", 572 | [127] = "blue", 573 | [128] = "green", 574 | [129] = "brown", 575 | [130] = "orange", 576 | [131] = "white", 577 | [132] = "white", 578 | [133] = "darkgreen", 579 | [134] = "white", 580 | [135] = "pink", 581 | [136] = "pink", 582 | [137] = "pink", 583 | [138] = "orange", 584 | [139] = "green", 585 | [140] = "blue", 586 | [141] = "blackblue", 587 | [142] = "blackpurple", 588 | [143] = "blackred", 589 | [144] = "darkgreen", 590 | [145] = "purple", 591 | [146] = "darkblue", 592 | [147] = "black", 593 | [148] = "purple", 594 | [149] = "darkpurple", 595 | [150] = "red", 596 | [151] = "darkgreen", 597 | [152] = "olivedrab", 598 | [153] = "brown", 599 | [154] = "tan", 600 | [155] = "green", 601 | [156] = "silver", 602 | [157] = "blue", 603 | [158] = "black", 604 | } 605 | 606 | Config.ClassList = { 607 | [0] = "Compact", 608 | [1] = "Sedan", 609 | [2] = "SUV", 610 | [3] = "Coupe", 611 | [4] = "Muscle", 612 | [5] = "Sport Classic", 613 | [6] = "Sport", 614 | [7] = "Super", 615 | [8] = "Motorbike", 616 | [9] = "Off-Road", 617 | [10] = "Industrial", 618 | [11] = "Utility", 619 | [12] = "Van", 620 | [13] = "Bike", 621 | [14] = "Boat", 622 | [15] = "Helicopter", 623 | [16] = "Plane", 624 | [17] = "Service", 625 | [18] = "Emergency", 626 | [19] = "Military", 627 | [20] = "Commercial", 628 | [21] = "Train" 629 | } 630 | 631 | function GetJobType(job) 632 | if Config.PoliceJobs[job] then 633 | return 'police' 634 | elseif Config.AmbulanceJobs[job] then 635 | return 'ambulance' 636 | elseif Config.DojJobs[job] then 637 | return 'doj' 638 | else 639 | return nil 640 | end 641 | end 642 | 643 | -- this is a hack, because the qb-menu in qb-policejob populates an impound location and passed it through to the event. 644 | -- if this impound locations are changed in qb-policejob, they must also be changed here. 645 | Config.ImpoundLocations = { 646 | [1] = vector4(436.68, -1007.42, 27.32, 180.0), 647 | [2] = vector4(-436.14, 5982.63, 31.34, 136.0), 648 | } 649 | -------------------------------------------------------------------------------- /ps-mdt/server/main.lua: -------------------------------------------------------------------------------- 1 | local QBCore = exports['qb-core']:GetCoreObject() 2 | -- Maybe cache? 3 | local incidents = {} 4 | local convictions = {} 5 | local bolos = {} 6 | 7 | -- TODO make it departments compatible 8 | local activeUnits = {} 9 | 10 | local impound = {} 11 | local dispatchMessages = {} 12 | 13 | local function IsPolice(job) 14 | for k, v in pairs(Config.PoliceJobs) do 15 | if job == k then 16 | return true 17 | end 18 | end 19 | return false 20 | end 21 | 22 | AddEventHandler("onResourceStart", function(resourceName) 23 | if (resourceName == 'ps-mdt') then 24 | activeUnits = {} 25 | end 26 | end) 27 | 28 | if Config.UseWolfknightRadar == true then 29 | RegisterNetEvent("wk:onPlateScanned") 30 | AddEventHandler("wk:onPlateScanned", function(cam, plate, index) 31 | local src = source 32 | local Player = QBCore.Functions.GetPlayer(src) 33 | local bolo = GetBoloStatus(plate) 34 | if bolo == true then 35 | TriggerClientEvent("wk:togglePlateLock", src, cam, true, bolo) 36 | end 37 | end) 38 | end 39 | RegisterNetEvent("ps-mdt:server:OnPlayerUnload", function() 40 | --// Delete player from the MDT on logout 41 | local src = source 42 | local player = QBCore.Functions.GetPlayer(src) 43 | if activeUnits[player.PlayerData.citizenid] ~= nil then 44 | activeUnits[player.PlayerData.citizenid] = nil 45 | end 46 | end) 47 | 48 | AddEventHandler("playerDropped", function(reason) 49 | --// Delete player from the MDT on logout 50 | local src = source 51 | local player = QBCore.Functions.GetPlayer(src) 52 | if player ~= nil then 53 | if activeUnits[player.PlayerData.citizenid] ~= nil then 54 | activeUnits[player.PlayerData.citizenid] = nil 55 | end 56 | else 57 | local license = QBCore.Functions.GetIdentifier(src, "license") 58 | local citizenids = GetCitizenID(license) 59 | 60 | for _, v in pairs(citizenids) do 61 | if activeUnits[v.citizenid] ~= nil then 62 | activeUnits[v.citizenid] = nil 63 | end 64 | end 65 | end 66 | end) 67 | 68 | RegisterNetEvent("ps-mdt:server:ToggleDuty", function() 69 | local src = source 70 | local player = QBCore.Functions.GetPlayer(src) 71 | if not player.PlayerData.job.onduty then 72 | --// Remove from MDT 73 | if activeUnits[player.PlayerData.citizenid] ~= nil then 74 | activeUnits[player.PlayerData.citizenid] = nil 75 | end 76 | end 77 | end) 78 | 79 | RegisterNetEvent('mdt:server:openMDT', function() 80 | local src = source 81 | local PlayerData = GetPlayerData(src) 82 | if not PermCheck(src, PlayerData) then return end 83 | local Radio = Player(src).state.radioChannel or 0 84 | --[[ if Radio > 100 then 85 | Radio = 0 86 | end ]] 87 | 88 | activeUnits[PlayerData.citizenid] = { 89 | cid = PlayerData.citizenid, 90 | callSign = PlayerData.metadata['callsign'], 91 | firstName = PlayerData.charinfo.firstname:sub(1,1):upper()..PlayerData.charinfo.firstname:sub(2), 92 | lastName = PlayerData.charinfo.lastname:sub(1,1):upper()..PlayerData.charinfo.lastname:sub(2), 93 | radio = Radio, 94 | unitType = PlayerData.job.name, 95 | duty = PlayerData.job.onduty 96 | } 97 | 98 | local JobType = GetJobType(PlayerData.job.name) 99 | local bulletin = GetBulletins(JobType) 100 | 101 | local calls = exports['ps-dispatch']:GetDispatchCalls() 102 | 103 | --TriggerClientEvent('mdt:client:dashboardbulletin', src, bulletin) 104 | TriggerClientEvent('mdt:client:open', src, bulletin, activeUnits, calls, PlayerData.citizenid) 105 | --TriggerClientEvent('mdt:client:GetActiveUnits', src, activeUnits) 106 | end) 107 | 108 | QBCore.Functions.CreateCallback('mdt:server:SearchProfile', function(source, cb, sentData) 109 | if not sentData then return cb({}) end 110 | local src = source 111 | local Player = QBCore.Functions.GetPlayer(src) 112 | if Player then 113 | local JobType = GetJobType(Player.PlayerData.job.name) 114 | if JobType ~= nil then 115 | local people = MySQL.query.await("SELECT p.citizenid, p.charinfo, md.pfp FROM players p LEFT JOIN mdt_data md on p.citizenid = md.cid WHERE LOWER(CONCAT(JSON_VALUE(p.charinfo, '$.firstname'), ' ', JSON_VALUE(p.charinfo, '$.lastname'))) LIKE :query OR LOWER(`charinfo`) LIKE :query OR LOWER(`citizenid`) LIKE :query OR LOWER(`fingerprint`) LIKE :query AND jobtype = :jobtype LIMIT 20", { query = string.lower('%'..sentData..'%'), jobtype = JobType }) 116 | local citizenIds = {} 117 | local citizenIdIndexMap = {} 118 | if not next(people) then cb({}) return end 119 | 120 | for index, data in pairs(people) do 121 | people[index]['warrant'] = false 122 | people[index]['convictions'] = 0 123 | people[index]['licences'] = GetPlayerLicenses(data.citizenid) 124 | people[index]['pp'] = ProfPic(data.gender, data.pfp) 125 | citizenIds[#citizenIds+1] = data.citizenid 126 | citizenIdIndexMap[data.citizenid] = index 127 | end 128 | 129 | local convictions = GetConvictions(citizenIds) 130 | 131 | if next(convictions) then 132 | for _, conv in pairs(convictions) do 133 | if conv.warrant then people[citizenIdIndexMap[conv.cid]].warrant = true end 134 | 135 | local charges = json.decode(conv.charges) 136 | people[citizenIdIndexMap[conv.cid]].convictions = people[citizenIdIndexMap[conv.cid]].convictions + #charges 137 | end 138 | end 139 | 140 | 141 | return cb(people) 142 | end 143 | end 144 | 145 | return cb({}) 146 | end) 147 | 148 | QBCore.Functions.CreateCallback("mdt:server:getWarrants", function(source, cb) 149 | local WarrantData = {} 150 | local data = MySQL.query.await("SELECT * FROM mdt_convictions", {}) 151 | for _, value in pairs(data) do 152 | if value.warrant == "1" then 153 | WarrantData[#WarrantData+1] = { 154 | cid = value.cid, 155 | linkedincident = value.linkedincident, 156 | name = GetNameFromId(value.cid), 157 | time = value.time 158 | } 159 | end 160 | end 161 | cb(WarrantData) 162 | end) 163 | 164 | QBCore.Functions.CreateCallback('mdt:server:OpenDashboard', function(source, cb) 165 | local PlayerData = GetPlayerData(source) 166 | if not PermCheck(source, PlayerData) then return end 167 | local JobType = GetJobType(PlayerData.job.name) 168 | local bulletin = GetBulletins(JobType) 169 | cb(bulletin) 170 | end) 171 | 172 | RegisterNetEvent('mdt:server:NewBulletin', function(title, info, time) 173 | local src = source 174 | local PlayerData = GetPlayerData(src) 175 | if not PermCheck(src, PlayerData) then return end 176 | local JobType = GetJobType(PlayerData.job.name) 177 | local playerName = GetNameFromPlayerData(PlayerData) 178 | local newBulletin = MySQL.insert.await('INSERT INTO `mdt_bulletin` (`title`, `desc`, `author`, `time`, `jobtype`) VALUES (:title, :desc, :author, :time, :jt)', { 179 | title = title, 180 | desc = info, 181 | author = playerName, 182 | time = tostring(time), 183 | jt = JobType 184 | }) 185 | 186 | AddLog(("A new bulletin was added by %s with the title: %s!"):format(playerName, title)) 187 | TriggerClientEvent('mdt:client:newBulletin', -1, src, {id = newBulletin, title = title, info = info, time = time, author = PlayerData.CitizenId}, JobType) 188 | end) 189 | 190 | RegisterNetEvent('mdt:server:deleteBulletin', function(id, title) 191 | if not id then return false end 192 | local src = source 193 | local PlayerData = GetPlayerData(src) 194 | if not PermCheck(src, PlayerData) then return end 195 | local JobType = GetJobType(PlayerData.job.name) 196 | 197 | MySQL.query.await('DELETE FROM `mdt_bulletin` where id = ?', {id}) 198 | AddLog("Bulletin with Title: "..title.." was deleted by " .. GetNameFromPlayerData(PlayerData) .. ".") 199 | end) 200 | 201 | QBCore.Functions.CreateCallback('mdt:server:GetProfileData', function(source, cb, sentId) 202 | if not sentId then return cb({}) end 203 | 204 | local src = source 205 | local PlayerData = GetPlayerData(src) 206 | if not PermCheck(src, PlayerData) then return cb({}) end 207 | local JobType = GetJobType(PlayerData.job.name) 208 | local target = GetPlayerDataById(sentId) 209 | local JobName = PlayerData.job.name 210 | 211 | if not target or not next(target) then return cb({}) end 212 | 213 | -- Convert to string because bad code, yes? 214 | if type(target.job) == 'string' then target.job = json.decode(target.job) end 215 | if type(target.charinfo) == 'string' then target.charinfo = json.decode(target.charinfo) end 216 | if type(target.metadata) == 'string' then target.metadata = json.decode(target.metadata) end 217 | 218 | local licencesdata = target.metadata['licences'] or { 219 | ['driver'] = false, 220 | ['business'] = false, 221 | ['weapon'] = false, 222 | ['pilot'] = false 223 | } 224 | 225 | local job, grade = UnpackJob(target.job) 226 | 227 | local person = { 228 | cid = target.citizenid, 229 | firstname = target.charinfo.firstname, 230 | lastname = target.charinfo.lastname, 231 | job = job.label, 232 | grade = grade.name, 233 | pp = ProfPic(target.charinfo.gender), 234 | licences = licencesdata, 235 | dob = target.charinfo.birthdate, 236 | mdtinfo = '', 237 | fingerprint = '', 238 | tags = {}, 239 | vehicles = {}, 240 | properties = {}, 241 | gallery = {}, 242 | isLimited = false 243 | } 244 | 245 | if Config.PoliceJobs[JobName] then 246 | local convictions = GetConvictions({person.cid}) 247 | person.convictions2 = {} 248 | local convCount = 1 249 | if next(convictions) then 250 | for _, conv in pairs(convictions) do 251 | if conv.warrant then person.warrant = true end 252 | local charges = json.decode(conv.charges) 253 | for _, charge in pairs(charges) do 254 | person.convictions2[convCount] = charge 255 | convCount = convCount + 1 256 | end 257 | end 258 | end 259 | local hash = {} 260 | person.convictions = {} 261 | 262 | for _,v in ipairs(person.convictions2) do 263 | if (not hash[v]) then 264 | person.convictions[#person.convictions+1] = v -- found this dedupe method on sourceforge somewhere, copy+pasta dev, needs to be refined later 265 | hash[v] = true 266 | end 267 | end 268 | local vehicles = GetPlayerVehicles(person.cid) 269 | 270 | if vehicles then 271 | person.vehicles = vehicles 272 | end 273 | local Coords = {} 274 | local Houses = {} 275 | local properties= GetPlayerProperties(person.cid) 276 | for k, v in pairs(properties) do 277 | Coords[#Coords+1] = { 278 | coords = json.decode(v["coords"]), 279 | } 280 | end 281 | for index = 1, #Coords, 1 do 282 | Houses[#Houses+1] = { 283 | label = properties[index]["label"], 284 | coords = tostring(Coords[index]["coords"]["enter"]["x"]..",".. Coords[index]["coords"]["enter"]["y"].. ",".. Coords[index]["coords"]["enter"]["z"]), 285 | } 286 | end 287 | -- if properties then 288 | person.properties = Houses 289 | -- end 290 | end 291 | 292 | local mdtData = GetPersonInformation(sentId, JobType) 293 | if mdtData then 294 | person.mdtinfo = mdtData.information 295 | person.fingerprint = mdtData.fingerprint 296 | person.profilepic = mdtData.pfp 297 | person.tags = json.decode(mdtData.tags) 298 | person.gallery = json.decode(mdtData.gallery) 299 | end 300 | 301 | local mdtData2 = GetPfpFingerPrintInformation(sentId) 302 | if mdtData2 then 303 | person.fingerprint = mdtData2.fingerprint 304 | person.profilepic = mdtData and mdtData.pfp or "" 305 | end 306 | 307 | return cb(person) 308 | end) 309 | 310 | RegisterNetEvent("mdt:server:saveProfile", function(pfp, information, cid, fName, sName, tags, gallery, fingerprint, licenses) 311 | local src = source 312 | local Player = QBCore.Functions.GetPlayer(src) 313 | ManageLicenses(cid, licenses) 314 | if Player then 315 | local JobType = GetJobType(Player.PlayerData.job.name) 316 | if JobType == 'doj' then JobType = 'police' end 317 | MySQL.Async.insert('INSERT INTO mdt_data (cid, information, pfp, jobtype, tags, gallery, fingerprint) VALUES (:cid, :information, :pfp, :jobtype, :tags, :gallery, :fingerprint) ON DUPLICATE KEY UPDATE cid = :cid, information = :information, pfp = :pfp, tags = :tags, gallery = :gallery, fingerprint = :fingerprint', { 318 | cid = cid, 319 | information = information, 320 | pfp = pfp, 321 | jobtype = JobType, 322 | tags = json.encode(tags), 323 | gallery = json.encode(gallery), 324 | fingerprint = fingerprint, 325 | }) 326 | end 327 | end) 328 | 329 | RegisterNetEvent("mdt:server:updateLicense", function(cid, type, status) 330 | local src = source 331 | local Player = QBCore.Functions.GetPlayer(src) 332 | if Player then 333 | if GetJobType(Player.PlayerData.job.name) == 'police' then 334 | ManageLicense(cid, type, status) 335 | end 336 | end 337 | end) 338 | 339 | -- Incidents 340 | 341 | 342 | RegisterNetEvent('mdt:server:getAllIncidents', function() 343 | local src = source 344 | local Player = QBCore.Functions.GetPlayer(src) 345 | if Player then 346 | local JobType = GetJobType(Player.PlayerData.job.name) 347 | if JobType == 'police' or JobType == 'doj' then 348 | local matches = MySQL.query.await("SELECT * FROM `mdt_incidents` ORDER BY `id` DESC LIMIT 30", {}) 349 | 350 | TriggerClientEvent('mdt:client:getAllIncidents', src, matches) 351 | end 352 | end 353 | end) 354 | 355 | RegisterNetEvent('mdt:server:searchIncidents', function(query) 356 | if query then 357 | local src = source 358 | local Player = QBCore.Functions.GetPlayer(src) 359 | if Player then 360 | local JobType = GetJobType(Player.PlayerData.job.name) 361 | if JobType == 'police' or JobType == 'doj' then 362 | local matches = MySQL.query.await("SELECT * FROM `mdt_incidents` WHERE `id` LIKE :query OR LOWER(`title`) LIKE :query OR LOWER(`author`) LIKE :query OR LOWER(`details`) LIKE :query OR LOWER(`tags`) LIKE :query OR LOWER(`officersinvolved`) LIKE :query OR LOWER(`civsinvolved`) LIKE :query OR LOWER(`author`) LIKE :query ORDER BY `id` DESC LIMIT 50", { 363 | query = string.lower('%'..query..'%') -- % wildcard, needed to search for all alike results 364 | }) 365 | 366 | TriggerClientEvent('mdt:client:getIncidents', src, matches) 367 | end 368 | end 369 | end 370 | end) 371 | 372 | RegisterNetEvent('mdt:server:getIncidentData', function(sentId) 373 | if sentId then 374 | local src = source 375 | local Player = QBCore.Functions.GetPlayer(src) 376 | if Player then 377 | local JobType = GetJobType(Player.PlayerData.job.name) 378 | if JobType == 'police' or JobType == 'doj' then 379 | local matches = MySQL.query.await("SELECT * FROM `mdt_incidents` WHERE `id` = :id", { 380 | id = sentId 381 | }) 382 | local data = matches[1] 383 | data['tags'] = json.decode(data['tags']) 384 | data['officersinvolved'] = json.decode(data['officersinvolved']) 385 | data['civsinvolved'] = json.decode(data['civsinvolved']) 386 | data['evidence'] = json.decode(data['evidence']) 387 | 388 | 389 | local convictions = MySQL.query.await("SELECT * FROM `mdt_convictions` WHERE `linkedincident` = :id", { 390 | id = sentId 391 | }) 392 | if convictions ~= nil then 393 | for i=1, #convictions do 394 | local res = GetNameFromId(convictions[i]['cid']) 395 | if res ~= nil then 396 | convictions[i]['name'] = res 397 | else 398 | convictions[i]['name'] = "Unknown" 399 | end 400 | convictions[i]['charges'] = json.decode(convictions[i]['charges']) 401 | end 402 | end 403 | TriggerClientEvent('mdt:client:getIncidentData', src, data, convictions) 404 | end 405 | end 406 | end 407 | end) 408 | 409 | RegisterNetEvent('mdt:server:getAllBolos', function() 410 | local src = source 411 | local Player = QBCore.Functions.GetPlayer(src) 412 | local JobType = GetJobType(Player.PlayerData.job.name) 413 | if JobType == 'police' or JobType == 'ambulance' then 414 | local matches = MySQL.query.await("SELECT * FROM `mdt_bolos` WHERE jobtype = :jobtype", {jobtype = JobType}) 415 | TriggerClientEvent('mdt:client:getAllBolos', src, matches) 416 | end 417 | end) 418 | 419 | RegisterNetEvent('mdt:server:searchBolos', function(sentSearch) 420 | if sentSearch then 421 | local src = source 422 | local Player = QBCore.Functions.GetPlayer(src) 423 | local JobType = GetJobType(Player.PlayerData.job.name) 424 | if JobType == 'police' or JobType == 'ambulance' then 425 | local matches = MySQL.query.await("SELECT * FROM `mdt_bolos` WHERE `id` LIKE :query OR LOWER(`title`) LIKE :query OR `plate` LIKE :query OR LOWER(`owner`) LIKE :query OR LOWER(`individual`) LIKE :query OR LOWER(`detail`) LIKE :query OR LOWER(`officersinvolved`) LIKE :query OR LOWER(`tags`) LIKE :query OR LOWER(`author`) LIKE :query AND jobtype = :jobtype", { 426 | query = string.lower('%'..sentSearch..'%'), -- % wildcard, needed to search for all alike results 427 | jobtype = JobType 428 | }) 429 | TriggerClientEvent('mdt:client:getBolos', src, matches) 430 | end 431 | end 432 | end) 433 | 434 | RegisterNetEvent('mdt:server:getBoloData', function(sentId) 435 | if sentId then 436 | local src = source 437 | local Player = QBCore.Functions.GetPlayer(src) 438 | local JobType = GetJobType(Player.PlayerData.job.name) 439 | if JobType == 'police' or JobType == 'ambulance' then 440 | local matches = MySQL.query.await("SELECT * FROM `mdt_bolos` WHERE `id` = :id AND jobtype = :jobtype LIMIT 1", { 441 | id = sentId, 442 | jobtype = JobType 443 | }) 444 | 445 | local data = matches[1] 446 | data['tags'] = json.decode(data['tags']) 447 | data['officersinvolved'] = json.decode(data['officersinvolved']) 448 | data['gallery'] = json.decode(data['gallery']) 449 | TriggerClientEvent('mdt:client:getBoloData', src, data) 450 | end 451 | end 452 | end) 453 | 454 | RegisterNetEvent('mdt:server:newBolo', function(existing, id, title, plate, owner, individual, detail, tags, gallery, officersinvolved, time) 455 | if id then 456 | local src = source 457 | local Player = QBCore.Functions.GetPlayer(src) 458 | local JobType = GetJobType(Player.PlayerData.job.name) 459 | if JobType == 'police' or JobType == 'ambulance' then 460 | local fullname = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname 461 | 462 | local function InsertBolo() 463 | MySQL.insert('INSERT INTO `mdt_bolos` (`title`, `author`, `plate`, `owner`, `individual`, `detail`, `tags`, `gallery`, `officersinvolved`, `time`, `jobtype`) VALUES (:title, :author, :plate, :owner, :individual, :detail, :tags, :gallery, :officersinvolved, :time, :jobtype)', { 464 | title = title, 465 | author = fullname, 466 | plate = plate, 467 | owner = owner, 468 | individual = individual, 469 | detail = detail, 470 | tags = json.encode(tags), 471 | gallery = json.encode(gallery), 472 | officersinvolved = json.encode(officersinvolved), 473 | time = tostring(time), 474 | jobtype = JobType 475 | }, function(r) 476 | if r then 477 | TriggerClientEvent('mdt:client:boloComplete', src, r) 478 | TriggerEvent('mdt:server:AddLog', "A new BOLO was created by "..fullname.." with the title ("..title..") and ID ("..id..")") 479 | end 480 | end) 481 | end 482 | 483 | local function UpdateBolo() 484 | MySQL.update("UPDATE mdt_bolos SET `title`=:title, plate=:plate, owner=:owner, individual=:individual, detail=:detail, tags=:tags, gallery=:gallery, officersinvolved=:officersinvolved WHERE `id`=:id AND jobtype = :jobtype LIMIT 1", { 485 | title = title, 486 | plate = plate, 487 | owner = owner, 488 | individual = individual, 489 | detail = detail, 490 | tags = json.encode(tags), 491 | gallery = json.encode(gallery), 492 | officersinvolved = json.encode(officersinvolved), 493 | id = id, 494 | jobtype = JobType 495 | }, function(r) 496 | if r then 497 | TriggerClientEvent('mdt:client:boloComplete', src, id) 498 | TriggerEvent('mdt:server:AddLog', "A BOLO was updated by "..fullname.." with the title ("..title..") and ID ("..id..")") 499 | end 500 | end) 501 | end 502 | 503 | if existing then 504 | UpdateBolo() 505 | elseif not existing then 506 | InsertBolo() 507 | end 508 | end 509 | end 510 | end) 511 | 512 | RegisterNetEvent('mdt:server:deleteBolo', function(id) 513 | if id then 514 | local src = source 515 | local Player = QBCore.Functions.GetPlayer(src) 516 | local JobType = GetJobType(Player.PlayerData.job.name) 517 | if JobType == 'police' then 518 | local fullname = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname 519 | MySQL.update("DELETE FROM `mdt_bolos` WHERE id=:id", { id = id, jobtype = JobType }) 520 | TriggerEvent('mdt:server:AddLog', "A BOLO was deleted by "..fullname.." with the ID ("..id..")") 521 | end 522 | end 523 | end) 524 | 525 | RegisterNetEvent('mdt:server:deleteICU', function(id) 526 | if id then 527 | local src = source 528 | local Player = QBCore.Functions.GetPlayer(src) 529 | local JobType = GetJobType(Player.PlayerData.job.name) 530 | if JobType == 'ambulance' then 531 | local fullname = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname 532 | MySQL.update("DELETE FROM `mdt_bolos` WHERE id=:id", { id = id, jobtype = JobType }) 533 | TriggerEvent('mdt:server:AddLog', "A ICU Check-in was deleted by "..fullname.." with the ID ("..id..")") 534 | end 535 | end 536 | end) 537 | 538 | RegisterNetEvent('mdt:server:incidentSearchPerson', function(query) 539 | if query then 540 | local src = source 541 | local Player = QBCore.Functions.GetPlayer(src) 542 | if Player then 543 | local JobType = GetJobType(Player.PlayerData.job.name) 544 | if JobType == 'police' or JobType == 'doj' then 545 | local function ProfPic(gender, profilepic) 546 | if profilepic then return profilepic end; 547 | if gender == "f" then return "img/female.png" end; 548 | return "img/male.png" 549 | end 550 | 551 | local result = MySQL.query.await("SELECT p.citizenid, p.charinfo, md.pfp from players p LEFT JOIN mdt_data md on p.citizenid = md.cid WHERE LOWER(`charinfo`) LIKE :query OR LOWER(`citizenid`) LIKE :query AND `jobtype` = :jobtype LIMIT 30", { 552 | query = string.lower('%'..query..'%'), -- % wildcard, needed to search for all alike results 553 | jobtype = JobType 554 | }) 555 | local data = {} 556 | for i=1, #result do 557 | local charinfo = json.decode(result[i].charinfo) 558 | data[i] = {id = result[i].citizenid, firstname = charinfo.firstname, lastname = charinfo.lastname, profilepic = ProfPic(charinfo.gender, result[i].pfp)} 559 | end 560 | TriggerClientEvent('mdt:client:incidentSearchPerson', src, data) 561 | end 562 | end 563 | end 564 | end) 565 | 566 | RegisterNetEvent('mdt:server:getAllReports', function() 567 | local src = source 568 | local Player = QBCore.Functions.GetPlayer(src) 569 | if Player then 570 | local JobType = GetJobType(Player.PlayerData.job.name) 571 | if JobType == 'police' or JobType == 'doj' or JobType == 'ambulance' then 572 | if JobType == 'doj' then JobType = 'police' end 573 | local matches = MySQL.query.await("SELECT * FROM `mdt_reports` WHERE jobtype = :jobtype ORDER BY `id` DESC LIMIT 30", { 574 | jobtype = JobType 575 | }) 576 | TriggerClientEvent('mdt:client:getAllReports', src, matches) 577 | end 578 | end 579 | end) 580 | 581 | RegisterNetEvent('mdt:server:getReportData', function(sentId) 582 | if sentId then 583 | local src = source 584 | local Player = QBCore.Functions.GetPlayer(src) 585 | if Player then 586 | local JobType = GetJobType(Player.PlayerData.job.name) 587 | if JobType == 'police' or JobType == 'doj' or JobType == 'ambulance' then 588 | if JobType == 'doj' then JobType = 'police' end 589 | local matches = MySQL.query.await("SELECT * FROM `mdt_reports` WHERE `id` = :id AND `jobtype` = :jobtype LIMIT 1", { 590 | id = sentId, 591 | jobtype = JobType 592 | }) 593 | local data = matches[1] 594 | data['tags'] = json.decode(data['tags']) 595 | data['officersinvolved'] = json.decode(data['officersinvolved']) 596 | data['civsinvolved'] = json.decode(data['civsinvolved']) 597 | data['gallery'] = json.decode(data['gallery']) 598 | TriggerClientEvent('mdt:client:getReportData', src, data) 599 | end 600 | end 601 | end 602 | end) 603 | 604 | RegisterNetEvent('mdt:server:searchReports', function(sentSearch) 605 | if sentSearch then 606 | local src = source 607 | local Player = QBCore.Functions.GetPlayer(src) 608 | if Player then 609 | local JobType = GetJobType(Player.PlayerData.job.name) 610 | if JobType == 'police' or JobType == 'doj' or JobType == 'ambulance' then 611 | if JobType == 'doj' then JobType = 'police' end 612 | local matches = MySQL.query.await("SELECT * FROM `mdt_reports` WHERE `id` LIKE :query OR LOWER(`author`) LIKE :query OR LOWER(`title`) LIKE :query OR LOWER(`type`) LIKE :query OR LOWER(`details`) LIKE :query OR LOWER(`tags`) LIKE :query AND `jobtype` = :jobtype ORDER BY `id` DESC LIMIT 50", { 613 | query = string.lower('%'..sentSearch..'%'), -- % wildcard, needed to search for all alike results 614 | jobtype = JobType 615 | }) 616 | 617 | TriggerClientEvent('mdt:client:getAllReports', src, matches) 618 | end 619 | end 620 | end 621 | end) 622 | 623 | RegisterNetEvent('mdt:server:newReport', function(existing, id, title, reporttype, details, tags, gallery, officers, civilians, time) 624 | if id then 625 | local src = source 626 | local Player = QBCore.Functions.GetPlayer(src) 627 | if Player then 628 | local JobType = GetJobType(Player.PlayerData.job.name) 629 | if JobType ~= nil then 630 | local fullname = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname 631 | local function InsertReport() 632 | MySQL.insert('INSERT INTO `mdt_reports` (`title`, `author`, `type`, `details`, `tags`, `gallery`, `officersinvolved`, `civsinvolved`, `time`, `jobtype`) VALUES (:title, :author, :type, :details, :tags, :gallery, :officersinvolved, :civsinvolved, :time, :jobtype)', { 633 | title = title, 634 | author = fullname, 635 | type = reporttype, 636 | details = details, 637 | tags = json.encode(tags), 638 | gallery = json.encode(gallery), 639 | officersinvolved = json.encode(officers), 640 | civsinvolved = json.encode(civilians), 641 | time = tostring(time), 642 | jobtype = JobType, 643 | }, function(r) 644 | if r then 645 | TriggerClientEvent('mdt:client:reportComplete', src, r) 646 | TriggerEvent('mdt:server:AddLog', "A new report was created by "..fullname.." with the title ("..title..") and ID ("..id..")") 647 | end 648 | end) 649 | end 650 | 651 | local function UpdateReport() 652 | MySQL.update("UPDATE `mdt_reports` SET `title` = :title, type = :type, details = :details, tags = :tags, gallery = :gallery, officersinvolved = :officersinvolved, civsinvolved = :civsinvolved, jobtype = :jobtype WHERE `id` = :id LIMIT 1", { 653 | title = title, 654 | type = reporttype, 655 | details = details, 656 | tags = json.encode(tags), 657 | gallery = json.encode(gallery), 658 | officersinvolved = json.encode(officers), 659 | civsinvolved = json.encode(civilians), 660 | jobtype = JobType, 661 | id = id, 662 | }, function(affectedRows) 663 | if affectedRows > 0 then 664 | TriggerClientEvent('mdt:client:reportComplete', src, id) 665 | TriggerEvent('mdt:server:AddLog', "A report was updated by "..fullname.." with the title ("..title..") and ID ("..id..")") 666 | end 667 | end) 668 | end 669 | 670 | if existing then 671 | UpdateReport() 672 | elseif not existing then 673 | InsertReport() 674 | end 675 | end 676 | end 677 | end 678 | end) 679 | 680 | QBCore.Functions.CreateCallback('mdt:server:SearchVehicles', function(source, cb, sentData) 681 | if not sentData then return cb({}) end 682 | local src = source 683 | local PlayerData = GetPlayerData(src) 684 | if not PermCheck(source, PlayerData) then return cb({}) end 685 | 686 | local src = source 687 | local Player = QBCore.Functions.GetPlayer(src) 688 | if Player then 689 | local JobType = GetJobType(Player.PlayerData.job.name) 690 | if JobType == 'police' or JobType == 'doj' then 691 | local vehicles = MySQL.query.await("SELECT pv.id, pv.citizenid, pv.plate, pv.vehicle, pv.mods, pv.state, p.charinfo FROM `player_vehicles` pv LEFT JOIN players p ON pv.citizenid = p.citizenid WHERE LOWER(`plate`) LIKE :query OR LOWER(`vehicle`) LIKE :query LIMIT 25", { 692 | query = string.lower('%'..sentData..'%') 693 | }) 694 | 695 | if not next(vehicles) then cb({}) return end 696 | 697 | for _, value in ipairs(vehicles) do 698 | if value.state == 0 then 699 | value.state = "Out" 700 | elseif value.state == 1 then 701 | value.state = "Garaged" 702 | elseif value.state == 2 then 703 | value.state = "Impounded" 704 | end 705 | 706 | value.bolo = false 707 | local boloResult = GetBoloStatus(value.plate) 708 | if boloResult then 709 | value.bolo = true 710 | end 711 | 712 | value.code = false 713 | value.stolen = false 714 | value.image = "img/not-found.webp" 715 | local info = GetVehicleInformation(value.plate) 716 | if info then 717 | value.code = info['code5'] 718 | value.stolen = info['stolen'] 719 | value.image = info['image'] 720 | end 721 | 722 | local ownerResult = json.decode(value.charinfo) 723 | 724 | value.owner = ownerResult['firstname'] .. " " .. ownerResult['lastname'] 725 | end 726 | -- idk if this works or I have to call cb first then return :shrug: 727 | return cb(vehicles) 728 | end 729 | 730 | return cb({}) 731 | end 732 | 733 | end) 734 | 735 | RegisterNetEvent('mdt:server:getVehicleData', function(plate) 736 | if plate then 737 | local src = source 738 | local Player = QBCore.Functions.GetPlayer(src) 739 | if Player then 740 | local JobType = GetJobType(Player.PlayerData.job.name) 741 | if JobType == 'police' or JobType == 'doj' then 742 | local vehicle = MySQL.query.await("select pv.*, p.charinfo from player_vehicles pv LEFT JOIN players p ON pv.citizenid = p.citizenid where pv.plate = :plate LIMIT 1", { plate = string.gsub(plate, "^%s*(.-)%s*$", "%1")}) 743 | if vehicle and vehicle[1] then 744 | vehicle[1]['impound'] = false 745 | if vehicle[1].state == 2 then 746 | vehicle[1]['impound'] = true 747 | end 748 | 749 | vehicle[1]['bolo'] = GetBoloStatus(vehicle[1]['plate']) 750 | vehicle[1]['information'] = "" 751 | 752 | vehicle[1]['name'] = "Unknown Person" 753 | 754 | local ownerResult = json.decode(vehicle[1].charinfo) 755 | vehicle[1]['name'] = ownerResult['firstname'] .. " " .. ownerResult['lastname'] 756 | 757 | local color1 = json.decode(vehicle[1].mods) 758 | vehicle[1]['color1'] = color1['color1'] 759 | 760 | vehicle[1]['dbid'] = 0 761 | 762 | local info = GetVehicleInformation(vehicle[1]['plate']) 763 | if info then 764 | vehicle[1]['information'] = info['information'] 765 | vehicle[1]['dbid'] = info['id'] 766 | vehicle[1]['image'] = info['image'] 767 | vehicle[1]['code'] = info['code5'] 768 | vehicle[1]['stolen'] = info['stolen'] 769 | end 770 | 771 | if vehicle[1]['image'] == nil then vehicle[1]['image'] = "img/not-found.webp" end -- Image 772 | end 773 | 774 | TriggerClientEvent('mdt:client:getVehicleData', src, vehicle) 775 | end 776 | end 777 | end 778 | end) 779 | 780 | RegisterNetEvent('mdt:server:saveVehicleInfo', function(dbid, plate, imageurl, notes, stolen, code5, impoundInfo) 781 | if plate then 782 | local src = source 783 | local Player = QBCore.Functions.GetPlayer(src) 784 | if Player then 785 | if GetJobType(Player.PlayerData.job.name) == 'police' then 786 | if dbid == nil then dbid = 0 end; 787 | local fullname = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname 788 | TriggerEvent('mdt:server:AddLog', "A vehicle with the plate ("..plate..") has a new image ("..imageurl..") edited by "..fullname) 789 | if tonumber(dbid) == 0 then 790 | MySQL.insert('INSERT INTO `mdt_vehicleinfo` (`plate`, `information`, `image`, `code5`, `stolen`) VALUES (:plate, :information, :image, :code5, :stolen)', { plate = string.gsub(plate, "^%s*(.-)%s*$", "%1"), information = notes, image = imageurl, code5 = code5, stolen = stolen }, function(infoResult) 791 | if infoResult then 792 | TriggerClientEvent('mdt:client:updateVehicleDbId', src, infoResult) 793 | TriggerEvent('mdt:server:AddLog', "A vehicle with the plate ("..plate..") was added to the vehicle information database by "..fullname) 794 | end 795 | end) 796 | elseif tonumber(dbid) > 0 then 797 | MySQL.update("UPDATE mdt_vehicleinfo SET `information`= :information, `image`= :image, `code5`= :code5, `stolen`= :stolen WHERE `plate`= :plate LIMIT 1", { plate = string.gsub(plate, "^%s*(.-)%s*$", "%1"), information = notes, image = imageurl, code5 = code5, stolen = stolen }) 798 | end 799 | 800 | if impoundInfo.impoundChanged then 801 | local vehicle = MySQL.single.await("SELECT p.id, p.plate, i.vehicleid AS impoundid FROM `player_vehicles` p LEFT JOIN `mdt_impound` i ON i.vehicleid = p.id WHERE plate=:plate", { plate = string.gsub(plate, "^%s*(.-)%s*$", "%1") }) 802 | if impoundInfo.impoundActive then 803 | local plate, linkedreport, fee, time = impoundInfo['plate'], impoundInfo['linkedreport'], impoundInfo['fee'], impoundInfo['time'] 804 | if (plate and linkedreport and fee and time) then 805 | if vehicle.impoundid == nil then 806 | -- This section is copy pasted from request impound and needs some attention. 807 | -- sentVehicle doesnt exist. 808 | -- data is defined twice 809 | -- INSERT INTO will not work if it exists already (which it will) 810 | local data = vehicle 811 | MySQL.insert('INSERT INTO `mdt_impound` (`vehicleid`, `linkedreport`, `fee`, `time`) VALUES (:vehicleid, :linkedreport, :fee, :time)', { 812 | vehicleid = data['id'], 813 | linkedreport = linkedreport, 814 | fee = fee, 815 | time = os.time() + (time * 60) 816 | }, function(res) 817 | -- notify? 818 | local data = { 819 | vehicleid = data['id'], 820 | plate = plate, 821 | beingcollected = 0, 822 | vehicle = sentVehicle, 823 | officer = Player.PlayerData.charinfo.firstname.. " "..Player.PlayerData.charinfo.lastname, 824 | number = Player.PlayerData.charinfo.phone, 825 | time = os.time() * 1000, 826 | src = src, 827 | } 828 | local vehicle = NetworkGetEntityFromNetworkId(sentVehicle) 829 | FreezeEntityPosition(vehicle, true) 830 | impound[#impound+1] = data 831 | 832 | TriggerClientEvent("police:client:ImpoundVehicle", src, true, fee) 833 | end) 834 | -- Read above comment 835 | end 836 | end 837 | else 838 | if vehicle.impoundid ~= nil then 839 | local data = vehicle 840 | local result = MySQL.single.await("SELECT id, vehicle, fuel, engine, body FROM `player_vehicles` WHERE plate=:plate LIMIT 1", { plate = string.gsub(plate, "^%s*(.-)%s*$", "%1")}) 841 | if result then 842 | local data = result 843 | MySQL.update("DELETE FROM `mdt_impound` WHERE vehicleid=:vehicleid", { vehicleid = data['id'] }) 844 | 845 | result.currentSelection = impoundInfo.CurrentSelection 846 | result.plate = plate 847 | TriggerClientEvent('ps-mdt:client:TakeOutImpound', src, result) 848 | end 849 | 850 | end 851 | end 852 | end 853 | end 854 | end 855 | end 856 | end) 857 | 858 | RegisterNetEvent('mdt:server:getAllLogs', function() 859 | local src = source 860 | local Player = QBCore.Functions.GetPlayer(src) 861 | if Player then 862 | if Config.LogPerms[Player.PlayerData.job.name] then 863 | if Config.LogPerms[Player.PlayerData.job.name][Player.PlayerData.job.grade.level] then 864 | 865 | local JobType = GetJobType(Player.PlayerData.job.name) 866 | local infoResult = MySQL.query.await('SELECT * FROM mdt_logs WHERE `jobtype` = :jobtype ORDER BY `id` DESC LIMIT 250', {jobtype = JobType}) 867 | 868 | TriggerLatentClientEvent('mdt:client:getAllLogs', src, 30000, infoResult) 869 | end 870 | end 871 | end 872 | end) 873 | 874 | -- Penal Code 875 | 876 | local function IsCidFelon(sentCid, cb) 877 | if sentCid then 878 | local convictions = MySQL.query.await('SELECT charges FROM mdt_convictions WHERE cid=:cid', { cid = sentCid }) 879 | local Charges = {} 880 | for i=1, #convictions do 881 | local currCharges = json.decode(convictions[i]['charges']) 882 | for x=1, #currCharges do 883 | Charges[#Charges+1] = currCharges[x] 884 | end 885 | end 886 | local PenalCode = Config.PenalCode 887 | for i=1, #Charges do 888 | for p=1, #PenalCode do 889 | for x=1, #PenalCode[p] do 890 | if PenalCode[p][x]['title'] == Charges[i] then 891 | if PenalCode[p][x]['class'] == 'Felony' then 892 | cb(true) 893 | return 894 | end 895 | break 896 | end 897 | end 898 | end 899 | end 900 | cb(false) 901 | end 902 | end 903 | 904 | exports('IsCidFelon', IsCidFelon) -- exports['erp_mdt']:IsCidFelon() 905 | 906 | RegisterCommand("isfelon", function(source, args, rawCommand) 907 | IsCidFelon(1998, function(res) 908 | end) 909 | end, false) 910 | 911 | RegisterNetEvent('mdt:server:getPenalCode', function() 912 | local src = source 913 | TriggerClientEvent('mdt:client:getPenalCode', src, Config.PenalCodeTitles, Config.PenalCode) 914 | end) 915 | 916 | RegisterNetEvent('mdt:server:setCallsign', function(cid, newcallsign) 917 | local Player = QBCore.Functions.GetPlayerByCitizenId(cid) 918 | Player.Functions.SetMetaData("callsign", newcallsign) 919 | end) 920 | 921 | RegisterNetEvent('mdt:server:saveIncident', function(id, title, information, tags, officers, civilians, evidence, associated, time) 922 | local src = source 923 | local Player = QBCore.Functions.GetPlayer(src) 924 | if Player then 925 | if GetJobType(Player.PlayerData.job.name) == 'police' then 926 | if id == 0 then 927 | local fullname = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname 928 | MySQL.insert('INSERT INTO `mdt_incidents` (`author`, `title`, `details`, `tags`, `officersinvolved`, `civsinvolved`, `evidence`, `time`, `jobtype`) VALUES (:author, :title, :details, :tags, :officersinvolved, :civsinvolved, :evidence, :time, :jobtype)', 929 | { 930 | author = fullname, 931 | title = title, 932 | details = information, 933 | tags = json.encode(tags), 934 | officersinvolved = json.encode(officers), 935 | civsinvolved = json.encode(civilians), 936 | evidence = json.encode(evidence), 937 | time = time, 938 | jobtype = 'police', 939 | }, function(infoResult) 940 | if infoResult then 941 | for i=1, #associated do 942 | MySQL.insert('INSERT INTO `mdt_convictions` (`cid`, `linkedincident`, `warrant`, `guilty`, `processed`, `associated`, `charges`, `fine`, `sentence`, `recfine`, `recsentence`, `time`) VALUES (:cid, :linkedincident, :warrant, :guilty, :processed, :associated, :charges, :fine, :sentence, :recfine, :recsentence, :time)', { 943 | cid = associated[i]['Cid'], 944 | linkedincident = infoResult, 945 | warrant = associated[i]['Warrant'], 946 | guilty = associated[i]['Guilty'], 947 | processed = associated[i]['Processed'], 948 | associated = associated[i]['Isassociated'], 949 | charges = json.encode(associated[i]['Charges']), 950 | fine = tonumber(associated[i]['Fine']), 951 | sentence = tonumber(associated[i]['Sentence']), 952 | recfine = tonumber(associated[i]['recfine']), 953 | recsentence = tonumber(associated[i]['recsentence']), 954 | time = time 955 | }) 956 | end 957 | TriggerClientEvent('mdt:client:updateIncidentDbId', src, infoResult) 958 | --TriggerEvent('mdt:server:AddLog', "A vehicle with the plate ("..plate..") was added to the vehicle information database by "..player['fullname']) 959 | end 960 | end) 961 | elseif id > 0 then 962 | MySQL.update("UPDATE mdt_incidents SET title=:title, details=:details, civsinvolved=:civsinvolved, tags=:tags, officersinvolved=:officersinvolved, evidence=:evidence WHERE id=:id", { 963 | title = title, 964 | details = information, 965 | tags = json.encode(tags), 966 | officersinvolved = json.encode(officers), 967 | civsinvolved = json.encode(civilians), 968 | evidence = json.encode(evidence), 969 | id = id 970 | }) 971 | for i=1, #associated do 972 | TriggerEvent('mdt:server:handleExistingConvictions', associated[i], id, time) 973 | end 974 | end 975 | end 976 | end 977 | end) 978 | 979 | RegisterNetEvent('mdt:server:handleExistingConvictions', function(data, incidentid, time) 980 | MySQL.query('SELECT * FROM mdt_convictions WHERE cid=:cid AND linkedincident=:linkedincident', { 981 | cid = data['Cid'], 982 | linkedincident = incidentid 983 | }, function(convictionRes) 984 | if convictionRes and convictionRes[1] and convictionRes[1]['id'] then 985 | MySQL.update('UPDATE mdt_convictions SET cid=:cid, linkedincident=:linkedincident, warrant=:warrant, guilty=:guilty, processed=:processed, associated=:associated, charges=:charges, fine=:fine, sentence=:sentence, recfine=:recfine, recsentence=:recsentence WHERE cid=:cid AND linkedincident=:linkedincident', { 986 | cid = data['Cid'], 987 | linkedincident = incidentid, 988 | warrant = data['Warrant'], 989 | guilty = data['Guilty'], 990 | processed = data['Processed'], 991 | associated = data['Isassociated'], 992 | charges = json.encode(data['Charges']), 993 | fine = tonumber(data['Fine']), 994 | sentence = tonumber(data['Sentence']), 995 | recfine = tonumber(data['recfine']), 996 | recsentence = tonumber(data['recsentence']), 997 | }) 998 | else 999 | MySQL.insert('INSERT INTO `mdt_convictions` (`cid`, `linkedincident`, `warrant`, `guilty`, `processed`, `associated`, `charges`, `fine`, `sentence`, `recfine`, `recsentence`, `time`) VALUES (:cid, :linkedincident, :warrant, :guilty, :processed, :associated, :charges, :fine, :sentence, :recfine, :recsentence, :time)', { 1000 | cid = data['Cid'], 1001 | linkedincident = incidentid, 1002 | warrant = data['Warrant'], 1003 | guilty = data['Guilty'], 1004 | processed = data['Processed'], 1005 | associated = data['Isassociated'], 1006 | charges = json.encode(data['Charges']), 1007 | fine = tonumber(data['Fine']), 1008 | sentence = tonumber(data['Sentence']), 1009 | recfine = tonumber(data['recfine']), 1010 | recsentence = tonumber(data['recsentence']), 1011 | time = time 1012 | }) 1013 | end 1014 | end) 1015 | end) 1016 | 1017 | RegisterNetEvent('mdt:server:removeIncidentCriminal', function(cid, incident) 1018 | MySQL.update('DELETE FROM mdt_convictions WHERE cid=:cid AND linkedincident=:linkedincident', { 1019 | cid = cid, 1020 | linkedincident = incident 1021 | }) 1022 | end) 1023 | 1024 | -- Dispatch 1025 | 1026 | RegisterNetEvent('mdt:server:setWaypoint', function(callid) 1027 | local src = source 1028 | local Player = QBCore.Functions.GetPlayer(source) 1029 | local JobType = GetJobType(Player.PlayerData.job.name) 1030 | if JobType == 'police' or JobType == 'ambulance' then 1031 | if callid then 1032 | local calls = exports['ps-dispatch']:GetDispatchCalls() 1033 | TriggerClientEvent('mdt:client:setWaypoint', src, calls[callid]) 1034 | end 1035 | end 1036 | end) 1037 | 1038 | RegisterNetEvent('mdt:server:callDetach', function(callid) 1039 | local src = source 1040 | local Player = QBCore.Functions.GetPlayer(src) 1041 | local playerdata = { 1042 | fullname = Player.PlayerData.charinfo.firstname.. " "..Player.PlayerData.charinfo.lastname, 1043 | job = Player.PlayerData.job, 1044 | cid = Player.PlayerData.citizenid, 1045 | callsign = Player.PlayerData.metadata.callsign 1046 | } 1047 | local JobType = GetJobType(Player.PlayerData.job.name) 1048 | if JobType == 'police' or JobType == 'ambulance' then 1049 | if callid then 1050 | TriggerEvent('dispatch:removeUnit', callid, playerdata, function(newNum) 1051 | TriggerClientEvent('mdt:client:callDetach', -1, callid, newNum) 1052 | end) 1053 | end 1054 | end 1055 | end) 1056 | 1057 | RegisterNetEvent('mdt:server:callAttach', function(callid) 1058 | local src = source 1059 | local Player = QBCore.Functions.GetPlayer(src) 1060 | local playerdata = { 1061 | fullname = Player.PlayerData.charinfo.firstname.. " "..Player.PlayerData.charinfo.lastname, 1062 | job = Player.PlayerData.job, 1063 | cid = Player.PlayerData.citizenid, 1064 | callsign = Player.PlayerData.metadata.callsign 1065 | } 1066 | local JobType = GetJobType(Player.PlayerData.job.name) 1067 | if JobType == 'police' or JobType == 'ambulance' then 1068 | if callid then 1069 | TriggerEvent('dispatch:addUnit', callid, playerdata, function(newNum) 1070 | TriggerClientEvent('mdt:client:callAttach', -1, callid, newNum) 1071 | end) 1072 | end 1073 | end 1074 | 1075 | end) 1076 | 1077 | RegisterNetEvent('mdt:server:attachedUnits', function(callid) 1078 | local src = source 1079 | local Player = QBCore.Functions.GetPlayer(src) 1080 | local JobType = GetJobType(Player.PlayerData.job.name) 1081 | if JobType == 'police' or JobType == 'ambulance' then 1082 | if callid then 1083 | local calls = exports['ps-dispatch']:GetDispatchCalls() 1084 | TriggerClientEvent('mdt:client:attachedUnits', src, calls[callid]['units'], callid) 1085 | end 1086 | end 1087 | end) 1088 | 1089 | RegisterNetEvent('mdt:server:callDispatchDetach', function(callid, cid) 1090 | local src = source 1091 | local Player = QBCore.Functions.GetPlayer(src) 1092 | local playerdata = { 1093 | fullname = Player.PlayerData.charinfo.firstname.. " "..Player.PlayerData.charinfo.lastname, 1094 | job = Player.PlayerData.job, 1095 | cid = Player.PlayerData.citizenid, 1096 | callsign = Player.PlayerData.metadata.callsign 1097 | } 1098 | local callid = tonumber(callid) 1099 | local JobType = GetJobType(Player.PlayerData.job.name) 1100 | if JobType == 'police' or JobType == 'ambulance' then 1101 | if callid then 1102 | TriggerEvent('dispatch:removeUnit', callid, playerdata, function(newNum) 1103 | TriggerClientEvent('mdt:client:callDetach', -1, callid, newNum) 1104 | end) 1105 | end 1106 | end 1107 | end) 1108 | 1109 | RegisterNetEvent('mdt:server:setDispatchWaypoint', function(callid, cid) 1110 | local src = source 1111 | local Player = QBCore.Functions.GetPlayer(src) 1112 | local callid = tonumber(callid) 1113 | local JobType = GetJobType(Player.PlayerData.job.name) 1114 | if JobType == 'police' or JobType == 'ambulance' then 1115 | if callid then 1116 | local calls = exports['ps-dispatch']:GetDispatchCalls() 1117 | TriggerClientEvent('mdt:client:setWaypoint', src, calls[callid]) 1118 | end 1119 | end 1120 | 1121 | end) 1122 | 1123 | RegisterNetEvent('mdt:server:callDragAttach', function(callid, cid) 1124 | local src = source 1125 | local Player = QBCore.Functions.GetPlayer(src) 1126 | local playerdata = { 1127 | name = Player.PlayerData.charinfo.firstname.. " "..Player.PlayerData.charinfo.lastname, 1128 | job = Player.PlayerData.job.name, 1129 | cid = Player.PlayerData.citizenid, 1130 | callsign = Player.PlayerData.metadata.callsign 1131 | } 1132 | local callid = tonumber(callid) 1133 | local JobType = GetJobType(Player.PlayerData.job.name) 1134 | if JobType == 'police' or JobType == 'ambulance' then 1135 | if callid then 1136 | TriggerEvent('dispatch:addUnit', callid, playerdata, function(newNum) 1137 | TriggerClientEvent('mdt:client:callAttach', -1, callid, newNum) 1138 | end) 1139 | end 1140 | end 1141 | end) 1142 | 1143 | RegisterNetEvent('mdt:server:setWaypoint:unit', function(cid) 1144 | local src = source 1145 | local Player = QBCore.Functions.GetPlayerByCitizenId(cid) 1146 | local PlayerCoords = GetEntityCoords(GetPlayerPed(Player.PlayerData.source)) 1147 | TriggerClientEvent("mdt:client:setWaypoint:unit", src, PlayerCoords) 1148 | end) 1149 | 1150 | -- Dispatch chat 1151 | 1152 | RegisterNetEvent('mdt:server:sendMessage', function(message, time) 1153 | if message and time then 1154 | local src = source 1155 | local Player = QBCore.Functions.GetPlayer(src) 1156 | if Player then 1157 | MySQL.scalar("SELECT pfp FROM `mdt_data` WHERE cid=:id LIMIT 1", { 1158 | id = Player.PlayerData.citizenid -- % wildcard, needed to search for all alike results 1159 | }, function(data) 1160 | if data == "" then data = nil end 1161 | local ProfilePicture = ProfPic(Player.PlayerData.charinfo.gender, data) 1162 | local callsign = Player.PlayerData.metadata.callsign or "000" 1163 | local Item = { 1164 | profilepic = ProfilePicture, 1165 | callsign = Player.PlayerData.metadata.callsign, 1166 | cid = Player.PlayerData.citizenid, 1167 | name = '('..callsign..') '..Player.PlayerData.charinfo.firstname.. " "..Player.PlayerData.charinfo.lastname, 1168 | message = message, 1169 | time = time, 1170 | job = Player.PlayerData.job.name 1171 | } 1172 | dispatchMessages[#dispatchMessages+1] = Item 1173 | TriggerClientEvent('mdt:client:dashboardMessage', -1, Item) 1174 | -- Send to all clients, for auto updating stuff, ya dig. 1175 | end) 1176 | end 1177 | end 1178 | end) 1179 | 1180 | RegisterNetEvent('mdt:server:refreshDispatchMsgs', function() 1181 | local src = source 1182 | local PlayerData = GetPlayerData(src) 1183 | if IsJobAllowedToMDT(PlayerData.job.name) then 1184 | TriggerClientEvent('mdt:client:dashboardMessages', src, dispatchMessages) 1185 | end 1186 | end) 1187 | 1188 | RegisterNetEvent('mdt:server:getCallResponses', function(callid) 1189 | local src = source 1190 | local Player = QBCore.Functions.GetPlayer(src) 1191 | if IsPolice(Player.PlayerData.job.name) then 1192 | local calls = exports['ps-dispatch']:GetDispatchCalls() 1193 | TriggerClientEvent('mdt:client:getCallResponses', src, calls[callid]['responses'], callid) 1194 | end 1195 | end) 1196 | 1197 | RegisterNetEvent('mdt:server:sendCallResponse', function(message, time, callid) 1198 | local src = source 1199 | local Player = QBCore.Functions.GetPlayer(src) 1200 | local name = Player.PlayerData.charinfo.firstname.. " "..Player.PlayerData.charinfo.lastname 1201 | if IsPolice(Player.PlayerData.job.name) then 1202 | TriggerEvent('dispatch:sendCallResponse', src, callid, message, time, function(isGood) 1203 | if isGood then 1204 | TriggerClientEvent('mdt:client:sendCallResponse', -1, message, time, callid, name) 1205 | end 1206 | end) 1207 | end 1208 | end) 1209 | 1210 | RegisterNetEvent('mdt:server:setRadio', function(cid, newRadio) 1211 | local src = source 1212 | local Player = QBCore.Functions.GetPlayer(src) 1213 | if Player.PlayerData.citizenid ~= cid then 1214 | TriggerClientEvent("QBCore:Notify", src, 'You can only change your radio!', 'error') 1215 | return 1216 | else 1217 | local radio = Player.Functions.GetItemByName("radio") 1218 | if radio ~= nil then 1219 | TriggerClientEvent('mdt:client:setRadio', src, newRadio) 1220 | else 1221 | TriggerClientEvent("QBCore:Notify", src, 'You do not have a radio!', 'error') 1222 | end 1223 | end 1224 | 1225 | end) 1226 | 1227 | local function isRequestVehicle(vehId) 1228 | local found = false 1229 | for i=1, #impound do 1230 | if impound[i]['vehicle'] == vehId then 1231 | found = true 1232 | impound[i] = nil 1233 | break 1234 | end 1235 | end 1236 | return found 1237 | end 1238 | exports('isRequestVehicle', isRequestVehicle) -- exports['erp_mdt']:isRequestVehicle() 1239 | 1240 | RegisterNetEvent('mdt:server:impoundVehicle', function(sentInfo, sentVehicle) 1241 | local src = source 1242 | local Player = QBCore.Functions.GetPlayer(src) 1243 | if Player then 1244 | if GetJobType(Player.PlayerData.job.name) == 'police' then 1245 | if sentInfo and type(sentInfo) == 'table' then 1246 | local plate, linkedreport, fee, time = sentInfo['plate'], sentInfo['linkedreport'], sentInfo['fee'], sentInfo['time'] 1247 | if (plate and linkedreport and fee and time) then 1248 | local vehicle = MySQL.query.await("SELECT id, plate FROM `player_vehicles` WHERE plate=:plate LIMIT 1", { plate = string.gsub(plate, "^%s*(.-)%s*$", "%1") }) 1249 | if vehicle and vehicle[1] then 1250 | local data = vehicle[1] 1251 | MySQL.insert('INSERT INTO `mdt_impound` (`vehicleid`, `linkedreport`, `fee`, `time`) VALUES (:vehicleid, :linkedreport, :fee, :time)', { 1252 | vehicleid = data['id'], 1253 | linkedreport = linkedreport, 1254 | fee = fee, 1255 | time = os.time() + (time * 60) 1256 | }, function(res) 1257 | local data = { 1258 | vehicleid = data['id'], 1259 | plate = plate, 1260 | beingcollected = 0, 1261 | vehicle = sentVehicle, 1262 | officer = Player.PlayerData.charinfo.firstname.. " "..Player.PlayerData.charinfo.lastname, 1263 | number = Player.PlayerData.charinfo.phone, 1264 | time = os.time() * 1000, 1265 | src = src, 1266 | } 1267 | local vehicle = NetworkGetEntityFromNetworkId(sentVehicle) 1268 | FreezeEntityPosition(vehicle, true) 1269 | impound[#impound+1] = data 1270 | 1271 | TriggerClientEvent("police:client:ImpoundVehicle", src, true, fee) 1272 | end) 1273 | end 1274 | end 1275 | end 1276 | end 1277 | end 1278 | end) 1279 | 1280 | RegisterNetEvent('mdt:server:getImpoundVehicles', function() 1281 | TriggerClientEvent('mdt:client:getImpoundVehicles', source, impound) 1282 | end) 1283 | 1284 | RegisterNetEvent('mdt:server:removeImpound', function(plate, currentSelection) 1285 | print("Removing impound", plate, currentSelection) 1286 | local src = source 1287 | local Player = QBCore.Functions.GetPlayer(src) 1288 | if Player then 1289 | if GetJobType(Player.PlayerData.job.name) == 'police' then 1290 | local result = MySQL.single.await("SELECT id, vehicle FROM `player_vehicles` WHERE plate=:plate LIMIT 1", { plate = string.gsub(plate, "^%s*(.-)%s*$", "%1")}) 1291 | if result and result[1] then 1292 | local data = result[1] 1293 | MySQL.update("DELETE FROM `mdt_impound` WHERE vehicleid=:vehicleid", { vehicleid = data['id'] }) 1294 | TriggerClientEvent('police:client:TakeOutImpound', src, currentSelection) 1295 | end 1296 | end 1297 | end 1298 | end) 1299 | 1300 | RegisterNetEvent('mdt:server:statusImpound', function(plate) 1301 | local src = source 1302 | local Player = QBCore.Functions.GetPlayer(src) 1303 | if Player then 1304 | if GetJobType(Player.PlayerData.job.name) == 'police' then 1305 | local vehicle = MySQL.query.await("SELECT id, plate FROM `player_vehicles` WHERE plate=:plate LIMIT 1", { plate = string.gsub(plate, "^%s*(.-)%s*$", "%1")}) 1306 | if vehicle and vehicle[1] then 1307 | local data = vehicle[1] 1308 | local impoundinfo = MySQL.query.await("SELECT * FROM `mdt_impound` WHERE vehicleid=:vehicleid LIMIT 1", { vehicleid = data['id'] }) 1309 | if impoundinfo and impoundinfo[1] then 1310 | TriggerClientEvent('mdt:client:statusImpound', src, impoundinfo[1], plate) 1311 | end 1312 | end 1313 | end 1314 | end 1315 | end) 1316 | 1317 | RegisterServerEvent("mdt:server:AddLog", function(text) 1318 | AddLog(text) 1319 | end) 1320 | 1321 | function GetBoloStatus(plate) 1322 | local result = MySQL.query.await("SELECT * FROM mdt_bolos where plate = @plate", {['@plate'] = plate}) 1323 | if result and result[1] then 1324 | return true 1325 | end 1326 | 1327 | return false 1328 | end 1329 | 1330 | function GetVehicleInformation(plate) 1331 | local result = MySQL.query.await('SELECT * FROM mdt_vehicleinfo WHERE plate = @plate', {['@plate'] = plate}) 1332 | if result[1] then 1333 | return result[1] 1334 | else 1335 | return false 1336 | end 1337 | end 1338 | 1339 | --------------------------------------------------------------------------------