308 lines
		
	
	
		
			No EOL
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			308 lines
		
	
	
		
			No EOL
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| local shopZones, shopPeds, shopPoints = {}, {}, {}
 | |
| local stashZones, stashPeds, stashPoints = {}, {}, {}
 | |
| 
 | |
| local function drawMarkerOnFrame(coords, marker)
 | |
|   ---@diagnostic disable-next-line: missing-parameter
 | |
|   DrawMarker(marker.id, coords.x, coords.y, coords.z, 0, 0, 0, 0, 0, 0, marker.size.x,  marker.size.y, marker.size.z, marker.color.r, marker.color.g, marker.color.b, marker.color.a, marker.bobUpAndDown, marker.faceCamera, 0, marker.rotate, marker.drawOnEnts)
 | |
| end
 | |
| 
 | |
| local function drawCustomMarker(center, marker, shopDistance, mechanicId, shopName, stashName)
 | |
|   if not marker then
 | |
|     if shopName and not stashName then
 | |
|       debugPrint("Marker is not setup for [SHOPS] ".. shopName .. " for location: " .. mechanicId, "warning")
 | |
|     elseif stashName then
 | |
|       debugPrint("Marker is not setup for [STASHES] " .. stashName .. " for location: " .. mechanicId, "warning")
 | |
|     end
 | |
| 
 | |
|     return
 | |
|   end
 | |
|   
 | |
|   local point = lib.points.new({
 | |
|     coords = center,
 | |
|     distance = shopDistance or 20,
 | |
|   })
 | |
| 
 | |
|   function point:nearby()
 | |
|     drawMarkerOnFrame(center, marker)
 | |
|   end
 | |
| 
 | |
|   return point
 | |
| end
 | |
| 
 | |
| local function openShop(mechanicId, shopIndex)
 | |
|   local mechanicConfig = Config.MechanicLocations[mechanicId]
 | |
| 
 | |
|   if not mechanicConfig or not mechanicConfig.shops or not mechanicConfig.shops[shopIndex] then
 | |
|     Framework.Client.Notify("Shop not found: " .. mechanicId .. " .. index: " .. shopIndex, "error")
 | |
|     return
 | |
|   end
 | |
| 
 | |
|   local shop = mechanicConfig.shops[shopIndex]
 | |
|   local shopItems = shop.items or {}
 | |
| 
 | |
|   local options = {}
 | |
|   for index, v in pairs(shopItems) do
 | |
|     table.insert(options, {
 | |
|       title = v.label,
 | |
|       icon = "box",
 | |
|       description = ("Buy for %d %s"):format(v.price, Config.Currency),
 | |
|       event = "jg-mechanic:client:input-shop-purchase-qty",
 | |
|       args = { shopIndex = shopIndex, itemIndex = index, item = v.name, price = v.price, mechanicId = mechanicId }
 | |
|     })
 | |
|   end
 | |
| 
 | |
|   local context = {
 | |
|     id = "shop_" .. mechanicId .. "_" .. shopIndex,
 | |
|     title = shop.name .. " Shop",
 | |
|     options = options
 | |
|   }
 | |
| 
 | |
|   lib.registerContext(context)
 | |
|   lib.showContext("shop_" .. mechanicId .. "_" .. shopIndex)
 | |
| end
 | |
| 
 | |
| RegisterNetEvent("jg-mechanic:client:input-shop-purchase-qty", function(args)
 | |
|   local input = lib.inputDialog("Enter the amount you want to buy", { "Amount" })
 | |
| 
 | |
|   if not input then 
 | |
|     Framework.Client.Notify("Invalid amount", "error")
 | |
|     return 
 | |
|   end
 | |
| 
 | |
|   local qty = tonumber(input[1])
 | |
|   if qty == nil or qty <= 0 then
 | |
|     Framework.Client.Notify("Invalid amount entered", "error")
 | |
|     return
 | |
|   end
 | |
| 
 | |
|   TriggerServerEvent("jg-mechanic:server:buy-item", args.shopIndex, args.itemIndex, qty, args.mechanicId)
 | |
| end)
 | |
| 
 | |
| function createMechanicShops()
 | |
|   for _, zone in pairs(shopZones) do
 | |
|     if zone and zone.remove then zone:remove() end
 | |
|   end
 | |
| 
 | |
|   for _, ped in pairs(shopPeds) do
 | |
|     if ped then DeleteEntity(ped) end
 | |
|   end
 | |
| 
 | |
|   for _, point in pairs(shopPoints) do
 | |
|     if point and point.remove then point:remove() end
 | |
|   end
 | |
| 
 | |
|   shopZones, shopPeds, shopPoints = {}, {}, {}
 | |
| 
 | |
