├── 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 |
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 |
30 |
31 |
32 |
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)
--------------------------------------------------------------------------------