├── item.sql
├── html
├── fonts
│ ├── Dosis-Medium.ttf
│ └── SquadaOne-Regular.ttf
├── ui.html
├── js
│ └── script.js
└── css
│ └── style.css
├── README.md
├── fxmanifest.lua
├── config.lua
├── client
└── plate.lua
└── server
└── plate.lua
/item.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO `items` (`name`, `label`, `weight`) VALUES
2 | ('licenseplate', 'License Plate', 1)
3 | ;
--------------------------------------------------------------------------------
/html/fonts/Dosis-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EntityEvolution/ev-platechanger/HEAD/html/fonts/Dosis-Medium.ttf
--------------------------------------------------------------------------------
/html/fonts/SquadaOne-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EntityEvolution/ev-platechanger/HEAD/html/fonts/SquadaOne-Regular.ttf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ev-platechanger
2 | A simple NUI plate changer.
3 |
4 | # LICENSE
5 | Feel free to do whatever you want.
6 |
7 | ### [Forum Post](https://forum.cfx.re/t/release-ev-platechanger/4749084)
8 | ### [Discord](https://discord.com/invite/u4zk4tVTkG)
9 | ### [Donations](https://www.buymeacoffee.com/bombayV)
10 |
11 |
12 | ### [Preview](https://c.fileglass.com/59bd7.png)
13 | ### [Video](https://youtu.be/yaS4xfmHQ1Y)
14 |
--------------------------------------------------------------------------------
/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | fx_version 'cerulean'
2 |
3 | game 'gta5'
4 |
5 | lua54 'yes'
6 |
7 | description 'A simple NUI plate creator by Entity Evolution'
8 |
9 | version '2.0.0'
10 |
11 | shared_scripts {
12 | 'config.lua',
13 | '@es_extended/imports.lua'
14 | --'@qb-core/import.lua'
15 | }
16 |
17 | client_scripts {
18 | 'client/*.lua'
19 | }
20 |
21 | server_scripts {
22 | '@mysql-async/lib/MySQL.lua', -- Remove if using QBCore
23 | 'server/*.lua'
24 | }
25 |
26 | ui_page 'html/ui.html'
27 |
28 | files {
29 | 'html/ui.html',
30 | 'html/fonts/*.ttf',
31 | 'html/css/style.css',
32 | 'html/js/script.js'
33 | }
34 |
--------------------------------------------------------------------------------
/config.lua:
--------------------------------------------------------------------------------
1 | Config = {}
2 | Config.JsKey = "Insert"
3 | Config.PlateHeader = "SAN ANDREAS"
4 | Config.EightChars = true
5 | Config.useButtons = true
6 | Config.MaxChars = 8 -- Max is 8
7 |
8 | Config.Locales = {
9 | Error = 'Something weird occured',
10 | ErrorVehicle = 'You somehow left your vehicle!',
11 | ErrorDriver = 'You need to be the driver',
12 | ErrorWalking = 'You are not in a vehicle',
13 | ErrorOwner = 'This is not your vehicle',
14 | ErrorPlate = 'Plate already exists',
15 | ErrorPlateReal = 'Your vehicle appears to be missing a real plate',
16 | ErrorCharsMin = 'Plate needs at least 1 character',
17 | ErrorCharsMax = 'Somehow you passed more than 6 chars',
18 | NewPlate = 'Your new plate has been set!'
19 | }
20 |
21 | Config.Blacklist = {
22 | 'NIGGER',
23 | 'NIGGA',
24 | 'NEGRO'
25 | }
--------------------------------------------------------------------------------
/html/ui.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Plate Changer
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
SAN ANDREAS
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/html/js/script.js:
--------------------------------------------------------------------------------
1 | const doc = document;
2 | const plate = doc.getElementById('wrapper');
3 | let currentKey = "";
4 | let usingButtons = false;
5 |
6 | this.window.addEventListener('load', e => {
7 | window.addEventListener('message', e => {
8 | switch (e.data.action) {
9 | case 'show':
10 | plate.style.display = 'flex';
11 | plate.style.opacity = '1';
12 | if (!usingButtons) {
13 | doc.getElementById('btns').style.display = 'none';
14 | }
15 | break;
16 |
17 | case 'hide':
18 | plate.style.display = 'none';
19 | plate.style.opacity = '0';
20 | if (!usingButtons) {
21 | doc.getElementById('btns').style.display = 'none';
22 | }
23 | break;
24 |
25 | case 'key':
26 | usingButtons = e.data.buttons;
27 | currentKey = e.data.key;
28 | doc.getElementById('title').textContent = e.data.title;
29 | if (e.data.chars) {
30 | document.getElementById("text").maxLength = '8'
31 | document.getElementsByName('text')[0].placeholder='ABCD1234';
32 | }
33 | break;
34 | }
35 | })
36 | })
37 |
38 | doc.getElementById('one').addEventListener('click', () => fetchNUI('getPlateText', doc.getElementById('text').value.toUpperCase()));
39 |
40 | doc.getElementById('two').addEventListener('click', () => fetchNUI('close'));
41 |
42 | const fetchNUI = async (cbname, data) => {
43 | const options = {
44 | method: 'POST',
45 | headers: {
46 | 'Content-Type': 'application/json; charset=UTF-8'
47 | },
48 | body: JSON.stringify(data)
49 | };
50 | const resp = await fetch(`https://ev-platechanger/${cbname}`, options);
51 | return await resp.json();
52 | }
53 |
54 | doc.onkeyup = e => {
55 | if (e.key == currentKey) {
56 | const inp = doc.getElementById('text');
57 | fetchNUI('getPlateText', inp.value.toUpperCase());
58 | } else if (e.key == 'Escape') {
59 | plate.style.opacity = '0';
60 | fetchNUI('close');
61 | }
62 | }
--------------------------------------------------------------------------------
/html/css/style.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: def;
3 | src: url(../fonts/SquadaOne-Regular.ttf);
4 | font-display: swap;
5 | }
6 |
7 | @font-face {
8 | font-family: def2;
9 | src: url(../fonts/Dosis-Medium.ttf);
10 | font-display: swap;
11 | }
12 |
13 | *:focus {
14 | outline: none;
15 | }
16 |
17 | body {
18 | overflow: hidden;
19 | }
20 |
21 | .wrapper {
22 | position: absolute;
23 | display: flex;
24 | justify-content: center;
25 | align-items: center;
26 | flex-direction: column;
27 | top: 0;
28 | left: 0;
29 | width: 100%;
30 | height: 100%;
31 | opacity: 0;
32 | }
33 |
34 | .container {
35 | position: relative;
36 | display: flex;
37 | justify-content: space-between;
38 | align-items: center;
39 | flex-direction: column;
40 | width: 28%;
41 | height: 22%;
42 | border-radius: 1.0vh;
43 | border: 1.2vh solid #373737;
44 |
45 | background-color: #dddddd;
46 | }
47 |
48 | .container span {
49 | position: relative;
50 | display: flex;
51 | justify-content: space-around;
52 | align-items: center;
53 | font-family: def;
54 | text-transform: uppercase;
55 | font-size: 5.0vh;
56 | margin-top: 1.0%;
57 | width: 100%;
58 | height: 15%;
59 | }
60 |
61 | .input-text {
62 | display: flex;
63 | justify-content: center;
64 | align-content: center;
65 | text-align: center;
66 | font-size: 10vh;
67 | width: 90%;
68 | height: 50%;
69 | border: none;
70 | background-color: #dddddd;
71 | margin-bottom: 5%;
72 | font-family: def2;
73 | text-transform: uppercase;
74 | }
75 |
76 | .btn-container {
77 | display: flex;
78 | justify-content: space-evenly;
79 | align-items: center;
80 | width: 28%;
81 | height: 8%;
82 |
83 | margin-top: 0.5%;
84 | }
85 |
86 | .btn-container button {
87 | width: 40%;
88 | height: 70%;
89 |
90 | border: none;
91 | border-radius: 0.5vh;
92 | box-shadow:0 0.3vh 0.4vh #00000058, 0 0 20vh #0000001a;
93 |
94 | font-family: def2;
95 | font-size: 2.5vh;
96 | font-weight: 700;
97 | background-color: #DBDBDB;
98 | transition: 0.2s;
99 | }
100 |
101 | .btn-container button:nth-of-type(1):hover {
102 | background-color: rgb(33, 131, 221);
103 | }
104 |
105 | .btn-container button:nth-of-type(2):hover {
106 | background-color: rgb(233, 29, 56);
107 | }
--------------------------------------------------------------------------------
/client/plate.lua:
--------------------------------------------------------------------------------
1 | local stateEsx = GetResourceState('es_extended') == 'started' or GetResourceState('extendedmode') == 'started'
2 | local stateQbus = GetResourceState('qb-core') == 'started'
3 | local isOpen = false
4 |
5 | local function showNoti(message)
6 | if stateEsx then
7 | ESX.ShowNotification(message)
8 | elseif stateQbus then
9 | QBCore.Functions.Notify(message)
10 | end
11 | end
12 |
13 | -- NUI Callback
14 | RegisterNUICallback('getPlateText', function(data, cb)
15 | if isOpen then
16 | local ped = PlayerPedId()
17 | if IsPedInAnyVehicle(ped, false) then
18 | if GetPedInVehicleSeat(GetVehiclePedIsIn(ped, false), -1) == ped then
19 | if data then
20 | for i=0, #Config.Blacklist, 1 do
21 | if Config.Blacklist[i] == data then
22 | return showNoti('You tried to set a plate with a bad word: ' .. data)
23 | end
24 | end
25 | if data:len() > 0 then
26 | SendNUIMessage({action = 'hide'})
27 | SetNuiFocus(0, 0)
28 | TriggerServerEvent('ev:getPlate', data, GetVehicleNumberPlateText(GetVehiclePedIsIn(ped, false)):match( "^%s*(.-)%s*$" ))
29 | isOpen = false
30 | else
31 | showNoti(Config.Locales.ErrorCharsMin)
32 | end
33 | else
34 | showNoti(Config.Locales.Error)
35 | end
36 | else
37 | showNoti(Config.Locales.ErrorDriver)
38 | end
39 | else
40 | showNoti(Config.Locales.ErrorVehicle)
41 | end
42 | end
43 | cb({})
44 | end)
45 |
46 | RegisterNUICallback('close', function(_, cb)
47 | if isOpen then
48 | isOpen = false
49 | SendNUIMessage({action = 'hide'})
50 | SetNuiFocus(0, 0)
51 | end
52 | cb({})
53 | end)
54 |
55 | -- Events
56 | RegisterNetEvent('ev:getPlateNui', function()
57 | if not isOpen then
58 | local ped = PlayerPedId()
59 | if IsPedInAnyVehicle(ped, false) then
60 | if GetPedInVehicleSeat(GetVehiclePedIsIn(ped, false), -1) == ped then
61 | isOpen = true
62 | SendNUIMessage({action = 'show'})
63 | SetNuiFocus(1, 1)
64 | else
65 | showNoti(Config.Locales.ErrorDriver)
66 | end
67 | else
68 | showNoti(Config.Locales.ErrorWalking)
69 | end
70 | end
71 | end)
72 |
73 | --Handlers
74 | AddEventHandler('playerSpawned', function()
75 | Wait(3000)
76 | SendNUIMessage({
77 | action = 'key',
78 | key = Config.JsKey,
79 | title = Config.PlateHeader,
80 | chars = Config.EightChars,
81 | buttons = Config.useButtons
82 | })
83 | end)
84 |
85 | AddEventHandler('onClientResourceStart', function(resourceName)
86 | if resourceName == GetCurrentResourceName() then
87 | Wait(3000)
88 | SendNUIMessage({
89 | action = 'key',
90 | key = Config.JsKey,
91 | title = Config.PlateHeader,
92 | chars = Config.EightChars,
93 | buttons = Config.useButtons
94 | })
95 | end
96 | end)
--------------------------------------------------------------------------------
/server/plate.lua:
--------------------------------------------------------------------------------
1 | local stateEsx = GetResourceState('es_extended') == 'started' or GetResourceState('extendedmode') == 'started'
2 | local stateQbus = GetResourceState('qb-core') == 'started'
3 |
4 | if stateEsx then
5 | RegisterNetEvent('ev:getPlate', function(plate, currentPlate)
6 | local source = source
7 | local xPlayer = ESX.GetPlayerFromId(source)
8 | if xPlayer then
9 | if GetVehiclePedIsIn(GetPlayerPed(source)) == 0 or not currentPlate then
10 | return xPlayer.showNotification(Config.Locales.ErrorVehicle)
11 | elseif plate:len() > 6 then
12 | return xPlayer.showNotification(Config.Locales.ErrorCharsMax)
13 | end
14 | for i=0, #Config.Blacklist, 1 do
15 | if Config.Blacklist[i] == plate then
16 | return xPlayer.showNotification('You tried to set a plate with a bad word: ' .. plate)
17 | end
18 | end
19 | MySQL.Async.fetchScalar('SELECT plate FROM owned_vehicles WHERE owner = @owner AND plate = @plate', {
20 | ['owner'] = xPlayer.identifier,
21 | ['plate'] = currentPlate
22 | }, function(resultPlate)
23 | if not resultPlate then
24 | return xPlayer.showNotification(Config.Locales.ErrorOwner)
25 | else
26 | MySQL.Async.fetchScalar('SELECT * FROM owned_vehicles WHERE plate = @plate', {
27 | ['plate'] = plate
28 | }, function(result)
29 | if not result then
30 | MySQL.Async.fetchAll('SELECT plate, vehicle FROM owned_vehicles WHERE plate = @plate', {
31 | ['plate'] = currentPlate
32 | },function(result)
33 | if result[1] then
34 | local vehicle = json.decode(result[1].vehicle)
35 | if not vehicle.plate then
36 | return xPlayer.showNotification(Config.Locales.ErrorPlateReal)
37 | end
38 | local oldPlate = vehicle.plate
39 | vehicle.plate = plate
40 | MySQL.Async.execute('UPDATE owned_vehicles SET plate = @newplate, vehicle = @vehicle WHERE plate = @oldplate AND owner=@identifier', {
41 | ['newplate'] = plate,
42 | ['oldplate'] = oldPlate,
43 | ['identifier'] = xPlayer.identifier,
44 | ['vehicle'] = json.encode(vehicle)
45 | })
46 | SetVehicleNumberPlateText(GetVehiclePedIsIn(GetPlayerPed(source)), plate)
47 | xPlayer.removeInventoryItem('licenseplate', 1)
48 | xPlayer.showNotification(Config.Locales.NewPlate)
49 | return
50 | end
51 | end)
52 | else
53 | xPlayer.showNotification(Config.Locales.ErrorPlate)
54 | end
55 | end)
56 | end
57 | end)
58 | end
59 | end)
60 |
61 | ESX.RegisterUsableItem('licenseplate', function(source)
62 | local source = source
63 | local xPlayer = ESX.GetPlayerFromId(source)
64 | local vehicle = GetVehiclePedIsIn(GetPlayerPed(source))
65 | if vehicle ~= 0 then
66 | MySQL.Async.fetchScalar('SELECT plate FROM owned_vehicles WHERE owner = @owner AND plate = @plate', {
67 | ['owner'] = xPlayer.identifier,
68 | ['plate'] = GetVehicleNumberPlateText(vehicle):match( "^%s*(.-)%s*$" )
69 | }, function(plate)
70 | if plate then
71 | TriggerClientEvent('ev:getPlateNui', source)
72 | else
73 | xPlayer.showNotification(Config.Locales.ErrorOwner)
74 | end
75 | end)
76 | else
77 | xPlayer.showNotification(Config.Locales.ErrorWalking)
78 | end
79 | end)
80 | elseif stateQbus then
81 | RegisterNetEvent('ev:getPlate', function(plate, currentPlate)
82 | local source = source
83 | local xPlayer = QBCore.Functions.GetPlayer(source)
84 | if xPlayer then
85 | if GetVehiclePedIsIn(GetPlayerPed(source)) == 0 or not currentPlate then
86 | return TriggerClientEvent('QBCore:Notify', source, Config.Locales.ErrorVehicle)
87 | elseif plate:len() > Config.MaxChars then
88 | return TriggerClientEvent('QBCore:Notify', source, Config.Locales.ErrorCharsMax)
89 | end
90 | for i=0, #Config.Blacklist, 1 do
91 | if Config.Blacklist[i] == plate then
92 | return TriggerClientEvent('QBCore:Notify', source, 'You tried to set a plate with a bad word: ' .. plate)
93 | end
94 | end
95 | exports.oxmysql:fetch('SELECT plate FROM player_vehicles WHERE plate = @plate AND citizenid = @citizenid', {
96 | ['plate'] = currentPlate,
97 | ['citizenid'] = xPlayer.PlayerData.citizenid
98 | }, function(result)
99 | if result[1] then
100 | exports.oxmysql:fetch('SELECT * FROM player_vehicles WHERE plate = @plate', {
101 | ['plate'] = plate
102 | }, function(exist)
103 | if not exist[1] then
104 | exports.oxmysql:fetch('SELECT plate, mods FROM player_vehicles WHERE plate = @plate', {
105 | ['plate'] = currentPlate
106 | },function(currentVehicle)
107 | if currentVehicle[1] then
108 | local vehicle = json.decode(currentVehicle[1].mods)
109 | if not vehicle.plate then
110 | return TriggerClientEvent('QBCore:Notify', source, Config.Locales.ErrorPlateReal)
111 | end
112 | local oldPlate = vehicle.plate
113 | vehicle.plate = plate
114 | exports.oxmysql:execute('UPDATE player_vehicles SET plate = @newplate, mods = @vehicle WHERE plate = @oldplate AND citizenid=@citizenid', {
115 | ['newplate'] = plate,
116 | ['oldplate'] = oldPlate,
117 | ['citizenid'] = xPlayer.PlayerData.citizenid,
118 | ['vehicle'] = json.encode(vehicle)
119 | })
120 | SetVehicleNumberPlateText(GetVehiclePedIsIn(GetPlayerPed(source)), plate)
121 | xPlayer.Functions.RemoveItem('licenseplate', 1)
122 | TriggerClientEvent('QBCore:Notify', source, Config.Locales.NewPlate)
123 | return
124 | end
125 | end)
126 | else
127 | TriggerClientEvent('QBCore:Notify', source, Config.Locales.ErrorPlate)
128 | end
129 | end)
130 | else
131 | TriggerClientEvent('QBCore:Notify', source, Config.Locales.ErrorOwner)
132 | end
133 | end)
134 | end
135 | end)
136 |
137 | QBCore.Functions.CreateUseableItem('licenseplate', function(source)
138 | local source = source
139 | local xPlayer = QBCore.Functions.GetPlayer(source)
140 | local vehicle = GetVehiclePedIsIn(GetPlayerPed(source))
141 | if vehicle ~= 0 then
142 | exports.oxmysql:fetch('SELECT plate FROM player_vehicles WHERE plate = @plate AND citizenid = @citizenid', {
143 | ['plate'] = GetVehicleNumberPlateText(vehicle):match( "^%s*(.-)%s*$" ),
144 | ['citizenid'] = xPlayer.PlayerData.citizenid
145 | }, function(result)
146 | if result[1] then
147 | TriggerClientEvent('ev:getPlateNui', source)
148 | else
149 | TriggerClientEvent('QBCore:Notify', source, Config.Locales.ErrorOwner)
150 | end
151 | end)
152 | else
153 | TriggerClientEvent('QBCore:Notify', source, Config.Locales.ErrorWalking)
154 | end
155 | end)
156 | end
--------------------------------------------------------------------------------