├── stream
├── house_1_1.ymap
├── house_1_2.ymap
├── house_1_3.ymap
├── windmill_1_1.ymap
└── windmill_1_2.ymap
├── template image assets
└── concrete_block.png
├── fxmanifest.lua
├── README.md
├── server
└── server.lua
├── utils
└── utils.lua
├── lang.lua
├── config.lua
└── client
└── client.lua
/stream/house_1_1.ymap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hiype/hiype-construction/HEAD/stream/house_1_1.ymap
--------------------------------------------------------------------------------
/stream/house_1_2.ymap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hiype/hiype-construction/HEAD/stream/house_1_2.ymap
--------------------------------------------------------------------------------
/stream/house_1_3.ymap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hiype/hiype-construction/HEAD/stream/house_1_3.ymap
--------------------------------------------------------------------------------
/stream/windmill_1_1.ymap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hiype/hiype-construction/HEAD/stream/windmill_1_1.ymap
--------------------------------------------------------------------------------
/stream/windmill_1_2.ymap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hiype/hiype-construction/HEAD/stream/windmill_1_2.ymap
--------------------------------------------------------------------------------
/template image assets/concrete_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hiype/hiype-construction/HEAD/template image assets/concrete_block.png
--------------------------------------------------------------------------------
/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | fx_version 'cerulean'
2 | game 'gta5'
3 |
4 | description 'Construction job for QBCore'
5 | version '1.0.0'
6 |
7 | shared_scripts {
8 | '@qb-core/shared/locale.lua',
9 | 'config.lua',
10 | '@qb-core/shared/items.lua',
11 | 'utils/utils.lua',
12 | 'lang.lua'
13 | }
14 |
15 | server_scripts {
16 | 'server/server.lua'
17 | }
18 |
19 | client_scripts {
20 | '@PolyZone/client.lua',
21 | '@PolyZone/BoxZone.lua',
22 | 'client/client.lua'
23 | }
24 |
25 | this_is_a_map 'yes'
26 |
27 | lua54 'yes'
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Construction job
2 |
3 | Building simulator in gta has arrived! Of course, this is still quite a new script but i believe it will do for now. If you have any problems, please post them in the Issues tab. There WILL be issues...i think.
4 |
5 | 📽️⬇️ Gameplay video below! 📽️⬇️
6 |
7 | [](https://www.youtube.com/watch?v=Sst28eVZsRY)
8 |
9 | ## Requirements
10 | - [QbCore framework](https://github.com/qbcore-framework)
11 | - Polyzone
12 | - qb-target
13 |
14 | ## Installation
15 | 1. Download this resource
16 | 2. Insert it into your game folder
17 | 3. Make sure to add this resource to your server.cfg file too
18 | 4. Move all of the .png images from **template image assets** folder to qb-inventory -> html -> images
19 | 5. (Optional) Delete the template image assets folder
20 |
21 | ## Q&A
22 | **Q: I cannot interact with a box zone, what do I do?**
23 | **A:** Check that the box zone height difference between MinZ and MaxZ is not too big, this in my experience breaks the target somehow.
24 |
25 |
26 | **Q: All of the crafting items in my inventory are missing images, how do I fix this?**
27 | **A:** Please read the 4th step of the installation guide.
--------------------------------------------------------------------------------
/server/server.lua:
--------------------------------------------------------------------------------
1 | local QBCore = exports['qb-core']:GetCoreObject()
2 |
3 | -- Add jobs and items to qb-core
4 | QBCore.Functions.AddJobs(Config.Job)
5 | QBCore.Functions.AddItems(Config.Items)
6 |
7 | RegisterNetEvent('hiype-construction:server:addItem', function(pItemName, pAmount)
8 | local src = source
9 | local Player = QBCore.Functions.GetPlayer(src)
10 |
11 | Player.Functions.AddItem(pItemName, pAmount)
12 | end)
13 |
14 | RegisterNetEvent('hiype-construction:server:removeItem', function(pItemName, pAmount)
15 | local src = source
16 | local Player = QBCore.Functions.GetPlayer(src)
17 |
18 | Player.Functions.RemoveItem(pItemName, pAmount)
19 | end)
20 |
21 | RegisterNetEvent('hiype-construction:server:set-job', function(job, grade)
22 | local src = source
23 | local Player = QBCore.Functions.GetPlayer(src)
24 |
25 | if not Player then return end
26 | Player.Functions.SetJob(job, grade)
27 | end)
28 |
29 | RegisterNetEvent('hiype-construction:server:add-money', function(amount, m_type)
30 | local Player = QBCore.Functions.GetPlayer(source)
31 | if m_type == "cash" then
32 | Player.Functions.AddMoney("cash", amount)
33 | else
34 | Player.Functions.AddMoney("bank", amount)
35 | end
36 | end)
--------------------------------------------------------------------------------
/utils/utils.lua:
--------------------------------------------------------------------------------
1 | -- From qb-core debug.lua
2 | function tPrint(tbl, indent)
3 | indent = indent or 0
4 | for k, v in pairs(tbl) do
5 | local tblType = type(v)
6 | local formatting = ("%s ^3%s:^0"):format(string.rep(" ", indent), k)
7 |
8 | if tblType == "table" then
9 | print(formatting)
10 | tPrint(v, indent + 1)
11 | elseif tblType == 'boolean' then
12 | print(("%s^1 %s ^0"):format(formatting, v))
13 | elseif tblType == "function" then
14 | print(("%s^9 %s ^0"):format(formatting, v))
15 | elseif tblType == 'number' then
16 | print(("%s^5 %s ^0"):format(formatting, v))
17 | elseif tblType == 'string' then
18 | print(("%s ^2'%s' ^0"):format(formatting, v))
19 | else
20 | print(("%s^2 %s ^0"):format(formatting, v))
21 | end
22 | end
23 | end
24 |
25 | function CountTableElements(arr)
26 | local counter = 0
27 | for k, v in pairs(arr) do
28 | counter = counter + 1
29 | end
30 | return counter
31 | end
32 |
33 | function RemoveElementFromTable(arr, elem)
34 | local newarr = {}
35 | for k, v in pairs(arr) do
36 | if k ~= elem then
37 | newarr[k] = v
38 | end
39 | end
40 | return newarr
41 | end
--------------------------------------------------------------------------------
/lang.lua:
--------------------------------------------------------------------------------
1 | local Translations = {
2 | info = {
3 | signing_contract = 'Signing contract',
4 | request_build = 'Request build',
5 | build_location_marked = 'Build location marked on map',
6 | construction_sites_header = 'Construction sites',
7 | craft = 'Craft',
8 | crafting = 'Crafting',
9 | signup = 'Sign up',
10 | signout = 'Sign out',
11 | cancel_build = 'Cancel build',
12 | rent_truck = 'Rent truck',
13 | return_truck = 'Return truck',
14 | interact = 'Interact',
15 | deliver_deliverables = 'Deliver necessary objects',
16 | preparation_work = 'Preparation work',
17 | unpack_deliverables = 'Unpack deliverables',
18 | job_done = 'Build completed',
19 | signing_job_documents = 'Signing job documents'
20 | },
21 | error = {
22 | you_have_an_active_build = 'You already have an active build',
23 | already_active = 'You already work as a construction worker',
24 | not_active = 'You need to work as construction worker first',
25 | not_all_items_in_inventory = 'Not all required items are in your inventory',
26 | no_active_build = 'There are no currently active builds',
27 | truck_already_rented_set_waypoint = 'The truck has already been rented, waypoint to it has been set',
28 | truck_not_close_enough = 'Rented truck is not close enough',
29 | you_dont_have_item = 'You dont have %s',
30 | out_of_range = 'Out of range',
31 | not_in_range_of_destination = 'Not in range of destination'
32 | }
33 | }
34 |
35 | Lang = nil
36 |
37 |
38 | Lang = Locale:new({
39 | phrases = Translations,
40 | warnOnMissing = true
41 | })
--------------------------------------------------------------------------------
/config.lua:
--------------------------------------------------------------------------------
1 | Config = {} -- Don't touch this
2 | Config.DeliveryTruck = {}
3 |
4 | Config.PolyzoneDebug = false
5 | Config.IsVehicleAffectedByCargo = true
6 |
7 | Config.NPCLocation = vector4(-96.7, -1014.0, 26.28, 166.69)
8 | Config.NPCModel = 'cs_floyd'
9 | Config.ContractSignTime = 5000
10 | Config.ModelLoadingTimeout = 7000
11 |
12 | -- Workbench
13 | Config.WorkbenchModel = 'prop_tool_bench02'
14 | Config.WorkbenchLocation = vector4(1074.71, -1996.75, 29.89, 232.27)
15 |
16 | Config.BlipScale = 0.8
17 |
18 | Config.DeliveryTruck.Model = 'flatbed'
19 | Config.DeliveryTruck.SpawnLocation = vector4(-121.66, -1041.46, 27.27, 236.55)
20 | Config.DeliveryTruck.RentPrice = 500
21 | Config.DeliveryTruck.RentReturn = 200
22 | Config.DeliveryTruck.SignTime = 7000
23 | Config.DeliveryTruck.RentPriceType = 'cash'
24 | Config.DeliveryTruck.RentReturnPriceType = 'cash'
25 | Config.DeliveryTruck.RentReturnDistance = 30
26 |
27 | Config.ConstructionSites = {
28 | ['windmill_park_construction_windmill'] = {
29 | type = 'object',
30 | listingIcon = 'fa-solid fa-wind',
31 | listingText = 'Build the windmill',
32 | locationName = 'Construction | Windmill',
33 | buildObjectLocation = vector4(2237.47, 1617.56, 74.77, 170.92),
34 | reward = 6000,
35 | rewardType = 'cash',
36 | pickupDropoff = vector3(2221.68, 1615.73, 75.72),
37 | deliverables = {
38 | ['pipes_1'] = {
39 | model = 'prop_pipes_ld_01',
40 | blipName = "Deliverable | Pipes",
41 | interactionText = 'Moving pipes',
42 | pickupLocation = vector4(-103.45, -1051.17, 26.32, 73.76),
43 | pickupDistance = 10,
44 | pickupDestination = vector4(2230.4, 1631.83, 75.35, 358.06),
45 | vehiclePowerModifier = 0.5,
46 | pickupInteractionTime = 5000,
47 | placementOnTruck = {
48 | x = 0.0,
49 | y = -3.3,
50 | z = 0.8,
51 | xRot = 0.0,
52 | yRot = 0.0,
53 | zRot = 0.0
54 | },
55 | RUNTIME_delivered = false, -- Dynamicaly changes at runtime, don't change this
56 | RUNTIME_unpacked = false
57 | },
58 | },
59 | workStages = {
60 | {
61 | ipl = nil,
62 | name = 'Building preparation',
63 | interactionIcon = 'fa-solid fa-hammer',
64 | interactionText = 'Prepare',
65 | interactionSubText = 'Unpacking material',
66 | interactionTime = 7000,
67 | unpackDeliverables = true,
68 | transitionLocation = vector4(2228.03, 1616.41, 75.00, 258.7),
69 | locations = {
70 | { model = 'prop_conc_blocks01c', coord = vector4(2230.97, 1612.54, 74.55, 273.59) },
71 | { model = 'prop_conc_blocks01c', coord = vector4(2231.18, 1619.63, 74.64, 338.08) },
72 | }
73 | },
74 | {
75 | ipl = 'windmill_1_1',
76 | name = 'Base building',
77 | interactionIcon = 'fa-solid fa-hammer',
78 | interactionText = 'Build',
79 | interactionSubText = 'Building windmill base',
80 | interactionTime = 7000,
81 | transitionLocation = vector4(2228.03, 1616.41, 75.73, 258.7),
82 | requirements = {
83 | ['windmill_base_part'] = 1
84 | },
85 | locations = {
86 | { model = 'prop_conc_blocks01c', coord = vector4(2230.97, 1612.54, 74.55, 273.59) },
87 | { model = 'prop_conc_blocks01c', coord = vector4(2231.18, 1619.63, 74.64, 338.08) },
88 | }
89 | },
90 | {
91 | ipl = 'windmill_1_2',
92 | name = 'Finish build',
93 | interactionIcon = 'fa-solid fa-hammer',
94 | interactionText = 'Finish build',
95 | interactionSubText = 'Adding finishing touches...',
96 | interactionItem = 'windmill_details_part',
97 | interactionTime = 30000,
98 | interactionCount = 1,
99 | transitionLocation = vector4(2228.03, 1616.41, 75.73, 258.7),
100 | interactionBox = {
101 | location = vector3(2237.13, 1617.96, 75.55),
102 | length = 4,
103 | width = 4,
104 | heading = 0,
105 | minZ = 74.8,
106 | maxZ = 78.8
107 | },
108 | }
109 | }
110 | },
111 | ['mirror_park_construction_house'] = {
112 | type = 'house',
113 | listingIcon = 'fa-solid fa-house',
114 | listingText = 'Build the house',
115 | locationName = 'Construction | Mirror park house',
116 | buildObjectLocation = vector4(1392.8, -768.35, 66.3, 31.16),
117 | reward = 8000,
118 | rewardType = 'cash',
119 | pickupDropoff = vector3(1377.29, -745.43, 67.23),
120 | deliverables = {
121 | ['pipes_1'] = {
122 | model = 'prop_pipes_01b',
123 | blipName = "Deliverable | Pipes",
124 | interactionText = 'Moving pipes',
125 | pickupLocation = vector4(-103.45, -1051.17, 26.32, 73.76),
126 | pickupDistance = 10,
127 | pickupDestination = vector4(1394.39, -755.12, 66.40, 237.42),
128 | vehiclePowerModifier = 0.5,
129 | pickupInteractionTime = 5000,
130 | placementOnTruck = {
131 | x = 0.0,
132 | y = -3.0,
133 | z = 0.4,
134 | xRot = 0.0,
135 | yRot = 0.0,
136 | zRot = 0.0
137 | },
138 | RUNTIME_delivered = false, -- Dynamicaly changes at runtime, don't change this
139 | RUNTIME_unpacked = false
140 | },
141 | },
142 | workStages = {
143 | {
144 | ipl = 'house_1_1',
145 | name = 'Base building',
146 | interactionIcon = 'fa-solid fa-hammer',
147 | interactionText = 'Build',
148 | interactionSubText = 'Building house base',
149 | interactionTime = 5000,
150 | unpackDeliverables = true,
151 | transitionLocation = vector4(1383.04, -755.6, 67.19, 220.11),
152 | requirements = {
153 | ['house_base_part'] = 1
154 | },
155 | locations = {
156 | { model = 'prop_conc_blocks01c', coord = vector4(1384.5, -760.85, 65.85, 216.49) },
157 | { model = 'prop_conc_blocks01c', coord = vector4(1390.4, -763.23, 65.93, 214.47) },
158 | { model = 'prop_conc_blocks01c', coord = vector4(1394.94, -760.95, 65.88, 270.79) },
159 | { model = 'prop_conc_blocks01c', coord = vector4(1396.92, -769.06, 65.51, 211.53) },
160 | }
161 | },
162 | {
163 | ipl = 'house_1_2',
164 | name = 'Wall and roof building',
165 | interactionIcon = 'fa-solid fa-hammer',
166 | interactionText = 'Build',
167 | interactionSubText = 'Building house walls and roof',
168 | interactionTime = 6000,
169 | transitionLocation = vector4(1383.04, -755.6, 67.19, 220.11),
170 | requirements = {
171 | ['house_wall_part'] = 1,
172 | ['house_roof_part'] = 1,
173 | },
174 | locations = {
175 | { model = 'prop_paints_pallete01', coord = vector4(1390.31, -766.23, 65.64, 193.94) },
176 | { model = 'prop_paints_pallete01', coord = vector4(1395.85, -769.65, 65.55, 174.58) },
177 | { model = 'prop_paints_pallete01', coord = vector4(1400.03, -772.2, 65.44, 207.71) },
178 | { model = 'prop_paints_pallete01', coord = vector4(1391.96, -776.15, 65.35, 136.1) },
179 | }
180 | },
181 | {
182 | ipl = 'house_1_3',
183 | name = 'Interior building',
184 | interactionIcon = 'fa-solid fa-hammer',
185 | interactionText = 'Build interior',
186 | interactionSubText = 'Building house interior',
187 | interactionItem = 'house_interior_part',
188 | interactionTime = 15000,
189 | interactionCount = 5,
190 | transitionLocation = vector4(1383.04, -755.6, 67.19, 220.11),
191 | interactionBox = {
192 | location = vector3(1393.62, -769.82, 67.19),
193 | length = 18.4,
194 | width = 16,
195 | heading = 210,
196 | minZ = 66.19,
197 | maxZ = 70.39
198 | },
199 | }
200 | }
201 | }
202 | }
203 |
204 | Config.Job = {
205 | ['construction'] = {
206 | label = 'Construction',
207 | defaultDuty = true,
208 | offDutyPay = false,
209 | grades = {
210 | ['0'] = {
211 | name = 'Helper',
212 | payment = 100
213 | }
214 | }
215 | }
216 | }
217 |
218 | Config.ItemRequirements = {
219 | ['house_base_part'] = {
220 | ['plastic'] = 3,
221 | ['steel'] = 10,
222 | ['aluminum'] = 15
223 | },
224 | ['house_wall_part'] = {
225 | ['plastic'] = 5,
226 | ['steel'] = 4,
227 | ['rubber'] = 5,
228 | ['glass'] = 10
229 | },
230 | ['house_roof_part'] = {
231 | ['plastic'] = 10,
232 | ['steel'] = 6,
233 | ['rubber'] = 2,
234 | },
235 | ['house_interior_part'] = {
236 | ['plastic'] = 10,
237 | ['glass'] = 4,
238 | ['rubber'] = 6,
239 | },
240 | ['windmill_base_part'] = {
241 | ['plastic'] = 3,
242 | ['steel'] = 10,
243 | ['aluminum'] = 15
244 | },
245 | ['windmill_details_part'] = {
246 | ['plastic'] = 10,
247 | ['glass'] = 4,
248 | ['rubber'] = 6,
249 | }
250 | }
251 |
252 | Config.Items = {
253 | ['house_base_part'] = {
254 | name = 'house_base_part',
255 | label = 'House base part',
256 | weight = 10,
257 | type = 'item',
258 | image = 'concrete_block.png',
259 | unique = false,
260 | useable = false,
261 | shouldClose = true,
262 | combinable = nil,
263 | description = 'Used in house construction',
264 | },
265 | ['house_wall_part'] = {
266 | name = 'house_wall_part',
267 | label = 'House wall part',
268 | weight = 10,
269 | type = 'item',
270 | image = 'concrete_block.png',
271 | unique = false,
272 | useable = false,
273 | shouldClose = true,
274 | combinable = nil,
275 | description = 'Used in house construction',
276 | },
277 | ['house_roof_part'] = {
278 | name = 'house_roof_part',
279 | label = 'House roof part',
280 | weight = 10,
281 | type = 'item',
282 | image = 'concrete_block.png',
283 | unique = false,
284 | useable = false,
285 | shouldClose = true,
286 | combinable = nil,
287 | description = 'Used in house construction',
288 | },
289 | ['house_interior_part'] = {
290 | name = 'house_interior_part',
291 | label = 'House interior part',
292 | weight = 10,
293 | type = 'item',
294 | image = 'concrete_block.png',
295 | unique = false,
296 | useable = false,
297 | shouldClose = true,
298 | combinable = nil,
299 | description = 'Used in house construction',
300 | },
301 | ['windmill_base_part'] = {
302 | name = 'windmill_base_part',
303 | label = 'Windmill base part',
304 | weight = 10,
305 | type = 'item',
306 | image = 'concrete_block.png',
307 | unique = false,
308 | useable = false,
309 | shouldClose = true,
310 | combinable = nil,
311 | description = 'Used in windmill construction',
312 | },
313 | ['windmill_details_part'] = {
314 | name = 'windmill_details_part',
315 | label = 'Windmill details part',
316 | weight = 10,
317 | type = 'item',
318 | image = 'concrete_block.png',
319 | unique = false,
320 | useable = false,
321 | shouldClose = true,
322 | combinable = nil,
323 | description = 'Used in windmill construction',
324 | },
325 | }
326 |
327 | Config.IPLs = {
328 | 'house_1_1',
329 | 'house_1_2',
330 | 'house_1_3',
331 | 'windmill_1_1',
332 | 'windmill_1_2'
333 | }
334 |
--------------------------------------------------------------------------------
/client/client.lua:
--------------------------------------------------------------------------------
1 | local QBCore = exports['qb-core']:GetCoreObject()
2 |
3 |
4 | local isBuildActive = false
5 | local isAnimating = false
6 | local isAttached = false
7 |
8 | local npc
9 | local blip
10 | local workbench
11 | local truck
12 | local loadBox
13 | local currentSpeedModifier
14 | local titleText
15 | local subText
16 |
17 | local deliverableObjects = {}
18 |
19 | RegisterCommand('loadipl', function(source, args)
20 | RequestIpl(args[1])
21 | end, false)
22 |
23 | RegisterCommand('removeipl', function(source, args)
24 | RemoveIpl(args[1])
25 | end, false)
26 |
27 | local function IsPlayerJobLabelCorrect()
28 | local PlayerData = QBCore.Functions.GetPlayerData()
29 |
30 | for _, v in pairs(Config.Job) do
31 | if PlayerData.job.label == v.label then return true end
32 | end
33 |
34 | return false
35 | end
36 |
37 | local function RemoveAllIPLs()
38 | local IPLs = Config.IPLs
39 | for i = 1, #IPLs do
40 | RemoveIpl(IPLs[i])
41 | end
42 | end
43 |
44 | local function LoadModel(pModel)
45 | RequestModel(pModel)
46 | while not HasModelLoaded(pModel) do
47 | Wait(5)
48 | end
49 | end
50 |
51 | local function AddBlipCoord(coord, name)
52 | blip = AddBlipForCoord(coord)
53 | SetBlipSprite(blip, 478)
54 | SetBlipScale(blip, Config.BlipScale)
55 | SetBlipAsShortRange(blip, true)
56 | BeginTextCommandSetBlipName("STRING")
57 | AddTextComponentString(name)
58 | EndTextCommandSetBlipName(blip)
59 | end
60 |
61 | local function PlayerHasAllItemsCraft(pItemKey)
62 | for k, v in pairs(Config.ItemRequirements[pItemKey]) do
63 | if not QBCore.Functions.HasItem(k, v) then return false end
64 | end
65 |
66 | return true
67 | end
68 |
69 | local function Draw2DText(x, y, text, scale, r, g, b, a)
70 | SetTextFont(4)
71 | SetTextProportional(7)
72 | SetTextScale(scale, scale)
73 | SetTextColour(r, g, b, a)
74 | SetTextDropShadow(0, 0, 0, 0, 255)
75 | SetTextEdge(4, 0, 0, 0, 255)
76 | SetTextEntry("STRING")
77 | AddTextComponentString(text)
78 | DrawText(x, y)
79 | end
80 |
81 | local function GetItemLabel(key)
82 | for k, v in pairs(Config.Items) do
83 | if k == key then
84 | return v.label
85 | end
86 | end
87 | end
88 |
89 | local function PlayerHasAllItems(pItemsArr)
90 | if type(pItemsArr) == 'table' then
91 | for k, v in pairs(pItemsArr) do
92 | if not QBCore.Functions.HasItem(k, v) then
93 | QBCore.Functions.Notify(string.format(Lang:t('error.you_dont_have_item'), GetItemLabel(k)), 'error', 5000)
94 | return false
95 | end
96 | end
97 | else
98 | if not QBCore.Functions.HasItem(pItemsArr, 1) then
99 | QBCore.Functions.Notify(string.format(Lang:t('error.you_dont_have_item'), tostring(pItemsArr)), 'error', 5000)
100 | return false
101 | end
102 | end
103 |
104 | return true
105 | end
106 |
107 | local function RemoveAllItems(pItemsArr)
108 | for k, v in pairs(pItemsArr) do
109 | TriggerServerEvent('hiype-construction:server:removeItem', k, v)
110 | end
111 | end
112 |
113 | local function GenerateSubText(stage)
114 | local text = ""
115 |
116 | if stage.locations ~= nil then
117 | if stage.requirements ~= nil then
118 | for k, _ in pairs(stage.requirements) do
119 | text = text .. CountTableElements(stage.locations) .. 'x ' .. GetItemLabel(k) .. '\n'
120 | end
121 | end
122 |
123 | if stage.unpackDeliverables then
124 | text = text .. Lang:t("info.unpack_deliverables") .. '\n'
125 | end
126 | else
127 | text = text .. stage.interactionCount .. 'x ' .. GetItemLabel(stage.interactionItem) .. '\n'
128 | end
129 |
130 | return text
131 | end
132 |
133 | local function RemoveItem(pItemName, pAmount)
134 | TriggerServerEvent('hiype-construction:server:removeItem', pItemName, pAmount)
135 | end
136 |
137 | local function GenerateItemRequirementsString(pItemKey)
138 | local str = ''
139 |
140 | for k, v in pairs(Config.ItemRequirements[pItemKey]) do
141 | str = str .. string.format('%s: %s
', QBCore.Shared.Items[k].label, v)
142 | end
143 |
144 | return str
145 | end
146 |
147 | local function loadAnimDict(dict)
148 | while not HasAnimDictLoaded(dict) do
149 | RequestAnimDict(dict)
150 | Wait(5)
151 | end
152 | end
153 |
154 | local function AllDeliverablesDelivered(deliverables)
155 | for k, v in pairs(deliverables) do
156 | if not v.RUNTIME_delivered then
157 | return false
158 | end
159 | end
160 |
161 | return true
162 | end
163 |
164 | local function AllDeliverablesUnpacked(deliverables)
165 | for _, v in pairs(deliverables) do
166 | if not v.RUNTIME_unpacked then
167 | return false
168 | end
169 | end
170 |
171 | return true
172 | end
173 |
174 | local function DeleteDeliverables()
175 | for _, v in pairs(deliverableObjects) do
176 | DeleteEntity(v)
177 | end
178 | end
179 |
180 | -- Function from dpemotes
181 | local function AddPropToPlayer(propHash, bone, off1, off2, off3, rot1, rot2, rot3)
182 | local Player = PlayerPedId()
183 | local x, y, z = table.unpack(GetEntityCoords(Player))
184 | local timeout = Config.ModelLoadingTimeout
185 | local loadingFailed = false
186 | local prop
187 |
188 | RequestModel(propHash)
189 | while not HasModelLoaded(propHash) do
190 | if timeout < 5 then
191 | print('Model loading timed out for prop: ' .. tostring(propHash))
192 | loadingFailed = true
193 | return
194 | end
195 |
196 | timeout = timeout - 5
197 | Wait(5)
198 | end
199 |
200 | if not loadingFailed then
201 | prop = CreateObject(GetHashKey(propHash), x, y, z + 0.2, true, true, true)
202 | SetEntityInvincible(prop, true)
203 | AttachEntityToEntity(prop, Player, GetPedBoneIndex(Player, bone), off1, off2, off3, rot1, rot2, rot3, true, true,
204 | false, true, 1, true)
205 | SetModelAsNoLongerNeeded(prop)
206 | end
207 |
208 | return prop
209 | end
210 |
211 | -- Edited function from qb-mechanicjob
212 | local function Anim(time, dict, anim)
213 | CreateThread(function()
214 | loadAnimDict(dict)
215 | isAnimating = true
216 |
217 | TaskPlayAnim(PlayerPedId(), dict, anim, 3.0, 3.0, -1, 16, 0, false, false, false)
218 |
219 | while true do
220 | TaskPlayAnim(PlayerPedId(), dict, anim, 3.0, 3.0, -1, 16, 0, 0, 0, 0)
221 | Wait(500)
222 | time = time - 500
223 | if time <= 0 or not isAnimating then
224 | StopAnimTask(PlayerPedId(), dict, anim, 1.0)
225 | return
226 | end
227 | end
228 | end)
229 | end
230 |
231 | -- Edited function from qb-mechanicjob
232 | local function AnimWithProp(time, dict, anim, propHash, bone, location)
233 | CreateThread(function()
234 | loadAnimDict(dict)
235 | isAnimating = true
236 |
237 | TaskPlayAnim(PlayerPedId(), dict, anim, 3.0, 3.0, -1, 16, 0, false, false, false)
238 | local prop = AddPropToPlayer(propHash, bone, location[1], location[2], location[3], location[4], location[5],
239 | location[6])
240 |
241 | while true do
242 | TaskPlayAnim(PlayerPedId(), dict, anim, 3.0, 3.0, -1, 16, 0, 0, 0, 0)
243 | Wait(500)
244 | time = time - 500
245 | if time <= 0 or not isAnimating then
246 | StopAnimTask(PlayerPedId(), dict, anim, 1.0)
247 | DeleteObject(prop)
248 | return
249 | end
250 | end
251 | end)
252 | end
253 |
254 | local function SpawnDeliverables(deliverables)
255 | for k, v in pairs(deliverables) do
256 | LoadModel(v.model)
257 | local object = CreateObject(v.model, v.pickupLocation.x, v.pickupLocation.y, v.pickupLocation.z, true, true,
258 | false)
259 | SetEntityHeading(object, v.pickupLocation.w)
260 | SetEntityInvincible(object, true)
261 | FreezeEntityPosition(object, true)
262 |
263 | local blip = AddBlipForEntity(object)
264 | SetBlipScale(blip, Config.BlipScale)
265 | BeginTextCommandSetBlipName("BLIPNAME_CONSTR")
266 | AddTextComponentString(v.blipName)
267 | EndTextCommandSetBlipName(blip)
268 |
269 | exports['qb-target']:AddTargetEntity(object, {
270 | options = {
271 | {
272 | num = 1,
273 | icon = 'fa-solid fa-hand',
274 | label = Lang:t("info.interact"),
275 | action = function(entity)
276 | if not v.RUNTIME_delivered then
277 | if not isAttached then
278 | if #(GetEntityCoords(entity) - GetEntityCoords(truck)) <= v.pickupDistance then
279 | Anim(v.pickupInteractionTime, "anim@amb@clubhouse@tutorial@bkr_tut_ig3@",
280 | "machinic_loop_mechandplayer")
281 | QBCore.Functions.Progressbar('prepare_base_progress_bar' .. k, v.interactionText,
282 | v.pickupInteractionTime, false, true, {
283 | disableMovement = true,
284 | disableCarMovement = true,
285 | disableMouse = false,
286 | disableCombat = true
287 | }, {}, {}, {}, function()
288 | AttachEntityToEntity(entity, truck,
289 | GetEntityBoneIndexByName(truck, 'frame_pickup_1'),
290 | v.placementOnTruck.x,
291 | v.placementOnTruck.y,
292 | v.placementOnTruck.z,
293 | v.placementOnTruck.xRot,
294 | v.placementOnTruck.yRot,
295 | v.placementOnTruck.zRot, false, false, true, false, 2, true)
296 | RemoveBlip(blip)
297 | isAttached = true
298 | currentSpeedModifier = v.vehiclePowerModifier
299 | end, function() end
300 | )
301 | else
302 | QBCore.Functions.Notify(Lang:t('error.out_of_range'), 'error', 5000)
303 | end
304 | else
305 | if loadBox:isPointInside(GetEntityCoords(truck)) then
306 | Anim(v.pickupInteractionTime, "anim@amb@clubhouse@tutorial@bkr_tut_ig3@",
307 | "machinic_loop_mechandplayer")
308 | QBCore.Functions.Progressbar('prepare_base_progress_bar' .. k, v.interactionText,
309 | v.pickupInteractionTime, false, true, {
310 | disableMovement = true,
311 | disableCarMovement = true,
312 | disableMouse = false,
313 | disableCombat = true
314 | }, {}, {}, {}, function()
315 | DetachEntity(entity, false, false)
316 | SetEntityCoords(entity, v.pickupDestination.x, v.pickupDestination.y,
317 | v.pickupDestination.z, false, false, false, true)
318 | SetEntityHeading(entity, v.pickupDestination.w)
319 | SetEntityInvincible(entity, true)
320 | SetModelAsNoLongerNeeded(v.model)
321 | blip = AddBlipForEntity(object)
322 | SetBlipScale(blip, Config.BlipScale)
323 | BeginTextCommandSetBlipName("blip_name_" .. tostring(k))
324 | AddTextComponentString(v.blipName)
325 | EndTextCommandSetBlipName(blip)
326 |
327 | isAttached = false
328 | v.RUNTIME_delivered = true
329 | currentSpeedModifier = 1 -- Resets vehicle speed to stock
330 | end, function() end
331 | )
332 | else
333 | QBCore.Functions.Notify(Lang:t('error.not_in_range_of_destination'), 'error', 5000)
334 | end
335 | end
336 | else
337 | Anim(v.pickupInteractionTime, "anim@amb@clubhouse@tutorial@bkr_tut_ig3@",
338 | "machinic_loop_mechandplayer")
339 | QBCore.Functions.Progressbar('prepare_deliverables_progress_bar' .. k, v.interactionText,
340 | v.pickupInteractionTime, false, true, {
341 | disableMovement = true,
342 | disableCarMovement = true,
343 | disableMouse = false,
344 | disableCombat = true
345 | }, {}, {}, {}, function()
346 | exports['qb-target']:RemoveTargetEntity(object, 'Do I need this?')
347 | DeleteEntity(entity)
348 | v.RUNTIME_unpacked = true
349 | end, function() end
350 | )
351 | end
352 | end,
353 | }
354 | },
355 | distance = 2.5,
356 | })
357 | end
358 | end
359 |
360 | local function AddWorkbench()
361 | local workbenchLocation = Config.WorkbenchLocation
362 | local workbenchModel = Config.WorkbenchModel
363 |
364 | LoadModel(workbenchModel)
365 | workbench = CreateObject(workbenchModel, workbenchLocation.x, workbenchLocation.y, workbenchLocation.z, false, true,
366 | false)
367 | SetEntityHeading(workbench, workbenchLocation.w)
368 |
369 | exports['qb-target']:AddTargetEntity(workbench, {
370 | options = {
371 | {
372 | num = 1,
373 | icon = 'fa-solid fa-hammer',
374 | label = Lang:t("info.craft"),
375 | action = function(entity)
376 | local craftables = {}
377 |
378 | craftables[#craftables + 1] = {
379 | isMenuHeader = true,
380 | header = Lang:t("info.construction_sites_header"),
381 | icon = nil
382 | }
383 |
384 | for k, v in pairs(Config.Items) do
385 | craftables[#craftables + 1] = {
386 | header = v.label,
387 | txt = GenerateItemRequirementsString(k),
388 | icon = 'fa-solid fa-hammer',
389 | params = {
390 | event = 'hiype-construction:client:craft-menu-return',
391 | args = {
392 | name = k,
393 | value = v
394 | }
395 | }
396 | }
397 | end
398 |
399 | exports['qb-menu']:openMenu(craftables)
400 | end
401 | },
402 | },
403 | distance = 2.5,
404 | })
405 | end
406 |
407 | local function StopBuild()
408 | isBuildActive = false
409 | RemoveAllIPLs()
410 | end
411 |
412 | local function StartBuild(pJobKey, pJobValue)
413 | CreateThread(function()
414 | titleText = Lang:t("info.preparation_work")
415 | subText = Lang:t("info.deliver_deliverables")
416 |
417 | isBuildActive = true
418 | QBCore.Functions.Notify(Lang:t("info.build_location_marked"), 'primary', 5000)
419 | AddBlipCoord(pJobValue.pickupDropoff, pJobValue.locationName)
420 | SpawnDeliverables(pJobValue.deliverables)
421 |
422 | loadBox = BoxZone:Create(pJobValue.pickupDropoff, 30.0, 30.0, {
423 | name = "hiype-construction:load-box",
424 | heading = 0,
425 | debugPoly = Config.PolyzoneDebug,
426 | minZ = 0.0,
427 | maxZ = 1000.0,
428 | })
429 |
430 | if not IsPlayerJobLabelCorrect() then return end
431 |
432 | while not AllDeliverablesDelivered(pJobValue.deliverables) do
433 | Wait(2000)
434 | end
435 |
436 | loadBox:destroy()
437 | RemoveBlip(blip)
438 |
439 | local currentStage = 1
440 | local stagesCount = CountTableElements(pJobValue.workStages)
441 |
442 | for index, v in ipairs(pJobValue.workStages) do
443 | local stageComplete = false
444 | local counter = 0
445 |
446 | titleText = v.name
447 | subText = GenerateSubText(v)
448 |
449 | if not IsScreenFadedOut() then
450 | DoScreenFadeOut(2000)
451 |
452 | while not IsScreenFadedOut() do
453 | Wait(100)
454 | end
455 | end
456 |
457 | SetEntityCoords(PlayerPedId(), v.transitionLocation.x, v.transitionLocation.y, v.transitionLocation.z, false,
458 | false, false, false)
459 | SetEntityHeading(PlayerPedId(), v.transitionLocation.w)
460 |
461 | if v.ipl ~= nil then
462 | RequestIpl(v.ipl)
463 | end
464 |
465 | Wait(500)
466 | DoScreenFadeIn(2000)
467 |
468 | if v.locations ~= nil then
469 | for i, location in ipairs(v.locations) do
470 | LoadModel(location.model)
471 |
472 | local object = CreateObject(location.model, location.coord.x, location.coord.y, location.coord.z,
473 | false, true, false)
474 | exports['qb-target']:AddTargetEntity(object, {
475 | options = {
476 | {
477 | num = 1,
478 | icon = v.interactionIcon,
479 | label = v.interactionText,
480 | action = function(entity)
481 | if v.requirements ~= nil then
482 | if PlayerHasAllItems(v.requirements) then
483 | Anim(v.interactionTime, "anim@amb@clubhouse@tutorial@bkr_tut_ig3@",
484 | "machinic_loop_mechandplayer")
485 | QBCore.Functions.Progressbar('prepare_base_progress_bar' .. i,
486 | v.interactionSubText, v.interactionTime, false, true, {
487 | disableMovement = true,
488 | disableCarMovement = true,
489 | disableMouse = false,
490 | disableCombat = true
491 | }, {}, {}, {}, function()
492 | RemoveAllItems(v.requirements)
493 | DeleteEntity(entity)
494 | counter += 1
495 |
496 | if counter >= #v.locations then
497 | stageComplete = true
498 | if currentStage >= stagesCount then
499 | QBCore.Functions.Notify(Lang:t('info.job_done'), 'success',
500 | 5000)
501 | isBuildActive = false
502 | TriggerServerEvent('hiype-construction:server:add-money',
503 | pJobValue.reward, pJobValue.rewardType)
504 | DeleteDeliverables()
505 | end
506 | end
507 | end, function() end
508 | )
509 | else
510 | QBCore.Functions.Notify(Lang:t("error.not_all_items_in_inventory"), 'error',
511 | 5000)
512 | end
513 | else
514 | Anim(v.interactionTime, "anim@amb@clubhouse@tutorial@bkr_tut_ig3@",
515 | "machinic_loop_mechandplayer")
516 | QBCore.Functions.Progressbar('prepare_base_progress_bar' .. i,
517 | v.interactionSubText, v.interactionTime, false, true, {
518 | disableMovement = true,
519 | disableCarMovement = true,
520 | disableMouse = false,
521 | disableCombat = true
522 | }, {}, {}, {}, function()
523 | DeleteEntity(entity)
524 | counter += 1
525 |
526 | if counter >= #v.locations then
527 | stageComplete = true
528 | if currentStage >= stagesCount then
529 | QBCore.Functions.Notify(Lang:t('info.job_done'), 'success', 5000)
530 | isBuildActive = false
531 | TriggerServerEvent('hiype-construction:server:add-money',
532 | pJobValue.reward, pJobValue.rewardType)
533 | DeleteDeliverables()
534 | end
535 | end
536 | end, function() end
537 | )
538 | end
539 | end
540 | },
541 | },
542 | distance = 2.5,
543 | })
544 | end
545 | else
546 | local iBox = v.interactionBox
547 | local interactionCounter = 0
548 |
549 | exports['qb-target']:AddBoxZone("hiype-construction:target:stage-construction",
550 | vector3(iBox.location.x, iBox.location.y, iBox.location.z), iBox.length, iBox.width, {
551 | name = "hiype-construction:target:stage-construction",
552 | heading = iBox.heading,
553 | debugPoly = Config.PolyzoneDebug,
554 | minZ = iBox.minZ,
555 | maxZ = iBox.maxZ,
556 | }, {
557 | options = {
558 | {
559 | icon = v.interactionIcon,
560 | label = v.interactionText,
561 | action = function(entity)
562 | if not PlayerHasAllItems(v.interactionItem) then
563 | QBCore.Functions.Notify(Lang:t("error.not_all_items_in_inventory"), 'error',
564 | 5000)
565 | return
566 | end
567 |
568 | Anim(v.interactionTime, "anim@amb@clubhouse@tutorial@bkr_tut_ig3@",
569 | "machinic_loop_mechandplayer")
570 | QBCore.Functions.Progressbar('stage_progress_bar', v.interactionSubText,
571 | v.interactionTime, false, true, {
572 | disableMovement = true,
573 | disableCarMovement = true,
574 | disableMouse = false,
575 | disableCombat = true
576 | }, {}, {}, {}, function()
577 | interactionCounter = interactionCounter + 1
578 | if interactionCounter >= v.interactionCount then
579 | stageComplete = true
580 | exports['qb-target']:RemoveZone(
581 | "hiype-construction:target:stage-construction")
582 |
583 | if currentStage >= stagesCount then
584 | QBCore.Functions.Notify(Lang:t('info.job_done'), 'success', 5000)
585 | isBuildActive = false
586 | TriggerServerEvent('hiype-construction:server:add-money',
587 | pJobValue.reward,
588 | pJobValue.rewardType)
589 | DeleteDeliverables()
590 | end
591 | end
592 | RemoveItem(v.interactionItem)
593 | end, function() end
594 | )
595 | end
596 | }
597 | },
598 | distance = 2.5,
599 | }
600 | )
601 | end
602 |
603 | while not stageComplete do
604 | Wait(1000)
605 | end
606 |
607 | if v.unpackDeliverables ~= nil and v.unpackDeliverables then
608 | while not AllDeliverablesUnpacked(pJobValue.deliverables) do
609 | Wait(1000)
610 | end
611 | end
612 |
613 | if isBuildActive then
614 | DoScreenFadeOut(2000)
615 |
616 | while not IsScreenFadedOut() do
617 | Wait(100)
618 | end
619 |
620 | if v.ipl ~= nil then
621 | RemoveIpl(v.ipl)
622 | end
623 | end
624 |
625 | currentStage = currentStage + 1
626 | end
627 | end)
628 | end
629 |
630 | local function InitializeNPC()
631 | local location = Config.NPCLocation
632 | local model = Config.NPCModel
633 |
634 | LoadModel(model)
635 |
636 | npc = CreatePed(0, model, location.x, location.y, location.z, location.w, false, true)
637 | SetEntityInvincible(npc, true)
638 | SetBlockingOfNonTemporaryEvents(npc, true)
639 | TaskStartScenarioInPlace(npc, "WORLD_HUMAN_CLIPBOARD", 0, true)
640 | FreezeEntityPosition(npc, true)
641 |
642 | exports['qb-target']:AddTargetEntity(npc, {
643 | options = {
644 | {
645 | num = 1,
646 | icon = 'fa-solid fa-pen',
647 | label = Lang:t("info.signup"),
648 | action = function(entity)
649 | if IsPlayerJobLabelCorrect() then
650 | QBCore.Functions.Notify(Lang:t("error.already_active"), 'error', 4000)
651 | return
652 | end
653 |
654 | for k, _ in pairs(Config.Job) do
655 | AnimWithProp(Config.ContractSignTime, "missfam4", "base", 'p_amb_clipboard_01', 36029,
656 | { 0.16, 0.08, 0.1, -130.0, -50.0, 0.0 })
657 | QBCore.Functions.Progressbar('singup_job_progress_bar', Lang:t('info.signing_job_documents'),
658 | Config.ContractSignTime, false, true, {
659 | disableMovement = true,
660 | disableCarMovement = true,
661 | disableMouse = false,
662 | disableCombat = true
663 | }, {}, {}, {}, function()
664 | TriggerServerEvent('hiype-construction:server:set-job', k, 0)
665 | end, function() end
666 | )
667 | break
668 | end
669 | end
670 | },
671 | {
672 | num = 2,
673 | icon = 'fa-solid fa-pen',
674 | label = Lang:t("info.signout"),
675 | action = function(entity)
676 | if not IsPlayerJobLabelCorrect() then
677 | QBCore.Functions.Notify(Lang:t("error.not_active"), 'error', 4000)
678 | return
679 | end
680 |
681 | TriggerServerEvent('hiype-construction:server:set-job', 'unemployed', 0)
682 | end
683 | },
684 | {
685 | num = 3,
686 | icon = 'fa-solid fa-pen',
687 | label = Lang:t("info.request_build"),
688 | action = function(entity)
689 | if not IsPlayerJobLabelCorrect() then
690 | QBCore.Functions.Notify(Lang:t("error.not_active"), 'error', 4000)
691 | return
692 | end
693 |
694 | if isBuildActive then
695 | QBCore.Functions.Notify(Lang:t("error.you_have_an_active_build"), 'error', 4000)
696 | return
697 | end
698 |
699 | local constructionSites = {}
700 |
701 | constructionSites[#constructionSites + 1] = {
702 | isMenuHeader = true,
703 | header = Lang:t("info.construction_sites_header"),
704 | icon = nil
705 | }
706 |
707 | for k, v in pairs(Config.ConstructionSites) do
708 | constructionSites[#constructionSites + 1] = {
709 | header = v.locationName,
710 | txt = v.listingText,
711 | icon = v.listingIcon,
712 | params = {
713 | event = 'hiype-construction:client:construction-menu-return',
714 | args = {
715 | name = k,
716 | value = v
717 | }
718 | }
719 | }
720 | end
721 |
722 | exports['qb-menu']:openMenu(constructionSites)
723 | end
724 | },
725 | {
726 | num = 4,
727 | icon = 'fa-solid fa-xmark',
728 | label = Lang:t("info.cancel_build"),
729 | action = function(entity)
730 | if not isBuildActive then
731 | QBCore.Functions.Notify(Lang:t("error.no_active_build"), 'error', 4000)
732 | return
733 | end
734 |
735 | StopBuild()
736 | end,
737 | job = 'construction'
738 | },
739 | {
740 | num = 5,
741 | icon = 'fa-solid fa-truck',
742 | label = Lang:t("info.rent_truck"),
743 | action = function(entity)
744 | if IsVehicleDriveable(truck, false) and truck ~= nil then
745 | local vehicleLocation = GetEntityCoords(truck)
746 |
747 | QBCore.Functions.Notify(Lang:t("error.truck_already_rented_set_waypoint"), 'error', 5000)
748 | SetNewWaypoint(vehicleLocation.x, vehicleLocation.y)
749 |
750 | return
751 | end
752 |
753 | AnimWithProp(Config.DeliveryTruck.SignTime, "missfam4", "base", 'p_amb_clipboard_01', 36029,
754 | { 0.16, 0.08, 0.1, -130.0, -50.0, 0.0 })
755 | QBCore.Functions.Progressbar('rent_truck_progress_bar', 'Signing rent documents',
756 | Config.DeliveryTruck.SignTime, false, true, {
757 | disableMovement = true,
758 | disableCarMovement = true,
759 | disableMouse = false,
760 | disableCombat = true
761 | }, {}, {}, {}, function()
762 | QBCore.Functions.SpawnVehicle(Config.DeliveryTruck.Model, function(veh)
763 | truck = veh
764 | TriggerEvent("vehiclekeys:client:SetOwner", QBCore.Functions.GetPlate(truck))
765 | TriggerServerEvent("hiype-construction:server:add-money",
766 | Config.DeliveryTruck.RentPrice * -1,
767 | Config.DeliveryTruck.RentPriceType)
768 | end, Config.DeliveryTruck.SpawnLocation, true)
769 | end, function() end
770 | )
771 | end,
772 | job = 'construction'
773 | },
774 | {
775 | num = 6,
776 | icon = 'fa-solid fa-truck',
777 | label = Lang:t("info.return_truck"),
778 | action = function(entity)
779 | if #(GetEntityCoords(entity) - GetEntityCoords(truck)) > Config.DeliveryTruck.RentReturnDistance then
780 | QBCore.Functions.Notify(Lang:t("error.truck_not_close_enough"), 'error', 5000)
781 | return
782 | end
783 |
784 | QBCore.Functions.DeleteVehicle(truck)
785 | TriggerServerEvent("hiype-construction:server:add-money", Config.DeliveryTruck.RentReturn,
786 | Config.DeliveryTruck.RentReturnPriceType)
787 | end,
788 | job = 'construction'
789 | }
790 | },
791 | distance = 2.5,
792 | })
793 | end
794 |
795 | RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
796 | LocalPlayer.state:set('isLoggedIn', true, false)
797 | InitializeNPC()
798 | CreateThread(function()
799 | Wait(50)
800 | RemoveAllIPLs()
801 | end)
802 | AddWorkbench()
803 | end)
804 |
805 |
806 | RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()
807 | StopBuild()
808 | end)
809 |
810 | AddEventHandler('onResourceStart', function(resource)
811 | if resource ~= GetCurrentResourceName() then return end
812 |
813 | -- TODO: Remove after development
814 | InitializeNPC()
815 | CreateThread(function()
816 | Wait(50)
817 | RemoveAllIPLs()
818 | end)
819 | AddWorkbench()
820 | end)
821 |
822 | RegisterNetEvent('hiype-construction:client:craft-menu-return', function(data)
823 | if not PlayerHasAllItemsCraft(data.name) then
824 | QBCore.Functions.Notify(Lang:t("error.not_all_items_in_inventory"), 'error', 5000)
825 | return
826 | end
827 |
828 | Anim(Config.ContractSignTime, "anim@amb@clubhouse@tutorial@bkr_tut_ig3@", "machinic_loop_mechandplayer")
829 | QBCore.Functions.Progressbar('join_construction_progress_bar', Lang:t("info.crafting"), Config.ContractSignTime,
830 | false, true, {
831 | disableMovement = true,
832 | disableCarMovement = true,
833 | disableMouse = false,
834 | disableCombat = true
835 | }, {}, {}, {}, function()
836 | RemoveAllItems(Config.ItemRequirements[data.name])
837 | TriggerServerEvent('hiype-construction:server:addItem', data.name, 1)
838 | end, function() end
839 | )
840 | end)
841 |
842 | RegisterNetEvent('hiype-construction:client:construction-menu-return', function(data)
843 | AnimWithProp(Config.ContractSignTime, "missfam4", "base", 'p_amb_clipboard_01', 36029,
844 | { 0.16, 0.08, 0.1, -130.0, -50.0, 0.0 })
845 | QBCore.Functions.Progressbar('join_construction_progress_bar', Lang:t("info.signing_contract"),
846 | Config.ContractSignTime, false, true, {
847 | disableMovement = true,
848 | disableCarMovement = true,
849 | disableMouse = false,
850 | disableCombat = true
851 | }, {}, {}, {}, function()
852 | StartBuild(data.name, data.value)
853 | end, function() end
854 | )
855 | end)
856 |
857 | -- Every frame actions
858 | CreateThread(function()
859 | local vehicleAffected = Config.IsVehicleAffectedByCargo
860 | while true do
861 | while isBuildActive do
862 | Draw2DText(0.9, 0.2, titleText, 0.50, 225, 0, 0, 255) -- Title
863 | Draw2DText(0.9, 0.25, subText, 0.40, 255, 255, 255, 255) -- Sub text
864 |
865 | if vehicleAffected and isAttached then
866 | if GetVehiclePedIsIn(PlayerPedId(), false) == truck then
867 | SetVehicleCheatPowerIncrease(truck, currentSpeedModifier)
868 | end
869 | end
870 |
871 | Wait(1)
872 | end
873 |
874 | Wait(4000)
875 | end
876 | end)
877 |
--------------------------------------------------------------------------------