├── 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 | [![Preview video](https://i.imgur.com/CnaMRhT.png)](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 | --------------------------------------------------------------------------------