|   for mechanicId, mechanicConfig in pairs(Config.MechanicLocations) do
 | |
|     local isMechanicEmployee = lib.callback.await("jg-mechanic:server:is-mechanic-employee", false, mechanicId)
 | |
|     if not isMechanicEmployee then goto continue end
 | |
| 
 | |
|     if not mechanicConfig.shops then
 | |
|       debugPrint(("No shops defined for '%s' in Config.MechanicLocations"):format(mechanicId), "warning")
 | |
|       goto continue
 | |
|     end
 | |
| 
 | |
|     for shopIndex, shop in ipairs(mechanicConfig.shops) do
 | |
|       if shop.type == "self-service" then
 | |
|         debugPrint(("Skipping self-service shop for '%s' (index: %d)"):format(mechanicId, shopIndex), "debug")
 | |
|         goto shop_continue
 | |
|       end
 | |
| 
 | |
|       if not shop.coords or not shop.size then
 | |
|         debugPrint(("Invalid shop data for '%s' (index: %d) - missing coords or size"):format(mechanicId, shopIndex), "warning")
 | |
|       end
 | |
| 
 | |
|       if shop.usePed then
 | |
|         local ped = createPedForTarget(shop.pedModel or "s_m_m_lathandy_01", shop.coords.xyz)
 | |
