├── stream ├── Starter.ymap └── _manifest.ymf ├── fxmanifest.lua ├── README.md ├── SQL.sql ├── config.lua ├── server.lua └── client.lua /stream/Starter.ymap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZiggyJoJo/just_apartments/HEAD/stream/Starter.ymap -------------------------------------------------------------------------------- /stream/_manifest.ymf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZiggyJoJo/just_apartments/HEAD/stream/_manifest.ymf -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | 3 | game 'gta5' 4 | 5 | this_is_a_map 'yes' 6 | 7 | lua54 'yes' 8 | 9 | author 'ZiggJoJo' 10 | 11 | version '1.0' 12 | 13 | shared_scripts { 14 | '@ox_lib/init.lua', 15 | 'config.lua' 16 | } 17 | 18 | client_scripts { 19 | 'client.lua' 20 | } 21 | 22 | server_scripts { 23 | '@mysql-async/lib/MySQL.lua', 24 | 'server.lua' 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Just Apartments 2 |

Video Demo


3 | 4 | # Dependencies 5 | 6 | - ESX 7 | - https://github.com/overextended/ox_lib 8 | - https://github.com/brentN5/bt-polyzone 9 | 10 | # Optional Dependencies 11 | 12 | - For Apartment Stashes: https://github.com/overextended/ox_inventory 13 | - For Apartment Wardrobe: https://github.com/ZiggyJoJo/brp-fivem-appearance 14 | - For bank logs if using NPWD: https://github.com/project-error/pefcl 15 | 16 | 17 | # Description 18 | 19 | Adds apartments that can be rented by players the cost and rent length can be changed by apartment in the SQL along with a starter apartment on Alta Street includes an option to view an apartment before purchasing the ability to give keys to other players along with a stash using OX_Inventory and a wardrobe using BRP Fivem Appearance. I plan to add offices and a shared garage system for those that have keys to the same apartment along with any other suggestions I might receive. 20 | 21 | 22 | # Setup 23 | 24 | 1. Ensure any dependencies before the script 25 | 2. Run the SQL file 26 | 3. Add TriggerServerEvent('just_apartments:getLastApartment') after your player spawns in if you use a spawn selector add it for last location option and TriggerServerEvent('just_apartments:updateLastApartment', nil) for any other option to prevent instancing outside of apartments 27 | 28 | # Support Me 29 |

Buy Me A Coffee


30 |

Tebex


31 | 32 |

Discord Server


33 | -------------------------------------------------------------------------------- /SQL.sql: -------------------------------------------------------------------------------- 1 | -- Dumping structure for table es_extended.apartments 2 | CREATE TABLE IF NOT EXISTS `apartments` ( 3 | `Name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 4 | `Price` int(11) DEFAULT 5000, 5 | `rentLength` int(11) DEFAULT 30 6 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 7 | 8 | -- Dumping data for table es_extended.apartments: ~6 rows (approximately) 9 | /*!40000 ALTER TABLE `apartments` DISABLE KEYS */; 10 | INSERT INTO `apartments` (`Name`, `Price`, `rentLength`) VALUES 11 | ('4IntegrityWay', 5000, 30), 12 | ('DelPerroHeights', 5000, 30), 13 | ('EclipseTowers', 7500, 30), 14 | ('RichardsMajestic', 5000, 30), 15 | ('TinselTowers', 5000, 30), 16 | ('WeazelPlazaApartments', 5000, 30); 17 | /*!40000 ALTER TABLE `apartments` ENABLE KEYS */; 18 | 19 | -- Dumping structure for table es_extended.apartment_keys 20 | CREATE TABLE IF NOT EXISTS `apartment_keys` ( 21 | `id` int(11) NOT NULL AUTO_INCREMENT, 22 | `appt_id` int(11) NOT NULL DEFAULT 0, 23 | `appt_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 24 | `player` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0', 25 | `player_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 26 | `appt_owner` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 27 | PRIMARY KEY (`id`) 28 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 29 | 30 | -- Dumping structure for table es_extended.owned_apartments 31 | CREATE TABLE IF NOT EXISTS `owned_apartments` ( 32 | `id` int(11) NOT NULL AUTO_INCREMENT, 33 | `owner` varchar(46) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 34 | `apartment` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 35 | `renew` tinyint(4) NOT NULL DEFAULT 1, 36 | `expired` tinyint(4) NOT NULL DEFAULT 0, 37 | `lastPayment` int(11) DEFAULT NULL, 38 | `renewDate` int(11) DEFAULT NULL, 39 | PRIMARY KEY (`id`) 40 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 41 | 42 | ALTER TABLE `users` 43 | ADD `last_property` varchar(50) DEFAULT NULL, 44 | ; 45 | 46 | 47 | 48 | 49 | -- Only needed if using the shared garages and not using luke_garages https://github.com/LukeWasTakenn/luke_garages 50 | 51 | -- If you have a separate column for your stored vehicles simply replace the instances in the server file with your columns label 52 | 53 | ALTER TABLE `owned_vehicles` 54 | ADD `garage` varchar(50) DEFAULT NULL, 55 | ADD `last_garage` varchar(50) DEFAULT NULL, 56 | ; 57 | -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | Config = {} 2 | 3 | Config.UseOxInventory = true 4 | 5 | Config.BrpFivemAppearance = true -- Must choose one or the other if either is used 6 | Config.ox_appearance = false 7 | 8 | Config.useGarages = true 9 | 10 | Config.usePEFCL = false 11 | 12 | Config.Apartments = { 13 | ['AltaStreetAppts'] = { 14 | label = "Alta Street Apartments", 15 | type = "StarterApartment", 16 | seperateExitPoint = true, 17 | entrance = {x = -270.78894042969, y = -958.21881103516, z = 31.227436065674, h = 118.76303863525}, 18 | exit = {x = -268.41479492188, y = -963.39013671875, z = 21.912317276001, h = 356.97747802734}, 19 | exitPoint = {x = -266.36004638672, y = -955.48510742188, z = 31.227432250977, h = 118.27416992188}, 20 | stash = {x = -268.89364624023, y = -958.93017578125, z = 21.917707443237, h = 60.0}, 21 | wardrobe = {x = -267.76, y = -956.6, z = 21.92, l = 2.4, w = 2.8, h = 0, minZ = 20.9, maxZ = 23.22}, 22 | zone = {name = 'AltaStreetAppts', x = -266.8, y = -959.7, z = 21.9, l = 8.7, w = 5.4, h = 0, minZ = 19, maxZ = 23}, 23 | blip = { 24 | scale = 0.7, 25 | sprite = 475, 26 | colour = 32 27 | }, 28 | }, 29 | 30 | ---------------- 31 | -- Apartments -- 32 | ---------------- 33 | ['4IntegrityWay'] = { 34 | label = "4 Integrity Way", 35 | type = "Apartment", 36 | seperateExitPoint = true, 37 | entrance = {x = -43.677406311035, y = -584.69696044922, z = 38.161270141602, h = 254.36935424805}, 38 | exit = {x = -31.511259078979, y = -594.93328857422, z = 80.030891418457, h = 250.01995849609}, 39 | exitPoint = {x = -44.424266815186, y = -587.35302734375, z = 38.160839080811, h = 67.393547058105}, 40 | stash = {x = -11.746654510498, y = -597.97283935547, z = 79.430206298828, h = 153.85530090332}, 41 | wardrobe = {x = -38.2, y = -589.3, z = 78.8, l = 4.8, w = 6.4, h = 340, minZ = 77, maxZ = 80}, 42 | zone = {name = '4IntegrityWay', x = 1145.3, y = -778.4, z = 7.6, l = 30.0, w = 20.0, h = 90, minZ = 55, maxZ = 60}, 43 | parking = { 44 | ["1"] = {x = -26.665096282959, y = -624.26470947266, z = 35.115489959717, h = 249.51577758789}, 45 | ["2"] = {x = -18.441314697266, y = -635.34942626953, z = 35.34984588623, h = 249.20275878906}, 46 | ["3"] = {x = -20.003524780273, y = -639.08392333984, z = 35.349895477295, h = 251.1537322998}, 47 | }, 48 | blip = { 49 | scale = 0.7, 50 | sprite = 475, 51 | colour = 32 52 | }, 53 | }, 54 | 55 | ['WeazelPlazaApartments'] = { 56 | label = "Weazel Plaza Apartments", 57 | type = "Apartment", 58 | seperateExitPoint = true, 59 | entrance = {x = -908.98883056641, y = -446.12484741211, z = 39.605274200439, h = 114.50199127197}, 60 | exit = {x = -890.73431396484, y = -436.75045776367, z = 121.60707092285, h = 27.384859085083}, 61 | exitPoint = {x = -906.2998046875, y = -451.72274780273, z = 39.605285644531, h = 120.24710083008}, 62 | stash = {x = -898.38647460938, y = -440.6930847168, z = 121.61553955078, h = 113.11880493164}, 63 | wardrobe = {x = -909.6, y = -445.4, z = 115.4, l = 4.6, w = 5.2, h = 27, minZ = 114, maxZ = 117}, 64 | zone = {name = 'WeazelPlazaApartments', x = 1145.3, y = -778.4, z = 7.6, l = 30.0, w = 20.0, h = 90, minZ = 55, maxZ = 60}, 65 | parking = { 66 | ["1"] = {x = -926.02062988281, y = -472.11840820313, z = 36.469593048096, h = 27.569271087646}, 67 | ["2"] = {x = -929.34710693359, y = -465.73324584961, z = 36.658302307129, h = 27.3541431427}, 68 | ["3"] = {x = -932.21258544922, y = -460.16870117188, z = 36.799449920654, h = 27.335224151611}, 69 | ["4"] = {x = -935.37347412109, y = -454.02523803711, z = 36.932556152344, h = 27.275016784668}, 70 | }, 71 | blip = { 72 | scale = 0.7, 73 | sprite = 475, 74 | colour = 32 75 | }, 76 | }, 77 | 78 | ['RichardsMajestic'] = { 79 | label = "Richards Majestic", 80 | type = "Apartment", 81 | seperateExitPoint = true, 82 | entrance = {x = -935.89715576172, y = -378.78204345703, z = 38.961292266846, h = 114.38324737549}, 83 | exit = {x = -907.18048095703, y = -372.34390258789, z = 109.44027709961, h = 29.907409667969}, 84 | exitPoint = {x = -932.91668701172, y = -383.57510375977, z = 38.96129989624, h = 119.42089080811}, 85 | stash = {x = -914.99621582031, y = -376.62802124023, z = 109.4489440918, h = 116.96913146973}, 86 | wardrobe = {x = -926.1, y = -381.5, z = 103.2, l = 4.2, w = 5.0, h = 27, minZ = 101, maxZ = 104.2}, 87 | zone = {name = 'RichardsMajestic', x = 1145.3, y = -778.4, z = 57.6, l = 30.0, w = 20.0, h = 90, minZ = 55, maxZ = 60}, 88 | parking = { 89 | ["1"] = {x = -888.60003662109, y = -337.46255493164, z = 34.159576416016, h = 208.54290771484}, 90 | ["2"] = {x = -891.90936279297, y = -339.06185913086, z = 34.160491943359, h = 207.65989685059}, 91 | ["3"] = {x = -894.96032714844, y = -340.53625488281, z = 34.15979385376, h = 206.32011413574}, 92 | ["4"] = {x = -898.35870361328, y = -342.14248657227, z = 34.159748077393, h = 206.7610168457}, 93 | ["5"] = {x = -894.33850097656, y = -349.31283569336, z = 34.159965515137, h = 27.798797607422}, 94 | ["6"] = {x = -891.02117919922, y = -347.40133666992, z = 34.159671783447, h = 28.056095123291}, 95 | }, 96 | blip = { 97 | scale = 0.7, 98 | sprite = 475, 99 | colour = 32 100 | }, 101 | }, 102 | 103 | ['DelPerroHeights'] = { 104 | label = "Del Perro Heights", 105 | type = "Apartment", 106 | seperateExitPoint = false, 107 | entrance = {x = -1447.6293945313, y = -537.32611083984, z = 34.740154266357, h = 215.23828125}, 108 | exit = {x = -1452.2774658203, y = -540.5205078125, z = 74.044303894043, h = 40.185394287109}, 109 | stash = {x = -1466.7415771484, y = -526.98858642578, z = 73.443618774414, h = 306.0309753418}, 110 | wardrobe = {x = -1450.2, y = -549.3, z = 72.8, l = 4.0, w = 4.4, h = 35, minZ = 70, maxZ = 74}, 111 | zone = {name = 'DelPerroHeights', x = 1145.3, y = -778.4, z = 7.6, l = 30.0, w = 20.0, h = 90, minZ = 55, maxZ = 60}, 112 | parking = { 113 | ["1"] = {x = -1411.2176513672, y = -533.68292236328, z = 30.903594970703, h = 214.52825927734}, 114 | ["2"] = {x = -1418.0433349609, y = -523.84423828125, z = 31.488754272461, h = 214.21955871582}, 115 | ["3"] = {x = -1424.7448730469, y = -514.32885742188, z = 32.089302062988, h = 214.19808959961}, 116 | ["4"] = {x = -1430.9952392578, y = -505.21267700195, z = 32.650783538818, h = 213.97334289551}, 117 | ["5"] = {x = -1437.2003173828, y = -496.05038452148, z = 33.167785644531, h = 213.69674682617}, 118 | }, 119 | blip = { 120 | scale = 0.7, 121 | sprite = 475, 122 | colour = 32 123 | }, 124 | }, 125 | 126 | ['TinselTowers'] = { 127 | label = "Tinsel Towers", 128 | type = "Apartment", 129 | seperateExitPoint = true, 130 | entrance = {x = -621.08837890625, y = 46.244003295898, z = 43.591468811035, h = 186.38636779785}, 131 | exit = {x = -602.87280273438, y = 58.898902893066, z = 98.200187683105, h = 90.942649841309}, 132 | exitPoint = {x = -614.61468505859, y = 46.540714263916, z = 43.591468811035, h = 184.96089172363}, 133 | stash = {x = -622.54583740234, y = 54.491561889648, z = 97.599517822266, h = 0.27685731649399}, 134 | wardrobe = {x = -594.7, y = 55.6, z = 97.0, l = 4.4, w = 4.2, h = 0, minZ = 95, maxZ = 99}, 135 | zone = {name = 'TinselTowers', x = 1145.3, y = -778.4, z = 7.6, l = 30.0, w = 20.0, h = 90, minZ = 55, maxZ = 60}, 136 | parking = { 137 | ["1"] = {x = -621.54418945313, y = 53.364902496338, z = 43.359733581543, h = 74.804344177246}, 138 | ["2"] = {x = -621.83892822266, y = 59.403179168701, z = 43.359127044678, h = 106.13849639893}, 139 | ["3"] = {x = -568.94396972656, y = 53.361534118652, z = 48.864318847656, h = 271.47863769531}, 140 | ["4"] = {x = -569.08227539063, y = 58.165046691895, z = 48.861988067627, h = 271.80157470703}, 141 | }, 142 | blip = { 143 | scale = 0.7, 144 | sprite = 475, 145 | colour = 32 146 | }, 147 | }, 148 | 149 | ['EclipseTowers'] = { 150 | label = "Eclipse Towers", 151 | type = "Apartment", 152 | seperateExitPoint = true, 153 | entrance = {x = -776.97711181641, y = 319.74661254883, z = 85.662673950195, h = 177.4141998291}, 154 | exit = {x = -773.93316650391, y = 341.95892333984, z = 196.68617248535, h = 91.444755554199}, 155 | exitPoint = {x = -770.62426757813, y = 319.72906494141, z = 85.662673950195, h = 177.16806030273}, 156 | stash = {x = -764.73388671875, y = 330.80294799805, z = 196.08601379395, h = 180.05033874512}, 157 | wardrobe = {x = -763.2, y = 329.0, z = 199.5, l = 5.2, w = 4.6, h = 0, minZ = 198, maxZ = 201}, 158 | zone = {name = 'EclipseTowers', x = 1145.3, y = -778.4, z = 7.6, l = 30.0, w = 20.0, h = 90, minZ = 55, maxZ = 60}, 159 | parking = { 160 | ["1"] = {x = -791.58074951172, y = 332.78469848633, z = 85.325965881348, h = 182.03956604004}, 161 | ["2"] = {x = -800.38586425781, y = 331.30599975586, z = 85.326347351074, h = 182.06657409668}, 162 | 163 | }, 164 | blip = { 165 | scale = 0.7, 166 | sprite = 475, 167 | colour = 32 168 | }, 169 | }, 170 | } -------------------------------------------------------------------------------- /server.lua: -------------------------------------------------------------------------------- 1 | ESX = nil 2 | 3 | TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) 4 | 5 | local instances = {} 6 | 7 | RegisterServerEvent("instance:set") 8 | AddEventHandler("instance:set", function(set) 9 | print('[INSTANCES] Instances looked like this: ', json.encode(instances)) 10 | local src = source 11 | -- TriggerClientEvent('DoTheBigRefreshYmaps', src) 12 | local instanceSource = 0 13 | if set then 14 | if set == 0 then 15 | for k,v in pairs(instances) do 16 | for k2,v2 in pairs(v) do 17 | if v2 == src then 18 | table.remove(v, k2) 19 | if #v == 0 then 20 | instances[k] = nil 21 | end 22 | end 23 | end 24 | end 25 | end 26 | instanceSource = set 27 | else 28 | instanceSource = math.random(1, 1000) 29 | while instances[instanceSource] and #instances[instanceSource] >= 1 do 30 | instanceSource = math.random(1, 1000) 31 | Citizen.Wait(1) 32 | end 33 | end 34 | print(instanceSource) 35 | if instanceSource ~= 0 then 36 | if not instances[instanceSource] then 37 | instances[instanceSource] = {} 38 | end 39 | table.insert(instances[instanceSource], src) 40 | end 41 | SetPlayerRoutingBucket(src, instanceSource) 42 | print('[INSTANCES] Instances now looks like this: ', json.encode(instances)) 43 | end) 44 | 45 | Namedinstances = {} 46 | 47 | RegisterServerEvent("instance:setNamed") 48 | AddEventHandler("instance:setNamed", function(setName) 49 | print('[INSTANCES] Named Instances looked like this: ', json.encode(Namedinstances)) 50 | local src = source 51 | local instanceSource = nil 52 | 53 | if setName == 0 then 54 | for k,v in pairs(Namedinstances) do 55 | for k2,v2 in pairs(v.people) do 56 | if v2 == src then 57 | table.remove(v.people, k2) 58 | end 59 | end 60 | if #v.people == 0 then 61 | Namedinstances[k] = nil 62 | end 63 | end 64 | instanceSource = setName 65 | else 66 | for k,v in pairs(Namedinstances) do 67 | if v.name == setName then 68 | instanceSource = k 69 | end 70 | end 71 | if instanceSource == nil then 72 | instanceSource = math.random(1, 1000) 73 | 74 | while Namedinstances[instanceSource] and #Namedinstances[instanceSource] >= 1 do 75 | instanceSource = math.random(1, 1000) 76 | Citizen.Wait(1) 77 | end 78 | end 79 | end 80 | if instanceSource ~= 0 then 81 | if not Namedinstances[instanceSource] then 82 | Namedinstances[instanceSource] = {name = setName, people = {}} 83 | end 84 | table.insert(Namedinstances[instanceSource].people, src) 85 | end 86 | SetPlayerRoutingBucket(src, instanceSource) 87 | print('[INSTANCES] Named Instances now look like this: ', json.encode(Namedinstances)) 88 | end) 89 | 90 | local playersAtDoor = {} 91 | 92 | RegisterServerEvent("just_apartments:purchaseApartment") 93 | AddEventHandler("just_apartments:purchaseApartment", function(apartment, currentApartmentLabel) 94 | local _source = source 95 | local xPlayer = ESX.GetPlayerFromId(_source) 96 | local Price = MySQL.scalar.await('SELECT Price FROM apartments WHERE Name = @Name', {['@Name'] = apartment}) 97 | local rentLength = MySQL.scalar.await('SELECT rentLength FROM apartments WHERE Name = @Name', {['@Name'] = apartment}) 98 | local balance 99 | 100 | if Config.usePEFCL then 101 | balance = exports.pefcl:getDefaultAccountBalance(_source) 102 | balance = balance.data 103 | else 104 | balance = xPlayer.getAccount('bank').money 105 | end 106 | 107 | if Price <= balance then 108 | local t = os.time() 109 | local date = os.date("%Y%m%d",t) 110 | local d = rentLength 111 | local renewDate = t + d * 24 * 60 * 60 112 | local oldLease = MySQL.scalar.await('SELECT id FROM owned_apartments WHERE apartment = @apartment AND owner = @owner', {['@apartment'] = apartment, ['@owner'] = xPlayer.identifier}) 113 | if oldLease ~= nil then 114 | local lastPayment = MySQL.update.await('UPDATE owned_apartments SET lastPayment = @lastPayment WHERE id = @id', {['@id'] = oldLease, ['@lastPayment'] = tonumber(date)}) 115 | local renewDateChange = MySQL.update.await('UPDATE owned_apartments SET renewDate = @renewDate WHERE id = @id', {['@id'] = oldLease, ['@renewDate'] = os.date("%Y%m%d",renewDate)}) 116 | local lastPayment = MySQL.update.await('UPDATE owned_apartments SET renew = @renew WHERE id = @id', {['@id'] = oldLease, ['@renew'] = tonumber(1)}) 117 | local renewDateChange = MySQL.update.await('UPDATE owned_apartments SET expired = @expired WHERE id = @id', {['@id'] = oldLease, ['@expired'] = tonumber(0)}) 118 | if Config.usePEFCL then 119 | exports.pefcl:removeBankBalance(_source, { amount = Price, message = (currentApartmentLabel.." lease renewal") }) 120 | else 121 | xPlayer.removeAccountMoney('bank', Price) 122 | end 123 | else 124 | MySQL.insert('INSERT INTO `owned_apartments` (`owner`, `lastPayment`, `renewDate`, `apartment`) VALUES (@owner, @lastPayment, @renewDate, @apartment)', { 125 | ['@apartment'] = apartment, 126 | ['@lastPayment'] = os.date("%Y%m%d",t), 127 | ['@renewDate'] = os.date("%Y%m%d",renewDate), 128 | ['@owner'] = xPlayer.identifier 129 | }) 130 | if Config.UseOxInventory then 131 | MySQL.scalar('SELECT id FROM owned_apartments WHERE apartment = @apartment AND owner = @owner', {['@apartment'] = apartment, ['@owner'] = xPlayer.identifier}, function(id) 132 | if id then 133 | exports.ox_inventory:RegisterStash((apartment..id.."Stash"), (apartment.." Stash - "..id), 50, 100000, id) 134 | end 135 | end) 136 | end 137 | if Config.usePEFCL then 138 | exports.pefcl:removeBankBalance(_source, { amount = Price, message = ("Apartment lease at "..currentApartmentLabel) }) 139 | else 140 | xPlayer.removeAccountMoney('bank', Price) 141 | end 142 | end 143 | else 144 | TriggerClientEvent("just_apartments:notification", _source , 'Not enough money', nil, "error") 145 | end 146 | end) 147 | 148 | RegisterServerEvent("just_apartments:changeLease") 149 | AddEventHandler("just_apartments:changeLease", function(data) 150 | local lastPayment = MySQL.update.await('UPDATE owned_apartments SET renew = @renew WHERE id = @id', {['@id'] = data.id, ['@renew'] = tonumber(data.renew)}) 151 | end) 152 | 153 | lib.callback.register('just_apartments:getOwnedApartments', function(source, apartment, coords) 154 | local _source = source 155 | local xPlayer = ESX.GetPlayerFromId(_source) 156 | local id = MySQL.scalar.await('SELECT id FROM owned_apartments WHERE owner = @owner AND apartment = @apartment', { ['@owner'] = xPlayer.identifier, ['@apartment'] = apartment }) 157 | local expired = MySQL.scalar.await('SELECT expired FROM owned_apartments WHERE id = @id', {['@id'] = id}) 158 | local renewDate = MySQL.scalar.await('SELECT renewDate FROM owned_apartments WHERE id = @id', {['@id'] = id}) 159 | local price = MySQL.scalar.await('SELECT Price FROM apartments WHERE name = @name', { ['@name'] = apartment }) 160 | if id ~= nil and expired == 0 then 161 | local renew = MySQL.scalar.await('SELECT renew FROM owned_apartments WHERE id = @id', {['@id'] = id}) 162 | local data = {coords = coords, id = id, price = price, renewDate = renewDate, renew = renew} 163 | return data 164 | else 165 | local id = false 166 | local data = {coords = coords, id = id, price = price, renewDate = renewDate} 167 | return data 168 | end 169 | end) 170 | 171 | lib.callback.register('just_apartments:checkApptOwnership', function(source, apartment, appt_id) 172 | local xPlayer = ESX.GetPlayerFromId(source) 173 | local _source = source 174 | 175 | local id = MySQL.scalar.await('SELECT id FROM owned_apartments WHERE owner = @owner AND apartment = @apartment', {['@owner'] = xPlayer.identifier,['@apartment'] = apartment}) 176 | if id ~= nil then 177 | return true 178 | else 179 | local id2 = MySQL.scalar.await('SELECT id FROM apartment_keys WHERE appt_id = @appt_id AND player = @player', {['@appt_id'] = appt_id,['@player'] = xPlayer.identifier}) 180 | if id2 ~= nil then 181 | -- exports.xng_parsingtable:ParsingTable_sv(id2) 182 | return true 183 | end 184 | end 185 | end) 186 | 187 | RegisterServerEvent("just_apartments:alertOwner") 188 | AddEventHandler("just_apartments:alertOwner", function(data) 189 | local _source = source 190 | if not playersAtDoor[data.apartment..data.id] then 191 | playersAtDoor[data.apartment..data.id] = { 192 | name = data.apartment..data.id, 193 | people = {} 194 | } 195 | end 196 | table.insert(playersAtDoor[data.apartment..data.id].people, _source) 197 | -- exports.xng_parsingtable:ParsingTable_sv(playersAtDoor) 198 | for k,v in pairs(Namedinstances) do 199 | -- exports.xng_parsingtable:ParsingTable_sv(Namedinstances) 200 | if v.name == (data.apartment..data.id) then 201 | for k2,v2 in pairs(Namedinstances[k].people) do 202 | TriggerClientEvent("just_apartments:notification", _source , 'Someones at the door', "Go buzz them in", "info") 203 | end 204 | end 205 | end 206 | end) 207 | 208 | RegisterServerEvent("just_apartments:getBuildingApartments") 209 | AddEventHandler("just_apartments:getBuildingApartments", function(data) 210 | local _source = source 211 | local xPlayer = ESX.GetPlayerFromId(_source) 212 | MySQL.query('SELECT * FROM owned_apartments WHERE apartment = @apartment',{ 213 | ['@apartment'] = data.currentApartment 214 | }, function(apartments) 215 | TriggerClientEvent('just_apartments:ringMenu', _source, apartments, data, xPlayer.identifier) 216 | end) 217 | end) 218 | 219 | lib.callback.register('just_apartments:getPlayersAtDoor', function(source, coords, name, id, exit) 220 | local _source = source 221 | local xPlayer = ESX.GetPlayerFromId(_source) 222 | if id == nil then 223 | TriggerClientEvent('just_apartments:exitMenu', _source, coords) 224 | else 225 | local id2 = MySQL.scalar.await('SELECT id FROM apartment_keys WHERE appt_id = @appt_id AND player = @player', {['@appt_id'] = id,['@player'] = xPlayer.identifier}) 226 | if id2 ~= nil then 227 | return coords, playersAtDoor[name..id], exit 228 | else 229 | local keyholders = MySQL.query.await('SELECT * FROM apartment_keys WHERE appt_id = @appt_id', {['@appt_id'] = id}) 230 | return coords, playersAtDoor[name..id], exit, keyholders 231 | end 232 | end 233 | end) 234 | 235 | RegisterServerEvent("just_apartments:bringPlayerIn") 236 | AddEventHandler("just_apartments:bringPlayerIn", function(data) 237 | local _source = source 238 | -- exports.xng_parsingtable:ParsingTable_sv(data) 239 | for k,v in pairs(playersAtDoor) do 240 | for k2,v2 in pairs(v.people) do 241 | if v2 == data.player then 242 | table.remove(v.people, k2) 243 | end 244 | end 245 | if #v.people == 0 then 246 | playersAtDoor[k] = nil 247 | end 248 | end 249 | -- exports.xng_parsingtable:ParsingTable_sv(data) 250 | TriggerClientEvent('just_apartments:enterExitApartment', data.player, data) 251 | end) 252 | 253 | AddEventHandler('onServerResourceStart', function(resourceName) 254 | if resourceName == 'just_apartments' or resourceName == GetCurrentResourceName() then 255 | MySQL.query('SELECT * FROM owned_apartments ', function(apartments) 256 | for i=1, #apartments, 1 do 257 | local todaysDate = tonumber(os.date("%Y%m%d",os.time())) 258 | if todaysDate > apartments[i].renewDate then 259 | if apartments[i].renew then 260 | local Price = MySQL.scalar.await('SELECT Price FROM apartments WHERE Name = @Name', {['@Name'] = apartments[i].apartment}) 261 | local rentLength = MySQL.scalar.await('SELECT rentLength FROM apartments WHERE Name = @Name', {['@Name'] = apartments[i].apartment}) 262 | local balance 263 | if Config.usePEFCL then 264 | balance = exports.pefcl:getTotalBankBalanceByIdentifier(source, apartments[i].owner) 265 | balance = balance.data 266 | else 267 | local accounts = MySQL.scalar.await('SELECT accounts FROM users WHERE identifier = @identifier', {['@identifier'] = apartments[i].owner}) 268 | accounts = json.decode(accounts) 269 | balance = accounts.bank 270 | end 271 | 272 | if Price <= balance then 273 | local t = os.time() 274 | local date = os.date("%Y%m%d",t) 275 | local d = rentLength 276 | local renewDate = t + d * 24 * 60 * 60 277 | local lastPayment = MySQL.update.await('UPDATE owned_apartments SET lastPayment = @lastPayment WHERE id = @id', {['@id'] = apartments[i].id, ['@lastPayment'] = tonumber(date)}) 278 | local renewDateChange = MySQL.update.await('UPDATE owned_apartments SET renewDate = @renewDate WHERE id = @id', {['@id'] = apartments[i].id, ['@renewDate'] = os.date("%Y%m%d",renewDate)}) 279 | if lastPayment ~= nil and renewDateChange ~= nil then 280 | if Config.usePEFCL then 281 | exports.pefcl:removeBankBalanceByIdentifier(source, { identifier = apartments[i].owner, amount = Price, message = apartments[i].apartment.." apartment lease renewal" }) 282 | else 283 | accounts.bank = balance - Price 284 | MySQL.update.await('UPDATE users SET accounts = @accounts WHERE identifier = @identifier', {['@identifier'] = apartments[i].owner, ['@accounts'] = json.encode(accounts)}) 285 | end 286 | end 287 | else 288 | MySQL.update('UPDATE owned_apartments SET expired = @expired WHERE id = @id', { 289 | ['@id'] = apartments[i].id, 290 | ['@expired'] = 1 291 | }, function(id) 292 | end) 293 | end 294 | else 295 | MySQL.update('UPDATE owned_apartments SET expired = @expired WHERE id = @id', { 296 | ['@id'] = apartments[i].id, 297 | ['@expired'] = 1 298 | }, function(id) 299 | end) 300 | end 301 | end 302 | end 303 | end) 304 | end 305 | end) 306 | 307 | ---------- 308 | -- Keys -- 309 | ---------- 310 | 311 | RegisterServerEvent("just_apartments:giveKeys") 312 | AddEventHandler("just_apartments:giveKeys", function(data) 313 | local _source = source 314 | local xPlayer = ESX.GetPlayerFromId(source) 315 | local xTarget = ESX.GetPlayerFromId(data.target) 316 | local hasKeys = MySQL.scalar.await('SELECT id FROM apartment_keys WHERE appt_id = @appt_id AND player = @player', {['@appt_id'] = data.appt_id, ['@player'] = xTarget.identifier}) 317 | -- exports.xng_parsingtable:ParsingTable_sv(xTarget) 318 | if hasKeys == nil then 319 | MySQL.insert('INSERT INTO `apartment_keys` (`appt_id`, `appt_name`, `player`, `player_name`, `appt_owner`) VALUES (@appt_id, @appt_name, @player, @player_name, @appt_owner)', { 320 | ['@appt_id'] = data.appt_id, 321 | ['@appt_name'] = data.appt_name, 322 | ['@player'] = xTarget.identifier, 323 | ['@player_name'] = xTarget.name, 324 | ['@appt_owner'] = xPlayer.identifier 325 | }) 326 | TriggerClientEvent("just_apartments:notification", _source , 'Key given', nil, "success") 327 | TriggerClientEvent("just_apartments:notification", data.target , 'Key recieved', nil, "success") 328 | else 329 | TriggerClientEvent("just_apartments:notification", _source , 'Person already has keys', nil, "error") 330 | end 331 | end) 332 | 333 | lib.callback.register('just_apartments:getAppartmentsWithKeys', function(source, currentApartment) 334 | local _source = source 335 | local xPlayer = ESX.GetPlayerFromId(_source) 336 | MySQL.query('SELECT * FROM apartment_keys WHERE appt_name = @appt_name AND player = @player',{ 337 | ['@appt_name'] = currentApartment, 338 | ['@player'] = xPlayer.identifier 339 | }, function(apartments) 340 | exports.xng_parsingtable:ParsingTable_sv(apartments) 341 | return apartments 342 | end) 343 | end) 344 | 345 | RegisterServerEvent("just_apartments:removeApartmentKeys") 346 | AddEventHandler("just_apartments:removeApartmentKeys", function(data) 347 | MySQL.update('DELETE FROM apartment_keys WHERE id = @id', {['@id'] = data.id}, function(affectedRows) 348 | if affectedRows then 349 | print(affectedRows) 350 | end 351 | end) 352 | end) 353 | 354 | ----------- 355 | -- Stash -- 356 | ----------- 357 | 358 | AddEventHandler('onServerResourceStart', function(resourceName) 359 | if resourceName == 'ox_inventory' or resourceName == GetCurrentResourceName() and Config.UseOxInventory then 360 | exports.ox_inventory:RegisterStash("AltaStreetAppts0Stash", "Alta Street Apartments", 25, 100000, true) 361 | MySQL.query('SELECT * FROM owned_apartments ', function(apartments) 362 | for i=1, #apartments, 1 do 363 | exports.ox_inventory:RegisterStash((apartments[i].apartment..apartments[i].id.."Stash"), (apartments[i].apartment.." Stash - "..apartments[i].id), 50, 100000, apartments[i].id) 364 | end 365 | end) 366 | end 367 | end) 368 | 369 | ------------------------- 370 | -- Save Last Apartment -- 371 | ------------------------- 372 | 373 | function Split(s, delimiter) 374 | if s ~= nil then 375 | result = {}; 376 | for match in (s..delimiter):gmatch("(.-)"..delimiter) do 377 | table.insert(result, match); 378 | end 379 | return result; 380 | end 381 | end 382 | 383 | RegisterServerEvent("just_apartments:updateLastApartment") 384 | AddEventHandler("just_apartments:updateLastApartment", function(last_property) 385 | local _source = source 386 | local xPlayer = ESX.GetPlayerFromId(_source) 387 | MySQL.update('UPDATE users SET last_property = @last_property WHERE identifier = @identifier', { 388 | ['@identifier'] = xPlayer.identifier, 389 | ['@last_property'] = last_property 390 | }, function(id) 391 | end) 392 | end) 393 | 394 | RegisterServerEvent("just_apartments:getLastApartment") 395 | AddEventHandler("just_apartments:getLastApartment", function() 396 | local _source = source 397 | local xPlayer = ESX.GetPlayerFromId(_source) 398 | if xPlayer ~= nil then 399 | local last_property = MySQL.scalar.await('SELECT last_property FROM users WHERE identifier = @identifier', {['@identifier'] = xPlayer.identifier}) 400 | local apartment_id = Split(last_property, " ") 401 | if apartment_id ~= nil then 402 | TriggerClientEvent('just_apartments:spawnInProperty', _source, apartment_id[1], apartment_id[2]) 403 | end 404 | end 405 | end) 406 | 407 | ------------ 408 | -- Garage -- 409 | ------------ 410 | 411 | lib.callback.register('just_apartments:GetVehicles', function(source, garage) 412 | local vehicles = {} 413 | local results = MySQL.Sync.fetchAll("SELECT `plate`, `vehicle`, `stored`, `garage`, `job` FROM `owned_vehicles` WHERE `garage` = @garage", { 414 | ['@garage'] = garage 415 | }) 416 | if results[1] ~= nil then 417 | for i = 1, #results do 418 | local result = results[i] 419 | local veh = json.decode(result.vehicle) 420 | vehicles[#vehicles+1] = {plate = result.plate, vehicle = veh, stored = result.stored, garage = result.garage} 421 | end 422 | return vehicles 423 | end 424 | end) 425 | 426 | RegisterServerEvent("just_apartments:SpawnVehicle") 427 | AddEventHandler("just_apartments:SpawnVehicle", function(model, plate, coords, heading) 428 | if type(model) == 'string' then model = GetHashKey(model) end 429 | local xPlayer = ESX.GetPlayerFromId(source) 430 | local vehicles = GetAllVehicles() 431 | plate = ESX.Math.Trim(plate) 432 | for i = 1, #vehicles do 433 | if ESX.Math.Trim(GetVehicleNumberPlateText(vehicles[i])) == plate then 434 | if GetVehiclePetrolTankHealth(vehicle) > 0 and GetVehicleBodyHealth(vehicle) > 0 then 435 | return xPlayer.showNotification(Locale('vehicle_already_exists')) end 436 | end 437 | end 438 | MySQL.Async.fetchAll('SELECT vehicle, plate, garage FROM `owned_vehicles` WHERE plate = @plate', {['@plate'] = ESX.Math.Trim(plate)}, function(result) 439 | if result[1] then 440 | CreateThread(function() 441 | local entity = Citizen.InvokeNative(`CREATE_AUTOMOBILE`, model, coords.x, coords.y, coords.z, coords.h) 442 | SetEntityHeading(entity, coords.h) 443 | local ped = GetPedInVehicleSeat(entity, -1) 444 | if ped > 0 then 445 | for i = -1, 6 do 446 | ped = GetPedInVehicleSeat(entity, i) 447 | local popType = GetEntityPopulationType(ped) 448 | if popType <= 5 or popType >= 1 then 449 | DeleteEntity(ped) 450 | end 451 | end 452 | end 453 | local playerPed = GetPlayerPed(xPlayer.source) 454 | local timer = GetGameTimer() 455 | while GetVehiclePedIsIn(playerPed) ~= entity do 456 | Wait(10) 457 | SetPedIntoVehicle(playerPed, entity, -1) 458 | if timer - GetGameTimer() > 15000 then 459 | break 460 | end 461 | end 462 | local ent = Entity(entity) 463 | ent.state.vehicleData = result[1] 464 | end) 465 | end 466 | end) 467 | end) 468 | 469 | RegisterServerEvent("just_apartments:SaveVehicle") 470 | AddEventHandler("just_apartments:SaveVehicle", function(vehicle, plate, ent, garage) 471 | MySQL.Async.execute('UPDATE `owned_vehicles` SET `vehicle` = @vehicle, `garage` = @garage, `last_garage` = @garage, `stored` = @stored WHERE `plate` = @plate', { 472 | ['@vehicle'] = json.encode(vehicle), 473 | ['@plate'] = ESX.Math.Trim(plate), 474 | ['@stored'] = 1, 475 | ['@garage'] = garage 476 | }) 477 | local ent = NetworkGetEntityFromNetworkId(ent) 478 | DeleteEntity(ent) 479 | end) 480 | 481 | lib.callback.register('just_apartments:CheckOwnership', function(source, plate) 482 | local result = MySQL.scalar.await('SELECT vehicle FROM owned_vehicles WHERE plate = ?', {plate}) 483 | if result ~= nil then 484 | return true 485 | else 486 | -- Player tried to cheat 487 | TriggerClientEvent("just_apartments:notification", source, "Wait this is a local's vehicle", nil, "error") 488 | return false 489 | end 490 | end) 491 | 492 | lib.callback.register('just_apartments:garageCheck', function(source, apartment) 493 | local _source = source 494 | local xPlayer = ESX.GetPlayerFromId(_source) 495 | local ownedapartments = MySQL.Sync.fetchAll('SELECT id, apartment, expired FROM owned_apartments WHERE owner = @owner AND apartment = @apartment AND expired = @expired', {['@owner'] = xPlayer.identifier, ['@apartment'] = apartment, ['@expired'] = 0}) 496 | if ownedapartments ~= nil then 497 | return ownedapartments 498 | end 499 | end) 500 | 501 | lib.callback.register('just_apartments:keyCheck', function(source, apartment) 502 | local _source = source 503 | local xPlayer = ESX.GetPlayerFromId(_source) 504 | local keyedApartments = MySQL.Sync.fetchAll('SELECT * FROM apartment_keys WHERE appt_name = @appt_name AND player = @player', {['@appt_name'] = apartment,['@player'] = xPlayer.identifier}) 505 | if keyedApartments ~= nil then 506 | return keyedApartments 507 | end 508 | end) -------------------------------------------------------------------------------- /client.lua: -------------------------------------------------------------------------------- 1 | local spot 2 | local ownedGarage 3 | local keyedGarages 4 | local state = nil 5 | local visible = false 6 | local passedCheck = false 7 | local checkOwnership = true 8 | local AtStash = false 9 | local currentApartment = nil 10 | local currentApartmentID = nil 11 | 12 | Citizen.CreateThread(function() 13 | while ESX == nil do 14 | TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) 15 | Citizen.Wait(0) 16 | end 17 | 18 | while ESX.GetPlayerData().job == nil do 19 | Citizen.Wait(10) 20 | end 21 | 22 | ESX.PlayerData = ESX.GetPlayerData() 23 | end) 24 | 25 | RegisterNetEvent('esx:onPlayerLogout') 26 | AddEventHandler('esx:onPlayerLogout', function() 27 | spot = nil 28 | ownedGarage = nil 29 | keyedGarages = nil 30 | state = nil 31 | visible = false 32 | passedCheck = false 33 | AtStash = false 34 | currentApartment = nil 35 | currentApartmentID = nil 36 | end) 37 | 38 | local function Blips(coords, type, label, job, blipOptions) 39 | if job then return end 40 | if blip == false then return end 41 | local blip = AddBlipForCoord(coords) 42 | SetBlipSprite(blip, blipOptions.sprite or 357) 43 | SetBlipScale(blip, blipOptions.scale or 0.8) 44 | SetBlipColour(blip, blipOptions.colour ~= nil and blipOptions.colour or type == 'car' and Config.BlipColors.Car or type == 'boat' and Config.BlipColors.Boat or Config.BlipColors.Aircraft) 45 | SetBlipAsShortRange(blip, true) 46 | BeginTextCommandSetBlipName("STRING") 47 | AddTextComponentString("Apartment") 48 | EndTextCommandSetBlipName(blip) 49 | end 50 | 51 | function comma_value(amount) 52 | local formatted = amount 53 | while true do 54 | formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') 55 | if (k==0) then 56 | break 57 | end 58 | end 59 | return formatted 60 | end 61 | 62 | function Split(s, delimiter) 63 | if s ~= nil then 64 | result = {}; 65 | for match in (s..delimiter):gmatch("(.-)"..delimiter) do 66 | table.insert(result, match); 67 | end 68 | return result; 69 | end 70 | end 71 | 72 | function onEnter(self) 73 | local apartment = Split(self.name, " ") 74 | currentApartment = apartment[1] 75 | if self.type == "garage" then 76 | 77 | elseif self.type == "entrance" then 78 | 79 | elseif self.type == "wardrobe" then 80 | 81 | elseif self.type == "stash" then 82 | 83 | elseif self.type == "exit" then 84 | 85 | end 86 | end 87 | 88 | function onExit(self) 89 | checkOwnership = true 90 | lib.hideTextUI() 91 | Citizen.Wait(100) 92 | visible = false 93 | passedCheck = false 94 | if self.type == "garage" then 95 | currentApartment = nil 96 | elseif self.type == "entrance" then 97 | elseif self.type == "wardrobe" then 98 | 99 | elseif self.type == "stash" then 100 | 101 | elseif self.type == "exit" then 102 | 103 | end 104 | end 105 | 106 | function insideZone(self) 107 | if self.type == "garage" then 108 | local ped = PlayerPedId() 109 | local inVehicle = GetVehiclePedIsIn(ped, false) 110 | local apartment = Split(self.name, " ") 111 | currentApartment = apartment[1] 112 | spot = apartment[3] 113 | if passedCheck then 114 | if not visible then 115 | if inVehicle ~= 0 then 116 | visible = true 117 | lib.showTextUI("[E] Store Vehicle", {icon = "fa-solid fa-car"}) 118 | else 119 | visible = true 120 | lib.showTextUI("[E] Open Garage", {icon = "fa-solid fa-car"}) 121 | end 122 | end 123 | if IsControlJustReleased(0, 54) then 124 | if inVehicle ~= 0 then 125 | TriggerEvent('just_apartments:viewGarages', currentApartment, 'just_apartments:StoreVehicle', inVehicle, false) 126 | passedCheck = false 127 | else 128 | TriggerEvent('just_apartments:viewGarages', currentApartment, 'just_apartments:GetOwnedVehicles', nil, true) 129 | passedCheck = false 130 | end 131 | end 132 | else 133 | ownedGarage = lib.callback.await('just_apartments:garageCheck', false, currentApartment) 134 | keyedGarages = lib.callback.await('just_apartments:keyCheck', false, currentApartment) 135 | if ownedGarage ~= nil then 136 | for i=1, #ownedGarage, 1 do 137 | if ownedGarage[i].apartment == currentApartment then 138 | passedCheck = true 139 | break 140 | end 141 | end 142 | end 143 | if keyedGarages ~= nil then 144 | for i=1, #keyedGarages, 1 do 145 | if keyedGarages[i].appt_name == currentApartment then 146 | passedCheck = true 147 | break 148 | end 149 | end 150 | end 151 | end 152 | elseif self.type == "entrance" then 153 | if not visible then 154 | visible = true 155 | lib.showTextUI("[E] Apartments", {icon = "fa-solid fa-building"}) 156 | end 157 | if IsControlJustReleased(0, 54) then 158 | if currentApartment == 'AltaStreetAppts' then 159 | TriggerEvent('just_apartments:enterExitApartment', Config.Apartments[currentApartment].exit, "Entering") 160 | else 161 | lib.callback('just_apartments:getOwnedApartments', false, function(data) 162 | local apartments = lib.callback.await('just_apartments:keyCheck', false, currentApartment) 163 | -- print(apartments, data) 164 | -- if apartments ~= nil then 165 | -- exports.xng_parsingtable:ParsingTable_cl(apartments) 166 | -- end 167 | -- exports.xng_parsingtable:ParsingTable_cl(data) 168 | TriggerEvent('just_apartments:keyEntryMenu', apartments, data) 169 | end, currentApartment, self.tpCoords) 170 | end 171 | end 172 | elseif self.type == "wardrobe" then 173 | if Config.BrpFivemAppearance or Config.ox_appearance then 174 | if currentApartment == "AltaStreetAppts" then 175 | if not visible then 176 | visible = true 177 | lib.showTextUI("[E] Wardrobe", {icon = "fa-solid fa-shirt"}) 178 | end 179 | if IsControlJustReleased(0, 54) then 180 | if Config.BrpFivemAppearance then 181 | TriggerEvent('fivem-appearance:useWardrobe') 182 | elseif Config.ox_appearance then 183 | TriggerEvent('ox_appearance:wardrobe') 184 | end end 185 | else 186 | if checkOwnership then 187 | passedCheck = lib.callback.await('just_apartments:checkApptOwnership', false, currentApartment, currentApartmentID) 188 | end 189 | if passedCheck then 190 | checkOwnership = false 191 | if not visible then 192 | visible = true 193 | lib.showTextUI("[E] Wardrobe", {icon = "fa-solid fa-shirt"}) 194 | end 195 | if IsControlJustReleased(0, 54) then 196 | if Config.BrpFivemAppearance then 197 | TriggerEvent('fivem-appearance:useWardrobe') 198 | elseif Config.ox_appearance then 199 | TriggerEvent('ox_appearance:wardrobe') 200 | end 201 | end 202 | end 203 | end 204 | end 205 | elseif self.type == "stash" then 206 | if Config.UseOxInventory then 207 | if currentApartment == "AltaStreetAppts" then 208 | if not visible then 209 | visible = true 210 | lib.showTextUI("[E] Stash", {icon = "fa-solid fa-box-open"}) 211 | end 212 | if IsControlJustReleased(0, 54) then 213 | exports.ox_inventory:openInventory('stash', {id = ("AltaStreetAppts".."0".."Stash"), owner = 0}) 214 | end 215 | else 216 | if checkOwnership then 217 | passedCheck = lib.callback.await('just_apartments:checkApptOwnership', false, currentApartment, currentApartmentID) 218 | end 219 | if passedCheck then 220 | checkOwnership = false 221 | if not visible then 222 | visible = true 223 | lib.showTextUI("[E] Stash", {icon = "fa-solid fa-box-open"}) 224 | end 225 | if IsControlJustReleased(0, 54) then 226 | exports.ox_inventory:openInventory('stash', {id = (currentApartment..currentApartmentID.."Stash"), owner = currentApartmentID}) 227 | end 228 | end 229 | end 230 | end 231 | elseif self.type == "exit" then 232 | if not visible then 233 | visible = true 234 | lib.showTextUI("[E] Exit Apartment", {icon = "fa-solid fa-door-open"}) 235 | end 236 | if IsControlJustReleased(0, 54) then 237 | if Config.Apartments[currentApartment].seperateExitPoint == true then 238 | 239 | if currentApartment == 'AltaStreetAppts' then 240 | TriggerEvent('just_apartments:enterExitApartment', Config.Apartments[currentApartment].exitPoint, "Exiting") 241 | else 242 | lib.callback('just_apartments:getPlayersAtDoor', false, function(coords, playersAtDoor, exit, keyholders) 243 | -- print(coords, playersAtDoor, exit, keyholders) 244 | TriggerEvent('just_apartments:exitMenu', coords, playersAtDoor, exit, keyholders) 245 | end, Config.Apartments[currentApartment].exitPoint, currentApartment, currentApartmentID, Config.Apartments[currentApartment].exit) 246 | end 247 | else 248 | lib.callback('just_apartments:getPlayersAtDoor', false, function(coords, playersAtDoor, exit, keyholders) 249 | TriggerEvent('just_apartments:exitMenu', coords, playersAtDoor, exit, keyholders) 250 | end, Config.Apartments[currentApartment].exitPoint, currentApartment, currentApartmentID, Config.Apartments[currentApartment].exit) 251 | end 252 | end 253 | end 254 | end 255 | 256 | for k, v in pairs(Config.Apartments) do 257 | lib.zones.box({ 258 | coords = vec3(v.entrance.x, v.entrance.y, v.entrance.z), 259 | size = vec3(3, 3, 3), 260 | rotation = v.entrance.h, 261 | debug = false, 262 | inside = insideZone, 263 | onEnter = onEnter, 264 | onExit = onExit, 265 | name = k.." Entrance", 266 | tpCoords = v.exit, 267 | type = "entrance", 268 | }) 269 | if v.wardrobe ~= nil then 270 | lib.zones.box({ 271 | coords = vec3(v.wardrobe.x, v.wardrobe.y, v.wardrobe.z), 272 | size = vec3(v.wardrobe.w, v.wardrobe.l, 4), 273 | rotation = v.wardrobe.h, 274 | debug = false, 275 | inside = insideZone, 276 | onEnter = onEnter, 277 | onExit = onExit, 278 | name = v.zone.name.." Wardrobe", 279 | type = "wardrobe", 280 | }) 281 | end 282 | if v.stash ~= nil then 283 | lib.zones.box({ 284 | coords = vec3(v.stash.x, v.stash.y, v.stash.z), 285 | size = vec3(3, 5.4, 4), 286 | rotation = v.stash.h, 287 | debug = false, 288 | inside = insideZone, 289 | onEnter = onEnter, 290 | onExit = onExit, 291 | name = v.zone.name.." Stash", 292 | type = "stash", 293 | }) 294 | end 295 | lib.zones.box({ 296 | coords = vec3(v.exit.x, v.exit.y, v.exit.z), 297 | size = vec3(3, 3, 3), 298 | rotation = v.exit.h, 299 | debug = false, 300 | inside = insideZone, 301 | onEnter = onEnter, 302 | onExit = onExit, 303 | name = v.zone.name.." Exit", 304 | tpCoords = v.entrance, 305 | type = "exit", 306 | }) 307 | if v.parking ~= nil and Config.useGarages then 308 | for k2, v2 in pairs(v.parking) do 309 | lib.zones.box({ 310 | coords = vec3(v2.x, v2.y, v2.z), 311 | size = vec3(3, 5.4, 4), 312 | rotation = v2.h, 313 | debug = false, 314 | inside = insideZone, 315 | onEnter = onEnter, 316 | onExit = onExit, 317 | name = k.." parking "..k2, 318 | type = "garage", 319 | }) 320 | end 321 | end 322 | 323 | if v.blip ~= false then 324 | Blips(vector3(v.entrance.x, v.entrance.y, v.entrance.z), v.type, v.label, v.job, v.blip) 325 | end 326 | end 327 | 328 | RegisterNetEvent('just_apartments:keyEntryMenu') 329 | AddEventHandler('just_apartments:keyEntryMenu', function (apartments, data) 330 | local data = data 331 | local options = {} 332 | TriggerEvent('just_apartments:leaseMenu', data) 333 | if not data.id then 334 | options = { 335 | { 336 | title = "Purchase "..Config.Apartments[currentApartment].label.." Apartment", 337 | description = "$"..comma_value(data.price), 338 | event = 'just_apartments:purchaseApartment', 339 | args = { 340 | currentApartment = currentApartment, 341 | coords = data.coords, 342 | id = data.id 343 | } 344 | }, 345 | { 346 | title = "Preview Appartment", 347 | description = "OOOOOO This is nice", 348 | event = 'just_apartments:enterExitApartment', 349 | args = { 350 | coords = data.coords, 351 | enteringExiting = "Viewing" 352 | } 353 | }, 354 | { 355 | title = "Ring Apartment", 356 | description = "Let Me In Now", 357 | arrow = true, 358 | serverEvent = 'just_apartments:getBuildingApartments', 359 | args = { 360 | currentApartment = currentApartment, 361 | coords = data.coords, 362 | id = data.id, 363 | ring = true 364 | } 365 | }, 366 | } 367 | else 368 | options = { 369 | { 370 | title = "Appt: "..data.id, 371 | description = "Enter", 372 | event = 'just_apartments:enterExitApartment', 373 | args = { 374 | coords = Config.Apartments[currentApartment].exit, 375 | enteringExiting = "Entering", 376 | id = data.id 377 | } 378 | }, 379 | { 380 | title = "Change Lease", 381 | description = "Should I Stay Or Should I Go Now", 382 | menu = 'just_apartments:leaseMenu', 383 | }, 384 | { 385 | title = "Ring Apartment", 386 | description = "Let Me In Now", 387 | arrow = true, 388 | serverEvent = 'just_apartments:getBuildingApartments', 389 | args = { 390 | currentApartment = currentApartment, 391 | coords = Config.Apartments[currentApartment].exit, 392 | id = data.id, 393 | ring = true 394 | } 395 | }, 396 | } 397 | end 398 | 399 | if apartments ~= nil then 400 | for i=1, #apartments, 1 do 401 | if apartments[i].id ~= nil then 402 | table.insert(options, { 403 | title = "Shared Apartments", 404 | description = "It's Not Mine But It's Still Nice", 405 | arrow = true, 406 | event = 'just_apartments:keyEntry', 407 | args = { 408 | apartments = apartments, 409 | data = data 410 | }, 411 | }) 412 | end 413 | end 414 | end 415 | lib.registerContext({ 416 | id = 'just_apartments:keyEntryMenu', 417 | title = Config.Apartments[currentApartment].label, 418 | options = options 419 | }) 420 | lib.showContext('just_apartments:keyEntryMenu') 421 | end) 422 | 423 | RegisterNetEvent('just_apartments:keyEntry') 424 | AddEventHandler('just_apartments:keyEntry', function (args) 425 | -- exports.xng_parsingtable:ParsingTable_cl(args) 426 | 427 | local apartments = args.apartments 428 | local data = args.data 429 | local options = {} 430 | for i=1, #apartments, 1 do 431 | table.insert(options, { 432 | title = "Appt: "..apartments[i].appt_id, 433 | description = "Enter", 434 | event = 'just_apartments:enterExitApartment', 435 | args = { 436 | coords = data.coords, 437 | enteringExiting = "Entering", 438 | id = apartments[i].appt_id 439 | } 440 | }) 441 | end 442 | lib.registerContext({ 443 | id = 'just_apartments:keyEntry', 444 | title = Config.Apartments[currentApartment].label.." Appts", 445 | menu = "just_apartments:keyEntryMenu", 446 | options = options 447 | }) 448 | lib.showContext('just_apartments:keyEntry') 449 | end) 450 | 451 | ---------- 452 | -- Ring -- 453 | ---------- 454 | 455 | 456 | RegisterNetEvent('just_apartments:ringMenu') 457 | AddEventHandler('just_apartments:ringMenu', function (apartments, data, identifier) 458 | local data = data 459 | local options = {} 460 | for i=1, #apartments, 1 do 461 | if apartments[i].owner ~= identifier then 462 | table.insert(options, { 463 | title = "Ring Appt: "..apartments[i].id, 464 | event = 'just_apartments:ringAppartment', 465 | args = { 466 | coords = data.coords, 467 | enteringExiting = "Entering", 468 | id = apartments[i].id, 469 | apartment = apartments[i].apartment 470 | } 471 | }) 472 | end 473 | end 474 | Citizen.Wait(100) 475 | lib.registerContext({ 476 | id = 'just_apartments:ringMenu', 477 | title = Config.Apartments[currentApartment].label.." Appts", 478 | menu = "just_apartments:keyEntryMenu", 479 | options = options 480 | }) 481 | Citizen.Wait(100) 482 | lib.showContext('just_apartments:ringMenu') 483 | end) 484 | 485 | RegisterNetEvent('just_apartments:ringAppartment') 486 | AddEventHandler('just_apartments:ringAppartment', function (data) 487 | TriggerServerEvent('just_apartments:alertOwner', data) 488 | end) 489 | 490 | ----------- 491 | -- Lease -- 492 | ----------- 493 | 494 | RegisterNetEvent('just_apartments:leaseMenu') 495 | AddEventHandler('just_apartments:leaseMenu', function (data) 496 | local data = data 497 | if data.renew == 1 then 498 | lib.registerContext({ 499 | id = 'just_apartments:leaseMenu', 500 | title = "Change Lease", 501 | menu = "just_apartments:keyEntryMenu", 502 | options = {{ 503 | title = "Cancel Lease", 504 | description = "Renews: "..string.sub(data.renewDate,5,6).."/"..string.sub(data.renewDate,7,8).."/"..string.sub(data.renewDate,1,4).." For $"..comma_value(data.price), 505 | event = 'just_apartments:changeLease', 506 | args = { 507 | renew = 0, 508 | id = data.id 509 | } 510 | }} 511 | }) 512 | elseif data.renewDate ~= nil then 513 | lib.registerContext({ 514 | id = 'just_apartments:leaseMenu', 515 | title = "Change Lease", 516 | menu = "just_apartments:keyEntryMenu", 517 | options = {{ 518 | title = "Resume Lease $"..comma_value(data.price), 519 | description = "Lease Ends: "..string.sub(data.renewDate,5,6).."/"..string.sub(data.renewDate,7,8).."/"..string.sub(data.renewDate,1,4), 520 | event = 'just_apartments:changeLease', 521 | args = { 522 | renew = 1, 523 | id = data.id 524 | } 525 | }} 526 | }) 527 | end 528 | end) 529 | 530 | RegisterNetEvent('just_apartments:changeLease') 531 | AddEventHandler('just_apartments:changeLease', function (data) 532 | TriggerServerEvent('just_apartments:changeLease', data) 533 | end) 534 | 535 | RegisterNetEvent('just_apartments:purchaseApartment') 536 | AddEventHandler('just_apartments:purchaseApartment', function (currentApartment) 537 | TriggerServerEvent('just_apartments:purchaseApartment', currentApartment.currentApartment) 538 | Citizen.Wait(250) 539 | TriggerServerEvent('just_apartments:getOwnedApartments', currentApartment.currentApartment, currentApartment.exit) 540 | end) 541 | 542 | RegisterNetEvent('just_apartments:exitMenu') 543 | AddEventHandler('just_apartments:exitMenu', function (coords, playersAtDoor, exitDoor, keyholders) 544 | local playersAtDoor = playersAtDoor 545 | 546 | local options = { 547 | { 548 | title = "Exit Apartment", 549 | description = "Go Out Into The World", 550 | event = 'just_apartments:enterExitApartment', 551 | args = { 552 | coords = coords, 553 | enteringExiting = "Exiting" 554 | } 555 | }, 556 | } 557 | 558 | if keyholders ~= nil then 559 | if #keyholders > 0 then 560 | table.insert(options, { 561 | title = "Manage Keyholders", 562 | description = "Who's Got Keys", 563 | arrow = true, 564 | event = 'just_apartments:keyholderMenu', 565 | args = { 566 | keyholders = keyholders 567 | } 568 | }) 569 | end 570 | end 571 | if playersAtDoor ~= nil then 572 | for i=1, #playersAtDoor.people, 1 do 573 | table.insert(options, { 574 | title = "Let In: "..playersAtDoor.people[i], 575 | description = "It's Always Nice To Have Company", 576 | arrow = true, 577 | event = 'just_apartments:letPlayerIn', 578 | args = { 579 | coords = exitDoor, 580 | enteringExiting = "Entering", 581 | playersAtDoor = playersAtDoor, 582 | player = playersAtDoor.people[i], 583 | id = currentApartmentID 584 | } 585 | }) 586 | end 587 | end 588 | 589 | Citizen.Wait(100) 590 | lib.registerContext({ 591 | id = 'just_apartments:exitMenu', 592 | title = "Elevator", 593 | options = options 594 | }) 595 | Citizen.Wait(100) 596 | lib.showContext('just_apartments:exitMenu') 597 | end) 598 | 599 | RegisterNetEvent('just_apartments:keyholderMenu') 600 | AddEventHandler('just_apartments:keyholderMenu', function (data) 601 | local keyholders = data.keyholders 602 | local options = {} 603 | 604 | if keyholders ~= nil then 605 | for i=1, #keyholders, 1 do 606 | table.insert(options, { 607 | title = "Name: "..keyholders[i].player_name, 608 | description = "Remove Key", 609 | event = 'just_apartments:removeApartmentKeys', 610 | args = { 611 | id = keyholders[i].id 612 | } 613 | }) 614 | end 615 | end 616 | 617 | Citizen.Wait(100) 618 | lib.registerContext({ 619 | id = 'just_apartments:keyholderMenu', 620 | title = "Keyholders", 621 | menu = "just_apartments:exitMenu", 622 | options = options 623 | }) 624 | Citizen.Wait(100) 625 | lib.showContext('just_apartments:keyholderMenu') 626 | end) 627 | 628 | RegisterNetEvent('just_apartments:removeApartmentKeys') 629 | AddEventHandler('just_apartments:removeApartmentKeys', function (data) 630 | local id = data.id 631 | TriggerServerEvent('just_apartments:removeApartmentKeys', id) 632 | end) 633 | 634 | RegisterNetEvent('just_apartments:letPlayerIn') 635 | AddEventHandler('just_apartments:letPlayerIn', function (data) 636 | TriggerServerEvent('just_apartments:bringPlayerIn', data) 637 | end) 638 | 639 | RegisterNetEvent('just_apartments:enterExitApartment') 640 | AddEventHandler('just_apartments:enterExitApartment', function (coords, enteringExiting) 641 | -- exports.xng_parsingtable:ParsingTable_cl(coords) 642 | if coords.id ~= nil then 643 | currentApartmentID = coords.id 644 | end 645 | local coords = coords 646 | local player = PlayerPedId() 647 | if currentApartment == 'AltaStreetAppts' then 648 | if IsControlJustReleased(0, 54) then 649 | if lib.progressBar({ 650 | duration = 5000, 651 | label = enteringExiting.." Apartment", 652 | useWhileDead = false, 653 | canCancel = true, 654 | disable = { 655 | move = true, 656 | }, 657 | }) then 658 | PlaySoundFrontend(-1, "CLOSED", "MP_PROPERTIES_ELEVATOR_DOORS", 1); 659 | Citizen.Wait(500) 660 | PlaySoundFrontend(-1, "Hack_Success", "DLC_HEIST_BIOLAB_PREP_HACKING_SOUNDS", 0) 661 | Citizen.Wait(500) 662 | PlaySoundFrontend(-1, "OPENED", "MP_PROPERTIES_ELEVATOR_DOORS", 1); 663 | 664 | if enteringExiting == "Entering" then 665 | TriggerServerEvent('instance:set') 666 | TriggerServerEvent('just_apartments:updateLastApartment', 'AltaStreetAppts') 667 | else 668 | TriggerServerEvent('instance:set', 0) 669 | currentApartment = nil 670 | currentApartmentID = nil 671 | TriggerServerEvent('just_apartments:updateLastApartment', nil) 672 | end 673 | SetEntityCoords(player, coords.x, coords.y, coords.z) 674 | SetEntityHeading(player, coords.h) 675 | lib.hideTextUI() 676 | end 677 | end 678 | else 679 | if lib.progressBar({ 680 | duration = 5000, 681 | label = coords.enteringExiting.." Apartment", 682 | useWhileDead = false, 683 | canCancel = true, 684 | disable = { 685 | move = true, 686 | }, 687 | }) then 688 | PlaySoundFrontend(-1, "CLOSED", "MP_PROPERTIES_ELEVATOR_DOORS", 1); 689 | Citizen.Wait(500) 690 | PlaySoundFrontend(-1, "Hack_Success", "DLC_HEIST_BIOLAB_PREP_HACKING_SOUNDS", 0) 691 | Citizen.Wait(500) 692 | PlaySoundFrontend(-1, "OPENED", "MP_PROPERTIES_ELEVATOR_DOORS", 1); 693 | 694 | if coords.enteringExiting == "Entering" then 695 | TriggerServerEvent('instance:setNamed', currentApartment..coords.id) 696 | TriggerServerEvent('just_apartments:updateLastApartment', currentApartment.." "..coords.id) 697 | currentApartmentID = coords.id 698 | SetEntityCoords(player, coords.coords.x, coords.coords.y, coords.coords.z) 699 | SetEntityHeading(player, coords.coords.h) 700 | elseif coords.enteringExiting == "Exiting" then 701 | if state == "Viewing" then 702 | TriggerServerEvent('instance:set', 0) 703 | TriggerServerEvent('just_apartments:updateLastApartment', nil) 704 | SetEntityCoords(player, Config.Apartments[currentApartment].entrance.x, Config.Apartments[currentApartment].entrance.y, Config.Apartments[currentApartment].entrance.z) 705 | SetEntityHeading(player, Config.Apartments[currentApartment].entrance.h) 706 | else 707 | TriggerServerEvent('instance:setNamed', 0) 708 | TriggerServerEvent('just_apartments:updateLastApartment', nil) 709 | SetEntityCoords(player, coords.coords.x, coords.coords.y, coords.coords.z) 710 | SetEntityHeading(player, coords.coords.h) 711 | end 712 | state = nil 713 | currentApartment = nil 714 | currentApartmentID = nil 715 | elseif coords.enteringExiting == "Viewing" then 716 | state = "Viewing" 717 | TriggerServerEvent('instance:set') 718 | SetEntityCoords(player, coords.coords.x, coords.coords.y, coords.coords.z) 719 | SetEntityHeading(player, coords.coords.h) 720 | end 721 | lib.hideTextUI() 722 | end 723 | end 724 | end) 725 | 726 | ---------- 727 | -- Keys -- 728 | ---------- 729 | 730 | TriggerEvent('chat:addSuggestion', '/giveapptkey', 'Give closest person keys') 731 | RegisterCommand('giveapptkey', function() 732 | TriggerEvent('just_apartments:givePlayerKeys') 733 | end, false) 734 | 735 | RegisterNetEvent('just_apartments:givePlayerKeys') 736 | AddEventHandler('just_apartments:givePlayerKeys', function () 737 | local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer() 738 | if closestPlayer ~= -1 and closestDistance <= 4.0 then 739 | local data = { target = GetPlayerServerId(closestPlayer), appt_id = currentApartmentID, appt_name = currentApartment } 740 | -- exports.xng_parsingtable:ParsingTable_cl(data) 741 | TriggerServerEvent('just_apartments:giveKeys', data) 742 | end 743 | end) 744 | 745 | RegisterNetEvent('just_apartments:removePlayerKeys') 746 | AddEventHandler('just_apartments:removePlayerKeys', function () 747 | TriggerServerEvent('just_apartments:removeKeys', data) 748 | end) 749 | 750 | RegisterNetEvent('just_apartments:notification') 751 | AddEventHandler('just_apartments:notification', function (Notificationtitle, Notificationdescription, Notificationtype) 752 | lib.notify({ 753 | title = Notificationtitle, 754 | description = Notificationdescription, 755 | status = Notificationtype 756 | }) 757 | end) 758 | 759 | ----------- 760 | -- Stash -- 761 | ----------- 762 | 763 | RegisterNetEvent('just_apartments:enteredStash') 764 | AddEventHandler('just_apartments:enteredStash', function (ApptID, apartment) 765 | local ApptID = ApptID 766 | local apartment = apartment 767 | AtStash = true 768 | TriggerEvent('just_apartments:useStash', ApptID, apartment) 769 | end) 770 | 771 | RegisterNetEvent('just_apartments:useStash') 772 | AddEventHandler('just_apartments:useStash', function (ApptID, apartment) 773 | local ApptID = ApptID 774 | local apartment = apartment 775 | lib.showTextUI("[E] Stash", {icon = "fa-solid fa-box-open"}) 776 | while AtStash do 777 | Citizen.Wait(0) 778 | if IsControlPressed(1, 38) then 779 | Citizen.Wait(100) 780 | exports.ox_inventory:openInventory('stash', {id = (apartment..ApptID.."Stash"), owner = ApptID}) 781 | end 782 | end 783 | end) 784 | 785 | ----------- 786 | -- Relog -- 787 | ----------- 788 | 789 | AddEventHandler('onClientResourceStart', function(resource) 790 | TriggerServerEvent('just_apartments:getLastApartment') 791 | end) 792 | 793 | RegisterNetEvent('just_apartments:spawnInProperty') 794 | AddEventHandler('just_apartments:spawnInProperty', function (property, ApptID) 795 | currentApartment = property 796 | if currentApartment == "AltaStreetAppts" then 797 | TriggerServerEvent('instance:set') 798 | else 799 | TriggerServerEvent('instance:setNamed', property..ApptID) 800 | currentApartmentID = ApptID 801 | end 802 | end) 803 | 804 | ------------ 805 | -- Garage -- 806 | ------------ 807 | 808 | RegisterNetEvent('just_apartments:viewGarages') 809 | AddEventHandler('just_apartments:viewGarages', function (currentApartment, event, vehicle, arrow) 810 | local options = {} 811 | if ownedGarage ~= nil then 812 | for i = 1, #ownedGarage do 813 | local data = ownedGarage[i] 814 | table.insert(options, { 815 | title = "Appt: "..data.id.." Garage", 816 | event = event, 817 | arrow = arrow, 818 | args = {id = data.id, name = data.apartment, vehicle = vehicle}, 819 | }) 820 | end 821 | end 822 | if keyedGarages ~= nil then 823 | for i = 1, #keyedGarages do 824 | local data = keyedGarages[i] 825 | table.insert(options, { 826 | title = "Appt: "..data.appt_id.." Garage", 827 | event = event, 828 | arrow = arrow, 829 | args = {id = data.appt_id, name = data.appt_name, vehicle = vehicle}, 830 | }) 831 | end 832 | end 833 | lib.registerContext({ 834 | id = 'just_apartments:apptGarageMenu', 835 | title = Config.Apartments[currentApartment].label.." Garage", 836 | options = options 837 | }) 838 | lib.showContext('just_apartments:apptGarageMenu') 839 | end) 840 | 841 | RegisterNetEvent('just_apartments:GetOwnedVehicles') 842 | AddEventHandler('just_apartments:GetOwnedVehicles', function (data) 843 | local vehicles = lib.callback.await('just_apartments:GetVehicles', false, data.name.."appt"..data.id) 844 | local options = {} 845 | if not vehicles then 846 | lib.registerContext({ 847 | id = 'just_apartments:GarageMenu', 848 | menu = 'just_apartments:apptGarageMenu', 849 | title = Config.Apartments[currentApartment].label.." Garage", 850 | options = {{title = "No vehicles parked"}} 851 | }) 852 | return lib.showContext('just_apartments:GarageMenu') 853 | else 854 | for i = 1, #vehicles do 855 | local data = vehicles[i] 856 | local vehicleMake = GetLabelText(GetMakeNameFromVehicleModel(data.vehicle.model)) 857 | local vehicleModel = GetLabelText(GetDisplayNameFromVehicleModel(data.vehicle.model)) 858 | local vehicleTitle = vehicleMake .. ' ' .. vehicleModel 859 | local stored = data.stored 860 | print(vehicleTitle, data.plate, stored) 861 | if stored then 862 | table.insert(options, { 863 | title = vehicleTitle, 864 | event = 'just_apartments:VehicleMenu', 865 | arrow = true, 866 | args = {name = vehicleTitle, plate = data.plate, model = vehicleModel, vehicle = data.vehicle}, 867 | metadata = { 868 | {label = 'Plate', value = data.plate}, 869 | } 870 | }) 871 | end 872 | end 873 | lib.registerContext({ 874 | id = 'just_apartments:GarageMenu', 875 | menu = 'just_apartments:apptGarageMenu', 876 | title = Config.Apartments[currentApartment].label.." Garage", 877 | options = options 878 | }) 879 | end 880 | lib.showContext('just_apartments:GarageMenu') 881 | end) 882 | 883 | RegisterNetEvent('just_apartments:VehicleMenu') 884 | AddEventHandler('just_apartments:VehicleMenu', function (data) 885 | lib.registerContext({ 886 | id = 'just_apartments:VehicleMenu', 887 | title = data.name, 888 | menu = 'just_apartments:GarageMenu', 889 | options = { 890 | { 891 | title = 'Take out vehicle', 892 | event = 'just_apartments:RequestVehicle', 893 | args = { 894 | vehicle = data.vehicle, 895 | type = 'garage' 896 | } 897 | } 898 | } 899 | }) 900 | 901 | lib.showContext('just_apartments:VehicleMenu') 902 | end) 903 | 904 | local function spawnVehicle(data, spawn) 905 | lib.requestModel(data.vehicle.model) 906 | TriggerServerEvent('just_apartments:SpawnVehicle', data.vehicle.model, data.vehicle.plate, spawn) 907 | lib.hideTextUI() 908 | Citizen.Wait(250) 909 | visible = false 910 | end 911 | 912 | RegisterNetEvent('just_apartments:RequestVehicle') 913 | AddEventHandler('just_apartments:RequestVehicle', function (data) 914 | local spawn = Config.Apartments[currentApartment].parking[spot] 915 | if ESX.Game.IsSpawnPointClear(vector3(spawn.x, spawn.y, spawn.z), 1.0) then 916 | return spawnVehicle(data, spawn) 917 | end 918 | end) 919 | 920 | RegisterNetEvent('just_apartments:StoreVehicle') 921 | AddEventHandler('just_apartments:StoreVehicle', function (data) 922 | local vehicle = data.vehicle 923 | local vehPlate = GetVehicleNumberPlateText(vehicle) 924 | local vehProps = lib.getVehicleProperties(vehicle) 925 | local isOwned = lib.callback.await('just_apartments:CheckOwnership', false, vehPlate) 926 | if isOwned and currentApartment ~= nil then 927 | TriggerServerEvent('just_apartments:SaveVehicle', vehProps, vehPlate, VehToNet(vehicle), data.name.."appt"..data.id) 928 | lib.hideTextUI() 929 | Citizen.Wait(250) 930 | visible = false 931 | end 932 | end) --------------------------------------------------------------------------------