├── README.md
├── client.lua
├── function
└── utils.lua
├── fxmanifest.lua
├── positionTable.json
├── server.lua
└── shared
├── config.lua
└── language.lua
/README.md:
--------------------------------------------------------------------------------
1 | # PX WEAPON CRAFTING
2 | Simple weapon crafting system with XP system and job control
3 |

4 |
5 |
6 | # **Frameworks**
7 | - ESX
8 | # **Features**
9 | - EnableDebug: A boolean flag indicating whether debugging is enabled for the crafting system
10 | - XpSystem: A boolean flag indicating activate the experience system for crafting weapons
11 | - ExperiancePerCraft: The amount of experience gained per craft in the crafting system
12 | - Weapon: A set of definitions for different weapons in the game, each with details such as weapon code, name, job requirements, required experience, an allowed job list, and items required for crafting. For example, the sniper rifle requires the police job, 10 experience points, and 2 iron and 5 copper for crafting
13 | - PositionCrafting: A list of locations in the game where you can craft, defined by coordinates and direction
14 |
15 | **🧷 Discord**
16 | [Pixel Scripts](https://discord.gg/KeZSH27fGe)
17 |
18 | **💻 Preview**
19 | [Video](https://www.youtube.com/watch?v=qLC1rGdf22g)
20 |
21 | **📖 Documentation**
22 | [View](https://app.gitbook.com/o/maLkzzf71CV0QY3XJcJC/s/ryJXOX2xIE06Mha4iYtD/~/changes/18/)
23 |
--------------------------------------------------------------------------------
/client.lua:
--------------------------------------------------------------------------------
1 | RegisterNetEvent('esx:playerLoaded')
2 | AddEventHandler('esx:playerLoaded', function(xPlayer)
3 | ESX.PlayerData = xPlayer
4 | end)
5 |
6 | RegisterNetEvent('esx:setJob')
7 | AddEventHandler('esx:setJob', function(job)
8 | ESX.PlayerData.job = job
9 | end)
10 |
11 | AddEventHandler('esx:onPlayerSpawn', function()
12 | SpawnObject()
13 | end)
14 |
15 | local cam
16 | local inCam
17 | local objectPosition = {}
18 |
19 | exports.ox_target:addModel(Crafting.PropBench, {
20 | {
21 | name = 'open_crafting',
22 | event = '',
23 | icon = 'fa-solid fa-screwdriver-wrench',
24 | label = lang.target_label,
25 | onSelect = function(entity, distance, coords, name)
26 | local coords = GetEntityCoords(entity.entity)
27 | CamON(entity.entity, coords, GetEntityHeading(entity.entity))
28 | end
29 | }
30 | })
31 |
32 | local view
33 |
34 | RegisterNetEvent('px_crafting:showCrafting')
35 | AddEventHandler('px_crafting:showCrafting', function()
36 | ShowAllEntity()
37 | end)
38 |
39 | function ShowAllEntity()
40 | local data = lib.callback.await('px_crafting:getTablePosition', false)
41 | if type(data) == "table" then
42 | local options = {}
43 | debug(data)
44 | for _, v in pairs(data) do
45 | options[#options + 1] = {
46 | label = v.name, args = {coords = v.coords, name = v.name}, close = true
47 | }
48 | end
49 | lib.registerMenu({
50 | id = 'some_menu_id',
51 | title = 'All Bench',
52 | position = 'top-right',
53 | options = options,
54 | onClose = function()
55 | view = false
56 | end,
57 | }, function(selected, scrollIndex, args)
58 | local coords = args.coords
59 | local name = args.name
60 | InfoEntity(coords, name)
61 | end)
62 | lib.showMenu('some_menu_id')
63 | end
64 | end
65 |
66 | function InfoEntity(coords, name)
67 | lib.registerMenu({
68 | id = 'info_entity',
69 | title = 'Menu Actions',
70 | position = 'top-right',
71 | options = {
72 | {label = 'View', args = {coords = coords}, close = false},
73 | {label = 'Delete', args = {coords = coords, name = name}, close = true}
74 | },
75 | onClose = function()
76 | ShowAllEntity()
77 | view = false
78 | end,
79 | }, function(selected, scrollIndex, args)
80 | local coords = args.coords
81 | local name = args.name
82 | if selected == 1 then
83 | debug('View')
84 | view = false
85 | Wait(50)
86 | view = true
87 | CreateMarker(coords)
88 | elseif selected == 2 then
89 | view = false
90 | TriggerServerEvent('px_weapon_crafting:DeleteEntity', coords, name)
91 | for _, v in pairs(objectPosition) do
92 | debug(v)
93 | DeleteEntity(v)
94 | end
95 | Wait(100)
96 | SpawnObject()
97 | end
98 | end)
99 | lib.showMenu('info_entity')
100 | end
101 |
102 | function CreateMarker(coords)
103 | while view do
104 | Wait(0)
105 | DrawMarker(2, coords.x, coords.y, coords.z + 2, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 1.0, 1.0, 1.0, 255, 128, 0, 50, true, true, 2, nil, nil, false)
106 | end
107 | end
108 |
109 | AddEventHandler('onResourceStart', function(resourceName)
110 | if GetCurrentResourceName() ~= resourceName then
111 | return
112 | end
113 | SpawnObject()
114 | end)
115 |
116 | function SpawnObject()
117 | local data = lib.callback.await('px_crafting:getTablePosition', false)
118 | if data ~= nil then
119 | for _, v in ipairs(data) do
120 | local heading = 0.0 + v.heading
121 | local createCrafting = CreateObject(Crafting.PropBench, v.coords.x, v.coords.y, v.coords.z, false, true,
122 | false)
123 |
124 | if createCrafting then
125 | SetEntityHeading(createCrafting, heading)
126 | SetEntityCollision(createCrafting, true, true)
127 | PlaceObjectOnGroundProperly(createCrafting)
128 | table.insert(objectPosition, createCrafting)
129 | else
130 | debug('Error during the creation of the crafting object.')
131 | end
132 | end
133 | end
134 | end
135 |
136 | AddEventHandler("onResourceStop", function(re)
137 | if re == GetCurrentResourceName() then
138 | for _, v in pairs(objectPosition) do
139 | debug(v)
140 | DeleteEntity(v)
141 | end
142 | end
143 | end)
144 |
145 | function OpenCrafting(coords, heading)
146 | local options = {}
147 | local PlayerJob = GetJobPlayer()
148 | local value = false
149 |
150 | debug(PlayerJob)
151 |
152 | for k, v in pairs(Crafting.Weapon) do
153 | debug(v)
154 |
155 | if v.requiredJob then
156 | for _, data in pairs(v.allowlistJob) do
157 | debug('For Job ' .. data)
158 |
159 | if data == PlayerJob then
160 | debug('Check')
161 | Wait(50)
162 | local option = {
163 | label = v.itemName,
164 | args = { value = k, code = v.itemCode },
165 | close = true,
166 | icon = "fa-solid fa-gun",
167 | iconColor = '#0061A2'
168 | }
169 |
170 | table.insert(options, option)
171 | value = true
172 | end
173 | end
174 | else
175 | debug(v)
176 | Wait(50)
177 | local option = {
178 | label = v.itemName,
179 | args = { value = k, code = v.itemCode },
180 | close = true,
181 | icon = "fa-solid fa-gun",
182 | iconColor = '#5C7CFA'
183 | }
184 |
185 | table.insert(options, option)
186 | value = true
187 | end
188 | end
189 |
190 | Wait(50)
191 |
192 | if value then
193 | lib.registerMenu({
194 | id = 'ApriCrafting',
195 | title = lang.menu_title,
196 | position = 'top-left',
197 | options = options,
198 | onClose = function()
199 | CamOFF()
200 | end,
201 | }, function(selected, scrollIndex, args)
202 | local value = args.value
203 | local code = args.code
204 | local hash = GetHashKey(value)
205 |
206 | if selected then
207 | CreaArma(hash, coords, heading, code, value)
208 | end
209 | end)
210 | lib.showMenu('ApriCrafting')
211 | elseif not value then
212 | debug('You cannot craft weapons')
213 | lib.notify({
214 | title = lang.notify_cannot_craft,
215 | description = '',
216 | type = 'error',
217 | position = 'top-center'
218 | })
219 | Wait(500)
220 | CamOFF()
221 | end
222 | end
223 |
224 | local function GetIntFromBlob(b, s, o)
225 | local r = 0
226 | for i = 1, s, 1 do
227 | r = r | (string.byte(b, o + i) << (i - 1) * 8)
228 | end
229 | return r
230 | end
231 |
232 | function GetWeaponStats(weaponHash, none)
233 | local blob = '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
234 | local retval = Citizen.InvokeNative(0xD92C739EE34C9EBA, weaponHash, blob, Citizen.ReturnResultAnyway())
235 | local hudDamage = GetIntFromBlob(blob, 8, 0)
236 | local hudSpeed = GetIntFromBlob(blob, 8, 8)
237 | local hudCapacity = GetIntFromBlob(blob, 8, 16)
238 | local hudAccuracy = GetIntFromBlob(blob, 8, 24)
239 | local hudRange = GetIntFromBlob(blob, 8, 32)
240 | return retval, hudDamage, hudSpeed, hudCapacity, hudAccuracy, hudRange
241 | end
242 |
243 | function CreaArma(hash, coords, d, code, value)
244 | local modelHash = hash
245 | inCam = true
246 | DeleteObject(obj)
247 | local s = d - 180
248 | local heading = s
249 | obj = CreateObject(modelHash, coords.x, coords.y, coords.z + 1.1, true, false, true)
250 | SetEntityHeading(obj, heading)
251 | OpenMenuWeapon(code, value)
252 | Citizen.CreateThread(function()
253 | while inCam do
254 | Wait(0)
255 | DisableControlAction(0, 34, true) -- A
256 | DisableControlAction(0, 30, true) -- D
257 | InfoCrafting()
258 | if IsControlPressed(0, 9) then -- Right
259 | heading = heading + 5
260 | SetEntityHeading(obj, heading)
261 | elseif IsControlPressed(0, 63) then -- Left
262 | heading = heading - 5
263 | SetEntityHeading(obj, heading)
264 | end
265 | end
266 | end)
267 | end
268 |
269 | function OpenMenuWeapon(hash, value)
270 | local options = {}
271 | debug("Code " .. hash)
272 | for k, v in pairs(Crafting.Weapon) do
273 | -- debug(v)
274 | if v.itemCode == hash then
275 | if v.weapon then
276 | options = {
277 | { label = lang.menu2_options_1, close = true, icon = "fa-solid fa-hammer" },
278 | { label = lang.menu2_options_2, close = true, icon = "fa-solid fa-circle-info" }
279 | }
280 | else
281 | options = {
282 | { label = lang.menu2_options_1, close = true, icon = "fa-solid fa-hammer" }
283 | }
284 | end
285 | end
286 | end
287 | Wait(100)
288 | lib.registerMenu({
289 | id = 'OpenMenuInfo',
290 | title = lang.menu2_title,
291 | position = 'top-left',
292 | options = options,
293 | onClose = function()
294 | DeleteObject(obj)
295 | lib.showMenu('ApriCrafting')
296 | end,
297 | }, function(selected, scrollIndex, args)
298 | if selected == 1 then
299 | OpenMenuCraft(hash, value)
300 | elseif selected == 2 then
301 | OpenMenuInfo(hash, value)
302 | end
303 | end)
304 | lib.showMenu('OpenMenuInfo')
305 | end
306 |
307 | function OpenMenuCraft(hash, value)
308 | local options = {}
309 | local craftingInfo = {}
310 | for k, v in pairs(Crafting.Weapon) do
311 | if k == value then
312 | itemName = v.itemCode
313 | xp = v.requiredXp
314 | time = v.requiredTime
315 | if Crafting.XpSystem then
316 | options[#options + 1] = {
317 | label = lang.menu3_options_3 .. " " .. xp,
318 | close = false,
319 | icon = "fa-solid fa-arrow-up-wide-short"
320 | }
321 | for j, l in pairs(v.ItemRequired) do
322 | debug("Item Name: " .. l.itemName .. " Item Quantity: " .. l.quantity)
323 | craftingInfo[#craftingInfo + 1] = {
324 | itemName = l.itemName,
325 | quantity = l.quantity,
326 | }
327 | options[#options + 1] = {
328 | label = lang.menu3_options_1 .. l.label .. " x " .. l.quantity,
329 | close = false,
330 | icon = "fa-solid fa-clipboard"
331 | }
332 | end
333 | else
334 | for j, l in pairs(v.ItemRequired) do
335 | debug("Item Name: " .. l.itemName .. " Item Quantity: " .. l.quantity)
336 | craftingInfo[#craftingInfo + 1] = {
337 | itemName = l.itemName,
338 | quantity = l.quantity,
339 | }
340 | options[#options + 1] = {
341 | label = lang.menu3_options_1 .. l.label .. " x " .. l.quantity,
342 | close = false,
343 | icon = "fa-solid fa-clipboard"
344 | }
345 | end
346 | end
347 | end
348 | end
349 | options[#options + 1] = {
350 | label = lang.menu3_options_2,
351 | close = true,
352 | args = { craftingInfo = craftingInfo },
353 | icon = "fa-solid fa-hammer"
354 | }
355 | Wait(50)
356 | lib.registerMenu({
357 | id = 'OpenMenuRealCraft',
358 | title = lang.menu3_title,
359 | position = 'top-left',
360 | options = options,
361 | onClose = function()
362 | OpenMenuWeapon(hash, value)
363 | end,
364 | }, function(selected, scrollIndex, args)
365 | local totalOptions = #options
366 | if selected == totalOptions then
367 | local data = lib.callback.await('px_crafting:getItemCount', false, args.craftingInfo)
368 | local item = args.craftingInfo
369 | debug('Start check item and xp...')
370 | debug(data)
371 | if data.value then
372 | if Crafting.XpSystem then
373 | if data.xp >= xp then
374 | debug('Start Crafting')
375 | if lib.progressBar({
376 | duration = time,
377 | label = 'Creating',
378 | useWhileDead = false,
379 | canCancel = true,
380 | disable = {
381 | car = true,
382 | },
383 | anim = {
384 | dict = 'mini@repair',
385 | clip = 'fixing_a_ped'
386 | },
387 | }) then
388 | CamOFF()
389 | TriggerServerEvent('px_crafting:removeItem', item, itemName)
390 | end
391 | else
392 | debug('You don\'t have enough experience points')
393 | lib.notify({
394 | title = lang.notify_enough_xp,
395 | description = '',
396 | type = 'error',
397 | position = 'top-center'
398 | })
399 | CamOFF()
400 | end
401 | else
402 | debug('Start Crafting')
403 | if lib.progressBar({
404 | duration = time,
405 | label = 'Creating',
406 | useWhileDead = false,
407 | canCancel = true,
408 | disable = {
409 | car = true,
410 | },
411 | anim = {
412 | dict = 'mini@repair',
413 | clip = 'fixing_a_ped'
414 | },
415 | }) then
416 | CamOFF()
417 | TriggerServerEvent('px_crafting:removeItem', item, itemName)
418 | end
419 | end
420 | else
421 | CamOFF()
422 | end
423 | end
424 | end)
425 | lib.showMenu('OpenMenuRealCraft')
426 | end
427 |
428 | function OpenMenuInfo(hash, value)
429 | local job = GetJobPlayer()
430 | debug("Player Job " .. job)
431 | local options = {}
432 | local _, hudDamage, hudSpeed, hudCapacity, hudAccuracy, hudRange = GetWeaponStats(GetHashKey(hash))
433 | debug(_, hudDamage, hudSpeed, hudCapacity, hudAccuracy, hudRange)
434 | options = {
435 | { label = lang.menu4_options_1 .. ' (' .. hudDamage .. '%)', progress = hudDamage, colorScheme = '#0061A2', close = false },
436 | { label = lang.menu4_options_2 .. ' (' .. hudSpeed .. '%)', progress = hudSpeed, colorScheme = '#0061A2', close = false },
437 | { label = lang.menu4_options_3 .. ' (' .. hudCapacity .. '%)', progress = hudCapacity, colorScheme = '#0061A2', close = false },
438 | { label = lang.menu4_options_4 .. ' (' .. hudAccuracy .. '%)', progress = hudAccuracy, colorScheme = '#0061A2', close = false },
439 | { label = lang.menu4_options_5 .. ' (' .. hudRange .. '%)', progress = hudRange, colorScheme = '#0061A2', close = false }
440 | }
441 | Wait(100)
442 | lib.registerMenu({
443 | id = 'OpenMenuInfo',
444 | title = lang.menu4_title,
445 | position = 'top-left',
446 | options = options,
447 | onClose = function()
448 | OpenMenuWeapon(hash, value)
449 | end,
450 | }, function(selected, scrollIndex, args)
451 | end)
452 | lib.showMenu('OpenMenuInfo')
453 | end
454 |
455 | function CamON(obj, coordsArma, heading)
456 | local coords = GetOffsetFromEntityInWorldCoords(obj, 0, -0.75, 0)
457 | RenderScriptCams(false, false, 0, 1, 0)
458 | DestroyCam(cam, false)
459 | FreezeEntityPosition(cache.ped, true)
460 |
461 | if not DoesCamExist(cam) then
462 | cam = CreateCam("DEFAULT_SCRIPTED_CAMERA", true)
463 | SetCamActive(cam, true)
464 | RenderScriptCams(true, true, 250, 1, 0)
465 | SetCamCoord(cam, coords.x, coords.y, coords.z + 1.2)
466 | SetCamRot(cam, 0.0, 0.0, GetEntityHeading(obj))
467 | OpenCrafting(coordsArma, heading)
468 | else
469 | CamOFF()
470 | Wait(500)
471 | CamON()
472 | end
473 | end
474 |
475 | function InfoCrafting()
476 | Scale = RequestScaleformMovie("INSTRUCTIONAL_BUTTONS");
477 | while not HasScaleformMovieLoaded(Scale) do
478 | Citizen.Wait(0)
479 | end
480 |
481 | BeginScaleformMovieMethod(Scale, "CLEAR_ALL");
482 | EndScaleformMovieMethod();
483 |
484 | --Destra
485 | BeginScaleformMovieMethod(Scale, "SET_DATA_SLOT");
486 | ScaleformMovieMethodAddParamInt(0);
487 | PushScaleformMovieMethodParameterString("~INPUT_MOVE_RIGHT_ONLY~");
488 | PushScaleformMovieMethodParameterString("Rotate right");
489 | EndScaleformMovieMethod();
490 |
491 | --Sinistra
492 | BeginScaleformMovieMethod(Scale, "SET_DATA_SLOT");
493 | ScaleformMovieMethodAddParamInt(1);
494 | PushScaleformMovieMethodParameterString("~INPUT_MOVE_LEFT_ONLY~");
495 | PushScaleformMovieMethodParameterString("Rotate Left");
496 | EndScaleformMovieMethod();
497 |
498 | BeginScaleformMovieMethod(Scale, "SET_DATA_SLOT");
499 | ScaleformMovieMethodAddParamInt(2);
500 | PushScaleformMovieMethodParameterString("~INPUT_CELLPHONE_CANCEL~");
501 | PushScaleformMovieMethodParameterString("Exit");
502 | EndScaleformMovieMethod();
503 |
504 |
505 | BeginScaleformMovieMethod(Scale, "DRAW_INSTRUCTIONAL_BUTTONS");
506 | ScaleformMovieMethodAddParamInt(0);
507 | EndScaleformMovieMethod();
508 |
509 | DrawScaleformMovieFullscreen(Scale, 255, 255, 255, 255, 0);
510 | end
511 |
512 | function InfoPlaceCrafting()
513 | Scale = RequestScaleformMovie("INSTRUCTIONAL_BUTTONS");
514 | while not HasScaleformMovieLoaded(Scale) do
515 | Citizen.Wait(0)
516 | end
517 |
518 | BeginScaleformMovieMethod(Scale, "CLEAR_ALL");
519 | EndScaleformMovieMethod();
520 |
521 | --Destra
522 | BeginScaleformMovieMethod(Scale, "SET_DATA_SLOT");
523 | ScaleformMovieMethodAddParamInt(0);
524 | PushScaleformMovieMethodParameterString("~INPUT_PICKUP~");
525 | PushScaleformMovieMethodParameterString("Place Prop");
526 | EndScaleformMovieMethod();
527 |
528 | --Rotate Left
529 | BeginScaleformMovieMethod(Scale, "SET_DATA_SLOT");
530 | ScaleformMovieMethodAddParamInt(1);
531 | PushScaleformMovieMethodParameterString("~INPUT_WEAPON_WHEEL_PREV~");
532 | PushScaleformMovieMethodParameterString("Rotate Right");
533 | EndScaleformMovieMethod();
534 |
535 | --Rotate Right
536 | BeginScaleformMovieMethod(Scale, "SET_DATA_SLOT");
537 | ScaleformMovieMethodAddParamInt(2);
538 | PushScaleformMovieMethodParameterString("~INPUT_WEAPON_WHEEL_NEXT~");
539 | PushScaleformMovieMethodParameterString("Rotate Left");
540 | EndScaleformMovieMethod();
541 |
542 |
543 | BeginScaleformMovieMethod(Scale, "DRAW_INSTRUCTIONAL_BUTTONS");
544 | ScaleformMovieMethodAddParamInt(0);
545 | EndScaleformMovieMethod();
546 |
547 | DrawScaleformMovieFullscreen(Scale, 255, 255, 255, 255, 0);
548 | end
549 |
550 | function CamOFF()
551 | -- lib.hideMenu('ApriCrafting')
552 | FreezeEntityPosition(PlayerPedId(), false)
553 | DeleteObject(obj)
554 | RenderScriptCams(false, true, 250, 1, 0)
555 | DestroyCam(cam, false)
556 | -- SetLocalPlayerAsGhost(false)
557 | inCam = false
558 | end
559 |
560 | local confirmed
561 | local heading
562 |
563 | function RotationToDirection(rotation)
564 | local adjustedRotation =
565 | {
566 | x = (math.pi / 180) * rotation.x,
567 | y = (math.pi / 180) * rotation.y,
568 | z = (math.pi / 180) * rotation.z
569 | }
570 | local direction =
571 | {
572 | x = -math.sin(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)),
573 | y = math.cos(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)),
574 | z = math.sin(adjustedRotation.x)
575 | }
576 | return direction
577 | end
578 |
579 | function DrawPropAxes(prop)
580 | local propForward, propRight, propUp, propCoords = GetEntityMatrix(prop)
581 |
582 | local propXAxisEnd = propCoords + propRight * 1.0
583 | local propYAxisEnd = propCoords + propForward * 1.0
584 | local propZAxisEnd = propCoords + propUp * 1.0
585 |
586 | DrawLine(propCoords.x, propCoords.y, propCoords.z + 0.1, propXAxisEnd.x, propXAxisEnd.y, propXAxisEnd.z, 255, 0, 0,
587 | 255)
588 | DrawLine(propCoords.x, propCoords.y, propCoords.z + 0.1, propYAxisEnd.x, propYAxisEnd.y, propYAxisEnd.z, 0, 255, 0,
589 | 255)
590 | DrawLine(propCoords.x, propCoords.y, propCoords.z + 0.1, propZAxisEnd.x, propZAxisEnd.y, propZAxisEnd.z, 0, 0, 255,
591 | 255)
592 | end
593 |
594 | function RayCastGamePlayCamera(distance)
595 | local cameraRotation = GetGameplayCamRot()
596 | local cameraCoord = GetGameplayCamCoord()
597 | local direction = RotationToDirection(cameraRotation)
598 | local destination =
599 | {
600 | x = cameraCoord.x + direction.x * distance,
601 | y = cameraCoord.y + direction.y * distance,
602 | z = cameraCoord.z + direction.z * distance
603 | }
604 | local a, b, c, d, e = GetShapeTestResult(StartShapeTestRay(cameraCoord.x, cameraCoord.y, cameraCoord.z, destination
605 | .x, destination.y, destination.z, -1, PlayerPedId(), 0))
606 | return b, c, e
607 | end
608 |
609 | RegisterNetEvent('px_crafting:placeProp')
610 | AddEventHandler('px_crafting:placeProp', function(prop)
611 | debug('Hola')
612 | prop = joaat(prop)
613 | heading = 0.0
614 | confirmed = false
615 |
616 | RequestModel(prop)
617 | while not HasModelLoaded(prop) do
618 | Wait(0)
619 | end
620 |
621 | local hit, coords
622 |
623 | while not hit do
624 | hit, coords = RayCastGamePlayCamera(10.0)
625 | Wait(0)
626 | end
627 |
628 | local propObject = CreateObject(prop, coords.x, coords.y, coords.z, true, false, true)
629 |
630 | CreateThread(function()
631 | while not confirmed do
632 | InfoPlaceCrafting()
633 | hit, coords, entity = RayCastGamePlayCamera(10.0)
634 | DisableControlAction(0, 24, true)
635 | DisableControlAction(0, 25, true)
636 | SetEntityCoordsNoOffset(propObject, coords.x, coords.y, coords.z, false, false, false, true)
637 | FreezeEntityPosition(propObject, true)
638 | SetEntityCollision(propObject, false, false)
639 | SetEntityAlpha(propObject, 100, false)
640 | debug(heading)
641 | DrawPropAxes(propObject)
642 | Wait(0)
643 |
644 | if IsControlPressed(0, 15) then -- Left
645 | heading = heading + 5.0
646 | elseif IsControlPressed(0, 14) then -- Right
647 | heading = heading - 5.0
648 | end
649 |
650 | if IsControlJustPressed(0, 177) then
651 | DeleteObject(propObject)
652 | confirmed = true
653 | end
654 |
655 | if heading > 360.0 then
656 | heading = 0.0
657 | elseif heading < 0.0 then
658 | heading = 360.0
659 | end
660 |
661 | SetEntityHeading(propObject, heading)
662 |
663 | if IsControlJustPressed(0, 38) then -- "E"
664 | local input = lib.inputDialog('Table Name', { '' })
665 |
666 | if not input then
667 | DeleteObject(propObject)
668 | return
669 | else
670 | confirmed = true
671 | SetEntityAlpha(propObject, 255, false)
672 | SetEntityCollision(propObject, true, true)
673 | TriggerServerEvent('px_crafting:SaveTable', input[1], coords.x, coords.y, coords.z, heading)
674 | table.insert(objectPosition, propObject)
675 | end
676 | end
677 | end
678 | end)
679 | end)
680 |
--------------------------------------------------------------------------------
/function/utils.lua:
--------------------------------------------------------------------------------
1 | function debug(...)
2 | if Crafting.EnableDebug then
3 | local args = { ... }
4 |
5 | for i = 1, #args do
6 | local arg = args[i]
7 | args[i] = type(arg) == 'table' and json.encode(arg, { sort_keys = true, indent = true }) or tostring(arg)
8 | end
9 |
10 | print('^1[DEBUG] ^7', table.concat(args, '\t'))
11 | end
12 | end
13 |
14 | function GetJobPlayer()
15 | return ESX.PlayerData.job.name
16 | end
17 |
18 | function GetPlayerXp()
19 | local src = source
20 | local xPlayer = ESX.GetPlayerFromId(src)
21 | local player_xp = MySQL.scalar.await('SELECT `crafting_level` FROM `users` WHERE `identifier` = ?', {
22 | xPlayer.identifier
23 | })
24 | return player_xp
25 | end
--------------------------------------------------------------------------------
/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | fx_version 'adamant'
2 | game 'gta5'
3 | lua54 'yes'
4 |
5 | author 'Haze'
6 | version '1.0.0'
7 |
8 | shared_scripts{
9 | '@es_extended/imports.lua',
10 | '@ox_lib/init.lua',
11 | 'shared/*.lua',
12 | 'function/utils.lua'
13 | }
14 |
15 | client_scripts{
16 | 'client.lua'
17 | }
18 |
19 | server_scripts{
20 | '@oxmysql/lib/MySQL.lua',
21 | 'server.lua'
22 | }
--------------------------------------------------------------------------------
/positionTable.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pixel-Scripts/px_weapon-crafting/262101c2ee52661fad43783c98d557d26524eaea/positionTable.json
--------------------------------------------------------------------------------
/server.lua:
--------------------------------------------------------------------------------
1 | lib.callback.register('px_crafting:getItemCount', function(source, item)
2 | local src = source
3 | local xPlayer = ESX.GetPlayerFromId(src)
4 | local hasEnoughItems = true
5 | for k, v in pairs(item) do
6 | local c = exports.ox_inventory:GetItem(src, v.itemName, nil, false)
7 | debug(c.label .. " " .. c.count)
8 |
9 | if c.count < v.quantity then
10 | hasEnoughItems = false
11 | TriggerClientEvent('ox_lib:notify', source, {
12 | type = 'error',
13 | title = 'You do not have: ' .. c.label.." Required "..v.quantity,
14 | position = 'top',
15 | description = '',
16 | 5000
17 | })
18 | end
19 | end
20 |
21 | if hasEnoughItems then
22 | local info = {}
23 | info.value = true
24 | info.xp = GetPlayerXp()
25 | debug(info)
26 | return info
27 | else
28 | local info = {}
29 | info.value = false
30 | return info
31 | end
32 | end)
33 |
34 | RegisterNetEvent('px_crafting:removeItem')
35 | AddEventHandler('px_crafting:removeItem', function(item, weapon)
36 | for k,v in pairs(item) do
37 | exports.ox_inventory:RemoveItem(source, v.itemName, v.quantity, nil, nil)
38 | end
39 | if Crafting.XpSystem then
40 | exports.ox_inventory:AddItem(source, weapon, 1, nil, nil)
41 | exports["px_weapon-crafting"]:addPlayerXp(source, Crafting.ExperiancePerCraft)
42 | else
43 | exports.ox_inventory:AddItem(source, weapon, 1, nil, nil)
44 | end
45 | end)
46 |
47 | RegisterNetEvent('px_crafting:SaveTable')
48 | AddEventHandler('px_crafting:SaveTable', function(name, coordsx, coordsy, coordsz, heading)
49 | local loadFile= LoadResourceFile(GetCurrentResourceName(), "./positionTable.json")
50 | if loadFile ~= nil then
51 | local extract = json.decode(loadFile)
52 | if type(extract) == "table" then
53 | debug(extract)
54 | table.insert(extract, {name = name, coords = vector3(coordsx, coordsy, coordsz), heading = heading})
55 | SaveResourceFile(GetCurrentResourceName(), "positionTable.json", json.encode(extract, { indent = true }), -1)
56 | else
57 | local Table = {}
58 | table.insert(Table, {name = name, coords = vector3(coordsx, coordsy, coordsz), heading = heading})
59 | SaveResourceFile(GetCurrentResourceName(), "positionTable.json", json.encode(Table, { indent = true }), -1)
60 | end
61 | end
62 | end)
63 |
64 | RegisterNetEvent('px_weapon_crafting:DeleteEntity')
65 | AddEventHandler('px_weapon_crafting:DeleteEntity', function(coords, name)
66 | local loadFile = LoadResourceFile(GetCurrentResourceName(), "./positionTable.json ")
67 | if loadFile ~= nil then
68 | local extract = json.decode(loadFile)
69 | if type(extract) == "table" then
70 | for k,v in ipairs(extract) do
71 | if v.name == name then
72 | debug(v.coords)
73 | debug(k)
74 | table.remove(extract, k)
75 | SaveResourceFile(GetCurrentResourceName(), "positionTable.json", json.encode(extract, { indent = true }), -1)
76 | end
77 | end
78 | end
79 | end
80 | end)
81 |
82 | lib.callback.register('px_crafting:getTablePosition', function(source)
83 | local loadFile= LoadResourceFile(GetCurrentResourceName(), "./positionTable.json")
84 | if loadFile ~= nil then
85 | local extract = json.decode(loadFile)
86 | return extract
87 | end
88 | end)
89 |
90 | RegisterCommand(Crafting.CommandGive, function(source, args, rawCommand)
91 | local xTarget = ESX.GetPlayerFromId(tonumber(args[1]))
92 | if args[1] ~= nil then
93 | if args[2] ~= nil then
94 | if xTarget ~= nil then
95 | exports["px_weapon-crafting"]:addPlayerXp(xTarget.source, args[2])
96 | else
97 | debug('User not found')
98 | end
99 | end
100 | end
101 | end, false)
102 |
103 | RegisterCommand(Crafting.Command, function(source, args, rawCommand)
104 | local xPlayer = ESX.GetPlayerFromId(source)
105 | for k, v in pairs(Crafting.PermissionCommand) do
106 | if v == xPlayer.getGroup() then
107 | debug(v)
108 | TriggerClientEvent('px_crafting:placeProp', source, Crafting.PropBench)
109 | return
110 | end
111 | end
112 | end)
113 |
114 | RegisterCommand(Crafting.CommandShow, function(source, args, rawCommand)
115 | local xPlayer = ESX.GetPlayerFromId(source)
116 | for k, v in pairs(Crafting.PermissionCommand) do
117 | if v == xPlayer.getGroup() then
118 | debug(v)
119 | TriggerClientEvent('px_crafting:showCrafting', source)
120 | return
121 | end
122 | end
123 | end)
124 |
125 | exports("addPlayerXp", function(source, xp)
126 | local xPlayer = ESX.GetPlayerFromId(source)
127 | local player_xp = MySQL.scalar.await('SELECT `crafting_level` FROM `users` WHERE `identifier` = ?', {
128 | xPlayer.identifier
129 | })
130 | local givexp = player_xp + tonumber(xp)
131 | local affectedRows = MySQL.update.await('UPDATE users SET `crafting_level` = ? WHERE identifier = ?', {
132 | givexp, xPlayer.identifier
133 | })
134 |
135 | TriggerClientEvent('ox_lib:notify', source, {
136 | type = 'success',
137 | title = lang.notify_earned_xp.." "..Crafting.ExperiancePerCraft.."xp",
138 | position = 'top',
139 | description = '',
140 | 5000
141 | })
142 | end)
143 |
--------------------------------------------------------------------------------
/shared/config.lua:
--------------------------------------------------------------------------------
1 | Crafting = {}
2 |
3 | Crafting = {
4 | Command = 'createCrafting',
5 | CommandShow = 'showCrafting',
6 | CommandGive = 'givecraftingxp',
7 | PermissionCommand = {'admin'},
8 | PropBench = 'gr_prop_gr_bench_02b',
9 | EnableDebug = true,
10 | XpSystem = true,
11 | ExperiancePerCraft = 2.5,
12 | Weapon = {
13 | ["prop_ld_ammo_pack_01"] = {
14 | itemCode = 'ammo-9',
15 | itemName = 'Ammo 9mm',
16 | requiredJob = false,
17 | requiredXp = 10,
18 | requiredTime = 1000,
19 | weapon = false,
20 | allowlistJob = {
21 | "police"
22 | },
23 | ItemRequired = {
24 | {label = 'Phone', itemName = "phone", quantity = 2},
25 | {label = 'Burger', itemName = "burger", quantity = 5}
26 | }
27 | },
28 | ["w_pi_pistol"] = {
29 | itemCode = 'WEAPON_PISTOL',
30 | itemName = 'Pistol',
31 | requiredJob = false,
32 | requiredXp = 0,
33 | requiredTime = 2000,
34 | weapon = true,
35 | allowlistJob = {
36 | "police"
37 | },
38 | ItemRequired = {
39 | {label = 'Radio', itemName = "radio", quantity = 1}
40 | }
41 | },
42 | ["w_ar_carbinerifle"] = {
43 | itemCode = 'WEAPON_CARBINERIFLE',
44 | itemName = 'Carabine Rifle',
45 | requiredJob = false,
46 | requiredXp = 1,
47 | requiredTime = 10000,
48 | weapon = true,
49 | allowlistJob = {
50 | "police",
51 | },
52 | ItemRequired = {
53 | {label = 'Water', itemName = "water", quantity = 1}
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/shared/language.lua:
--------------------------------------------------------------------------------
1 | lang = {
2 | target_label = 'Open Creation',
3 | menu_title = 'Workbench',
4 | menu2_title = 'Weapon Information Menu',
5 | menu2_options_1 = 'Create Weapon',
6 | menu2_options_2 = 'View Weapon Information',
7 |
8 | menu3_title = 'Weapon Information Menu',
9 | menu3_options_1 = 'Name: ',
10 | menu3_options_2 = 'Build',
11 | menu3_options_3 = 'Required XP: ',
12 |
13 | menu4_title = 'Weapon Information Menu',
14 | menu4_options_1 = 'Damage',
15 | menu4_options_2 = 'Speed',
16 | menu4_options_3 = 'Capacity',
17 | menu4_options_4 = 'Accuracy',
18 | menu4_options_5 = 'Range',
19 |
20 | --Notifications
21 | notify_enough_xp = 'You do not have enough experience',
22 | notify_copy_coords = 'You have copied the coordinates',
23 | notify_earned_xp = 'You have earned ',
24 | notify_cannot_craft = 'You cannot craft weapons'
25 | }
26 |
--------------------------------------------------------------------------------