diff --git a/README.md b/README.md index 2a2878fd..185d67a3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,16 @@ # fxserver-esx_phone FXServer esx_phone + +[INSTALLATION] + +1) CD in your resources folder + +2) +``` +git clone https://github.com/FXServer-ESX/fxserver-esx_phone +``` +3) Add this in your server.cfg : + +``` +start esx_phone +``` \ No newline at end of file diff --git a/__resource.lua b/__resource.lua new file mode 100644 index 00000000..c7e09045 --- /dev/null +++ b/__resource.lua @@ -0,0 +1,35 @@ +resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937' + +description 'ESX Phone' + +server_scripts { + '@mysql-async/lib/MySQL.lua', + 'server/main.lua' +} + +client_script 'client/main.lua' + +ui_page 'html/ui.html' + +files { + 'html/ui.html', + 'html/bankgothic.ttf', + 'html/pdown.ttf', + 'html/css/app.css', + 'html/scripts/mustache.min.js', + 'html/scripts/app.js', + 'html/img/cursor.png', + 'html/img/keys/enter.png', + 'html/img/keys/return.png', + 'html/img/phone.png', + 'html/img/icons/signal.png', + 'html/img/icons/rep.png', + 'html/img/icons/msg.png', + 'html/img/icons/add.png', + 'html/img/icons/back.png', + 'html/img/icons/new-msg.png', + 'html/img/icons/reply.png', + 'html/img/icons/write.png', + 'html/img/icons/edit.png', + 'html/img/icons/location.png' +} \ No newline at end of file diff --git a/client/main.lua b/client/main.lua new file mode 100644 index 00000000..73dc6121 --- /dev/null +++ b/client/main.lua @@ -0,0 +1,265 @@ +local Keys = { + ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57, + ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177, + ["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18, + ["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182, + ["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81, + ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70, + ["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178, + ["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173, + ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118 +} + +ESX = nil +local GUI = {} +GUI.Time = 0 +GUI.PhoneIsShowed = false +GUI.MessagesIsShowed = false +GUI.AddContactIsShowed = false +local PhoneData = {phoneNumber = 0, contacts = {}} +local RegisteredMessageCallbacks = {} +local ContactJustAdded = false +local CurrentAction = nil +local CurrentActionMsg = '' +local CurrentActionData = {} +local CurrentDispatchRequestId = -1 + +Citizen.CreateThread(function() + + while ESX == nil do + TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) + Citizen.Wait(0) + end + + ESX.UI.Menu.RegisterType('phone', OpenPhone, ClosePhone) + +end) + +function OpenPhone() + + TriggerServerEvent('esx_phone:reload', PhoneData.phoneNumber) + + SendNUIMessage({ + showPhone = true, + phoneData = PhoneData + }) + + GUI.PhoneIsShowed = true + + ESX.SetTimeout(200, function() + SetNuiFocus(true, true) + end) + +end + +function ClosePhone() + + SendNUIMessage({ + showPhone = false + }) + + SetNuiFocus(false) + + GUI.PhoneIsShowed = false + +end + +RegisterNetEvent('esx_phone:loaded') +AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) + + PhoneData.phoneNumber = phoneNumber + PhoneData.contacts = {} + + for i=1, #contacts, 1 do + table.insert(PhoneData.contacts, contacts[i]) + end + + SendNUIMessage({ + reloadPhone = true, + phoneData = PhoneData + }) + +end) + +RegisterNetEvent('esx_phone:addContact') +AddEventHandler('esx_phone:addContact', function(name, phoneNumber, isOnline) + + table.insert(PhoneData.contacts, { + name = name, + number = phoneNumber, + online = isOnline + }) + -- CALL HERE RELOADCONTACT + SendNUIMessage({ + contactAdded = true, + phoneData = PhoneData + }) +end) + +RegisterNetEvent('esx_phone:addSpecialContact') +AddEventHandler('esx_phone:addSpecialContact', function(name, phoneNumber, base64Icon) + + SendNUIMessage({ + addSpecialContact = true, + name = name, + number = phoneNumber, + base64Icon = base64Icon + }) + +end) + +RegisterNUICallback('add_contact', function(data, cb) + + local phoneNumber = tonumber(data.phoneNumber) + local contactName = tostring(data.contactName) + + if phoneNumber then + TriggerServerEvent('esx_phone:addPlayerContact', phoneNumber, contactName) + end + +end) + + +RegisterNetEvent('esx_phone:onMessage') +AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId) + + ESX.ShowNotification('~b~Nouveau message') + + SendNUIMessage({ + newMessage = true, + phoneNumber = phoneNumber, + message = message, + position = position, + anonyme = anon, + job = job + }) + + if dispatchRequestId then + + CurrentAction = 'dispatch' + CurrentActionMsg = job .. ' - Appuez sur ~INPUT_CONTEXT~ pour prendre l\'appel' + CurrentDispatchRequestId = dispatchRequestId + + CurrentActionData = { + phoneNumber = phoneNumber, + message = message, + position = position, + actions = actions, + anonyme = anon, + job = job + } + + ESX.SetTimeout(15000, function() + CurrentAction = nil + end) + + end + +end) + +RegisterNetEvent('esx_phone:stopDispatch') +AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId, playerName) + + if CurrentDispatchRequestId == dispatchRequestId and CurrentAction == 'dispatch' then + CurrentAction = nil + ESX.ShowNotification(playerName .. ' a pris l\'appel') + end + +end) + +RegisterNUICallback('setGPS', function(data) + SetNewWaypoint(data.x, data.y) + ESX.ShowNotification('Position entrée dans le GPS') +end) + +RegisterNUICallback('send', function(data) + + local phoneNumber = data.number + + if tonumber(phoneNumber) ~= nil then + phoneNumber = tonumber(phoneNumber) + end + + TriggerServerEvent('esx_phone:send', phoneNumber, data.message, data.anonyme) + + SendNUIMessage({ + showMessageEditor = false + }) + + ESX.ShowNotification('Message envoyé') + +end) + +RegisterNUICallback('escape', function() + ESX.UI.Menu.Close('phone', GetCurrentResourceName(), 'main') +end) + +Citizen.CreateThread(function() + while true do + + Wait(0) + + if GUI.PhoneIsShowed then -- codes here: https://pastebin.com/guYd0ht4 + DisableControlAction(0, 1, true) -- LookLeftRight + DisableControlAction(0, 2, true) -- LookUpDown + DisableControlAction(0, 25, true) -- Input Aim + DisableControlAction(0, 106, true) -- Vehicle Mouse Control Override + + DisableControlAction(0, 24, true) -- Input Attack + DisableControlAction(0, 140, true) -- Melee Attack Alternate + DisableControlAction(0, 141, true) -- Melee Attack Alternate + DisableControlAction(0, 142, true) -- Melee Attack Alternate + DisableControlAction(0, 257, true) -- Input Attack 2 + DisableControlAction(0, 263, true) -- Input Melee Attack + DisableControlAction(0, 264, true) -- Input Melee Attack 2 + + DisableControlAction(0, 12, true) -- Weapon Wheel Up Down + DisableControlAction(0, 14, true) -- Weapon Wheel Next + DisableControlAction(0, 15, true) -- Weapon Wheel Prev + DisableControlAction(0, 16, true) -- Select Next Weapon + DisableControlAction(0, 17, true) -- Select Prev Weapon + else + + if IsControlPressed(0, Keys['F1']) and (GetGameTimer() - GUI.Time) > 150 then + + if not ESX.UI.Menu.IsOpen('phone', GetCurrentResourceName(), 'main') then + ESX.UI.Menu.CloseAll() + ESX.UI.Menu.Open('phone', GetCurrentResourceName(), 'main') + end + + GUI.Time = GetGameTimer() + + end + + end + end +end) + +-- Key controls +Citizen.CreateThread(function() + while true do + + Citizen.Wait(0) + + if CurrentAction ~= nil then + + SetTextComponentFormat('STRING') + AddTextComponentString(CurrentActionMsg) + DisplayHelpTextFromStringLabel(0, 0, 1, -1) + + if IsControlPressed(0, Keys['E']) and (GetGameTimer() - GUI.Time) > 300 then + + if CurrentAction == 'dispatch' then + TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) + SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) + end + + CurrentAction = nil + GUI.Time = GetGameTimer() + + end + + end + + end +end) \ No newline at end of file diff --git a/esx_phone.sql b/esx_phone.sql new file mode 100644 index 00000000..19d3b255 --- /dev/null +++ b/esx_phone.sql @@ -0,0 +1,11 @@ +CREATE TABLE `user_contacts` ( + `id` int(11) NOT NULL, + `identifier` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, + `number` int(11) NOT NULL +); + +ALTER TABLE `user_contacts` ADD PRIMARY KEY (`id`); + +ALTER TABLE `users` +ADD COLUMN `phone_number` INT NULL AFTER `position`; diff --git a/html/bankgothic.ttf b/html/bankgothic.ttf new file mode 100644 index 00000000..f3d8049b Binary files /dev/null and b/html/bankgothic.ttf differ diff --git a/html/css/app.css b/html/css/app.css new file mode 100644 index 00000000..d29c0273 --- /dev/null +++ b/html/css/app.css @@ -0,0 +1,392 @@ +html, body { + width: 100%; + height: 100%; + overflow: hidden; +} + +#cursor { + position: absolute; + z-index: 999999; + display: none; +} + +#console { + position: absolute; + left: 0; + bottom: 0; + width: 320px; + height: 250px; + background-color: rgba(0, 0, 0, 0.8); + color: white; + padding: 5px; + overflow-y: auto; + overflow-x: hidden; +} + +#phone { + background-image: url(../img/phone.png); + background-repeat: no-repeat; + font-family: 'Catamaran', sans-serif; + font-weight: 400; + font-size: 12px; + width: 298px; + height: 480px; + color: #212121; + position: absolute; + right: 300px; + bottom: 0; + overflow: hidden; + cursor: default; + display: none; +} + +#phone > .container { + position: absolute; + top: 59px; + left: 46px; + width: 207px; + height: 365px; + background-repeat: no-repeat; + overflow: hidden; +} + +#phone .head { + position: relative; + text-transform: uppercase; + text-align: center; + height: 20px; + line-height: 20px; + background-color: black; + z-index: 5; +} + +#phone-icon { + display: block; + position: absolute; + height: 12px; + line-height: 20px; + top: 4px; + right: 5px; +} + +#phone-number { + display: block; + position: absolute; + height: 20px; + line-height: 20px; + top: 0; + left: 5px; + color: white; +} + +#phone .menu { + max-height: 320px; + overflow-y: scroll; + z-index: 1; +} + +#phone .menu .home { + list-style: none outside none; + margin: 0; + padding: 13px; + color: white; +} + +#phone .menu .phone-icon { + position: relative; + display: block; + float: left; + text-align: center; + width: 60px; + padding-top: 50px; + padding-bottom: 5px; + background-size: 32px 32px; + background-color: transparent; + background-repeat: no-repeat; + background-position: center 10px; + cursor: pointer; +} + +#phone .menu .phone-icon::after { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.01); +} + +#phone-icon-rep { + background-image: url("../img/icons/rep.png"); +} + +#phone-icon-msg { + background-image: url("../img/icons/msg.png"); +} + +#phone .menu::-webkit-scrollbar { + display: none; +} + +#phone .menu .phone-icon.selected, #phone .menu .phone-icon:hover { + background-color: rgba(255, 255, 255, 0.1); +} + +.screen { + position: absolute; + top: 0; + left: 207px; + width: 207px; + height: 365px; + background-color: #dde1e8; + z-index: 1; + transition: left 0.25s ease; + overflow: hidden; +} + +.screen.active { + left: 0; +} + +.screen > .container { + position: absolute; + top: 50px; + left: 0; + width: 224px; + height: 315px; + overflow-y: scroll; +} + +.head-screen { + position: absolute; + height: 30px; + line-height: 30px; + text-transform: uppercase; + background-color: white; + text-align: center; + color: #212121; + background-color: white; + font-weight: 600; + border-bottom: 1px solid rgba(0, 0, 0, 0.3); + font-size: 13px; + top: 20px; + left: 0; + right: 0; + z-index: 5; +} + +.btn-head { + display: block; + color: #212121; + width: 30px; + height: 30px; + font-size: 20px; + cursor: pointer; +} + +.btn-head:hover { + background-color: #f1f1f1; +} + +.btn-head-back { + float: left; + background: transparent url("../img/icons/back.png") no-repeat 50%; +} + +.btn-head-new { + float: right; + background: transparent url("../img/icons/add.png") no-repeat 50%; +} + +.message, .contact { + position: relative; + padding: 3px 35px 3px 25px; + background-color: white; + border-bottom: 1px solid rgba(0, 0, 0, 0.3); + color: #212121; + min-height: 30px; +} + +.message.no-item, .contact.no-item { + padding: 3px 5px; +} + +.contact { + opacity: 0.5; +} + +.contact.online, .contact.no-item { + opacity: 1; +} + +.contact::after { + position: absolute; + display: block; + content: ''; + width: 8px; + height: 8px; + background-color: #dcdcdc; + left: 10px; + top: 8px; + border-radius: 50%; +} + +.contact.online::after { + background-color: #6ecc49; +} + +.contact.no-item::after { + display: none; +} + +.no-item { + text-align: center; +} + +.message .sender-info, .message .body, .contact .sender-info, { + margin-right: 30px; +} + +.message .phone-number { + line-height: 10px; + color: #999; +} + +.message .body { + margin-top: 5px; + line-height: 16px; +} + +.sender { + font-weight: 600; +} + +.actions { + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 24px; +} + +.actions .new-msg { + display: block; + width: 24px; + height: 24px; + background: transparent url("../img/icons/write.png") no-repeat 50%; + cursor: pointer; +} + +.actions .reply-btn { + background: transparent url("../img/icons/reply.png") no-repeat 50%; +} + +.actions .gps { + display: none; + width: 24px; + height: 24px; + background: transparent url(../img/icons/location.png) no-repeat 50%; + cursor: pointer; +} + +.actions .ok-btn { + display: none; + width: 24px; + height: 24px; + background: transparent url(../img/icons/ok.png) no-repeat 50%; + cursor: pointer; +} + +.actions .new-msg.anonyme, .contact .actions .new-msg { + display: none; +} + +.contact.online .actions .new-msg, .actions .gps.active, .actions .ok-btn.showOK { + display: block; +} + +.actions .new-msg:hover, .actions .gps:hover, .actions .ok-btn:hover { + background-color: #f1f1f1; +} + +#writer textarea { + width: 207px; + min-width: 207px; + max-width: 207px; + height: 255px; + min-height: 255px; + max-height: 255px; + background-color: #f1f1f1; + border: none; + box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; + padding: 5px; + outline: none; +} + +#writer label { + display: block; + width: 100%; + height: 30px; + line-height: 34px; + background-color: #f1f1f1; + border: none; + box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; + font-size: 14px; + font-weight: 600; + color: #6b6b6b; +} + +#writer label input { + top: 2px; + position: relative; + left: 5px; + margin-right: 10px; +} + +#writer button, #contact button { + display: block; + float: left; + width: 103px; + height: 30px; + line-height: 30px; + background-color: #dcdcdc; + border-color: rgba(0, 0, 0, 0.5); + border: none; + color: white; + font-weight: 600; + cursor: pointer; + outline: none; +} + +#writer button.marged, #contact button.marged { + margin-left: 1px; +} + +#writer #writer_cancel, #contact #contact_cancel { + background-color: #d9534f; +} + +#writer #writer_send:hover, #contact #contact_send:hover { + background-color: #74c374; +} + +#writer #writer_cancel:hover, #contact #contact_cancel:hover { + background-color: #dc6b68; +} + +#writer #writer_send, #contact #contact_send { + background-color: #5cb85c; +} + +#contact input { + display: block; + width: 100%; + height: 30px; + line-height: 30px; + padding: 2px 5px; + background-color: #f1f1f1; + border: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.3); + outline: none; + box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; +} \ No newline at end of file diff --git a/html/img/cursor.png b/html/img/cursor.png new file mode 100644 index 00000000..39536f95 Binary files /dev/null and b/html/img/cursor.png differ diff --git a/html/img/icons/add.png b/html/img/icons/add.png new file mode 100644 index 00000000..ad5a1310 Binary files /dev/null and b/html/img/icons/add.png differ diff --git a/html/img/icons/back.png b/html/img/icons/back.png new file mode 100644 index 00000000..f7fa8c38 Binary files /dev/null and b/html/img/icons/back.png differ diff --git a/html/img/icons/edit.png b/html/img/icons/edit.png new file mode 100644 index 00000000..9f0c9db5 Binary files /dev/null and b/html/img/icons/edit.png differ diff --git a/html/img/icons/location.png b/html/img/icons/location.png new file mode 100644 index 00000000..54e65f47 Binary files /dev/null and b/html/img/icons/location.png differ diff --git a/html/img/icons/msg.png b/html/img/icons/msg.png new file mode 100644 index 00000000..aec485a3 Binary files /dev/null and b/html/img/icons/msg.png differ diff --git a/html/img/icons/new-msg.png b/html/img/icons/new-msg.png new file mode 100644 index 00000000..a5970481 Binary files /dev/null and b/html/img/icons/new-msg.png differ diff --git a/html/img/icons/ok.png b/html/img/icons/ok.png new file mode 100644 index 00000000..c08a8a7a Binary files /dev/null and b/html/img/icons/ok.png differ diff --git a/html/img/icons/rep.png b/html/img/icons/rep.png new file mode 100644 index 00000000..efb0082e Binary files /dev/null and b/html/img/icons/rep.png differ diff --git a/html/img/icons/reply.png b/html/img/icons/reply.png new file mode 100644 index 00000000..0b359ecc Binary files /dev/null and b/html/img/icons/reply.png differ diff --git a/html/img/icons/signal.png b/html/img/icons/signal.png new file mode 100644 index 00000000..143301f5 Binary files /dev/null and b/html/img/icons/signal.png differ diff --git a/html/img/icons/write.png b/html/img/icons/write.png new file mode 100644 index 00000000..28f32beb Binary files /dev/null and b/html/img/icons/write.png differ diff --git a/html/img/keys/enter.png b/html/img/keys/enter.png new file mode 100644 index 00000000..3eb9a1f6 Binary files /dev/null and b/html/img/keys/enter.png differ diff --git a/html/img/keys/return.png b/html/img/keys/return.png new file mode 100644 index 00000000..a3ca125a Binary files /dev/null and b/html/img/keys/return.png differ diff --git a/html/img/phone.png b/html/img/phone.png new file mode 100644 index 00000000..47f6f82f Binary files /dev/null and b/html/img/phone.png differ diff --git a/html/img/pointer.png b/html/img/pointer.png new file mode 100644 index 00000000..9e1fcd47 Binary files /dev/null and b/html/img/pointer.png differ diff --git a/html/pdown.ttf b/html/pdown.ttf new file mode 100644 index 00000000..69c76cf3 Binary files /dev/null and b/html/pdown.ttf differ diff --git a/html/scripts/app.js b/html/scripts/app.js new file mode 100644 index 00000000..160d91d1 --- /dev/null +++ b/html/scripts/app.js @@ -0,0 +1,434 @@ +(function(){ + + let ContactTpl = + '
Aucun contact
+