|         shopPeds[#shopPeds+1] = ped
 | |
| 
 | |
|         -- If vec4, set heading
 | |
|         if shop.coords.w then
 | |
|           SetEntityHeading(ped, shop.coords.w or 0)
 | |
|         end
 | |
| 
 | |
|         if Config.Target == "qb-target" then
 | |
|           exports["qb-target"]:AddTargetEntity(ped, {
 | |
|             options = {
 | |
|               {
 | |
|                 label = "Access Shop",
 | |
|                 icon = "fas fa-store",
 | |
|                 action = function()
 | |
|                   openShop(mechanicId, shopIndex)
 | |
|                 end,
 | |
|               },
 | |
|             },
 | |
|             distance = 2.5,
 | |
|           })
 | |
|         elseif Config.Target == "ox_target" then
 | |
|           exports.ox_target:addLocalEntity(ped, {
 | |
|             {
 | |
|               name = "access_shop_" .. mechanicId .. "_" .. shopIndex,
 | |
|               label = "Access Shop",
 | |
|               icon = "fas fa-store",
 | |
|               onSelect = function()
 | |
|                 openShop(mechanicId, shopIndex)
 | |
|               end,
 | |
|             },
 | |
|           })
 | |
|         end
 | |
|       else
 | |
|         local zoneData = {
 | |
|           coords = shop.coords,
 | |
|           size = vector3(shop.size, shop.size, 2.0),
 | |
|           rotation = shop.coords.w or 0,
 | |
|           debug = Config.Debug,
 | |
|           onEnter = function()
 | |
|             Framework.Client.ShowTextUI("Press [E] to access " .. shop.name)
 | |
|           end,
 | |
|           onExit = function()
 | |
|             Framework.Client.HideTextUI()
 | |
|             lib.hideContext()
 | |
|           end,
 | |
|           inside = function()
 | |
|             if IsControlJustReleased(0, 38) then
 | |
|               openShop(mechanicId, shopIndex)
 | |
|             end
 | |
|           end
 | |
|         }
 | |
| 
 | |
|         local zone = lib.zones.box(zoneData)
 | |
|         shopZones[#shopZones+1] = zone
 | |
| 
 | |
|         local point = drawCustomMarker(shop.coords, shop.marker, 10.0, mechanicId, shop.name, nil)
 | |
|         shopPoints[#shopPoints + 1] = point
 | |
|       end
 | |
| 
 | |
|       ::shop_continue::
 | |
|     end
 | |
| 
 | |
|     ::continue::
 | |
|   end
 | |
| end
 | |
| 
 | |
| function createMechanicStashes()
 | |
|   for _, zone in pairs(stashZones) do
 | |
|     if zone and zone.remove then zone:remove() end
 | |
|   end
 | |
| 
 | |
|   for _, ped in pairs(stashPeds) do
 | |
|     if ped then DeleteEntity(ped) end
 | |
|   end
 | |
| 
 | |
|   for _, point in pairs(stashPoints) do
 | |
|     if point and point.remove then point:remove() end
 | |
|   end
 | |
| 
 | |
|   stashZones, stashPeds, stashPoints = {}, {}, {}
 | |
| 
 | |
|   for mechanicId, mechanicConfig in pairs(Config.MechanicLocations) do
 | |
|     local isMechanicEmployee = lib.callback.await("jg-mechanic:server:is-mechanic-employee", false, mechanicId)
 | |
|     if not isMechanicEmployee then goto continue end
 | |
| 
 | |
|     if not mechanicConfig or not mechanicConfig.stashes then
 | |
|       debugPrint(("No stashes defined for '%s' in Config.MechanicLocations"):format(mechanicId), "warning")
 | |
|       goto continue
 | |
|     end
 | |
| 
 | |
|     for stashIndex, stash in ipairs(mechanicConfig.stashes) do
 | |
|       if stash.type == "self-service" then
 | |
|         debugPrint(("Skipping self-service stash for '%s' (index: %d)"):format(mechanicId, stashIndex), "debug")
 | |
|         goto stash_continue
 | |
|       end
 | |
| 
 | |
|       if not stash.coords or not stash.size then
 | |
|         debugPrint(("Invalid stash data for '%s' (index: %d) - missing coords or size"):format(mechanicId, stashIndex), "warning")
 | |
|         goto stash_continue
 | |
|       end
 | |
| 
 | |
|       local stashId = mechanicId .. "_" .. stash.name:gsub(" ", "_"):lower() .. "_" .. stashIndex
 | |
| 
 | |
|       if stash.usePed then
 | |
|         local ped = createPedForTarget(stash.pedModel or "s_m_m_lathandy_01", stash.coords.xyz)
 | |
|         stashPeds[#stashPeds + 1] = ped
 | |
| 
 | |
|         -- If vec4, set heading
 | |
|         if stash.coords.w then
 | |
|           SetEntityHeading(ped, stash.coords.w or 0)
 | |
|         end
 | |
| 
 | |
|         if Config.Target == "qb-target" then
 | |
|           exports["qb-target"]:AddTargetEntity(ped, {
 | |
|             options = {
 | |
|               {
 | |
|                 label = "Access Stash",
 | |
|                 icon = "fas fa-box",
 | |
|                 action = function()
 | |
|                   Framework.Client.OpenInventoryStash(stashId, stash.name)
 | |
|                 end,
 | |
|               },
 | |
|             },
 | |
|             distance = 2.5,
 | |
|           })
 | |
|         elseif Config.Target == "ox_target" then
 | |
|           exports.ox_target:addLocalEntity(ped, {
 | |
|             {
 | |
|               name = "access_stash_" .. stashId,
 | |
|               label = "Access Stash",
 | |
|               icon = "fas fa-box",
 | |
|               onSelect = function()
 | |
|                 Framework.Client.OpenInventoryStash(stashId, stash.name)
 | |
|               end,
 | |
|             },
 | |
|           })
 | |
|         end
 | |
|       else
 | |
|         local zoneData = {
 | |
|           coords = stash.coords,
 | |
|           size = vector3(stash.size, stash.size, 2.0),
 | |
|           rotation = stash.coords.w or 0,
 | |
|           debug = Config.Debug,
 | |
|           onEnter = function()
 | |
|             Framework.Client.ShowTextUI("Press [E] to access " .. stash.name)
 | |
|           end,
 | |
|           onExit = function()
 | |
|             Framework.Client.HideTextUI()
 | |
|           end,
 | |
|           inside = function()
 | |
|             if IsControlJustReleased(0, 38) then
 | |
|               Framework.Client.OpenInventoryStash(stashId, stash.name)
 | |
|             end
 | |
|           end
 | |
|         }
 | |
| 
 | |
|         local zone = lib.zones.box(zoneData)
 | |
|         stashZones[#stashZones + 1] = zone
 | |
|         
 | |
|         local point = drawCustomMarker(stash.coords, stash.marker, 10.0, mechanicId, nil, stash.name)
 | |
|         stashPoints[#stashPoints + 1] = point 
 | |
|       end
 | |
| 
 | |
|       ::stash_continue::
 | |
|     end
 | |
| 
 | |
|     ::continue::
 | |
|   end
 | |
| end
 | |
| 
 | |
| -- Remove peds on resource stop/restart
 | |
| AddEventHandler("onResourceStop", function(resource)
 | |
|   if resource ~= GetCurrentResourceName() then return end
 | |
| 
 | |
|   if shopPeds then
 | |
|     for _, ped in pairs(shopPeds) do
 | |
|       if DoesEntityExist(ped) then
 | |
|         DeleteEntity(ped)
 | |
|       end
 | |
|     end
 | |
|     shopPeds = {}
 | |
|   end
 | |
| 
 | |
|   if stashPeds then
 | |
|     for _, ped in pairs(stashPeds) do
 | |
|       if DoesEntityExist(ped) then
 | |
|         DeleteEntity(ped)
 | |
|       end
 | |
|     end
 | |
|     stashPeds = {}
 | |
|   end
 | |
| end) | 
