332 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| local QBCore = exports['qb-core']:GetCoreObject()
 | |
| local placedBowls = {}
 | |
| 
 | |
| -- Load placed bowls from server
 | |
| RegisterNetEvent('pet-bowls:client:loadBowls', function(bowls)
 | |
|     print("^2[Pet-Bowls]^7 Loading " .. #bowls .. " bowls from server")
 | |
|     
 | |
|     for bowlId, bowl in pairs(bowls) do
 | |
|         local coords = json.decode(bowl.coords)
 | |
|         local bowlCoords = vector3(coords.x, coords.y, coords.z)
 | |
|         
 | |
|         -- Find the closest object to these coordinates
 | |
|         local closestObject = nil
 | |
|         local closestDistance = 2.0 -- Maximum distance to consider
 | |
|         
 | |
|         local objects = GetGamePool('CObject')
 | |
|         for _, object in pairs(objects) do
 | |
|             if DoesEntityExist(object) then
 | |
|                 local objectCoords = GetEntityCoords(object)
 | |
|                 local distance = #(objectCoords - bowlCoords)
 | |
|                 
 | |
|                 if distance < closestDistance then
 | |
|                     local model = GetEntityModel(object)
 | |
|                     -- Check if this is a valid bowl model
 | |
|                     for _, bowlConfig in pairs(Config.BowlProps) do
 | |
|                         if GetHashKey(bowlConfig.model) == model then
 | |
|                             closestObject = object
 | |
|                             closestDistance = distance
 | |
|                             break
 | |
|                         end
 | |
|                     end
 | |
|                 end
 | |
|             end
 | |
|         end
 | |
|         
 | |
|         if closestObject then
 | |
|             -- Store in local table
 | |
|             placedBowls[bowlId] = {
 | |
|                 object = closestObject,
 | |
|                 id = bowlId,
 | |
|                 model = bowl.model,
 | |
|                 type = bowl.type,
 | |
|                 fillLevel = bowl.fill_level
 | |
|             }
 | |
|             
 | |
|             -- Add target
 | |
|             AddTargetToBowl(closestObject, bowlId, bowl.type)
 | |
|             print("^2[Pet-Bowls]^7 Added target to bowl: " .. bowlId)
 | |
|         else
 | |
|             print("^3[Pet-Bowls]^7 Could not find object for bowl: " .. bowlId)
 | |
|         end
 | |
|     end
 | |
| end)
 | |
| 
 | |
| -- Function to add qb-target to a bowl
 | |
| function AddTargetToBowl(bowlObject, bowlId, bowlType)
 | |
|     if not DoesEntityExist(bowlObject) then 
 | |
|         print("^1[Pet-Bowls]^7 Cannot add target to non-existent object")
 | |
|         return 
 | |
|     end
 | |
|     
 | |
|     -- Debug info
 | |
|     print("^2[Pet-Bowls]^7 Adding target to object: " .. bowlObject .. " with ID: " .. bowlId)
 | |
|     
 | |
|     -- Add target with explicit options
 | |
|     exports['qb-target']:AddTargetEntity(bowlObject, {
 | |
|         options = {
 | |
|             {
 | |
|                 type = "client",
 | |
|                 event = "pet-bowls:client:useBowl",
 | |
|                 icon = "fas fa-hand",
 | |
|                 label = "Use Bowl",
 | |
|                 bowlId = bowlId,
 | |
|                 bowlType = bowlType
 | |
|             },
 | |
|             {
 | |
|                 type = "client",
 | |
|                 event = "pet-bowls:client:openFillMenu",
 | |
|                 icon = "fas fa-fill",
 | |
|                 label = "Fill Bowl",
 | |
|                 bowlId = bowlId,
 | |
|                 bowlType = bowlType
 | |
|             }
 | |
|         },
 | |
|         distance = 2.0
 | |
|     })
 | |
| end
 | |
| 
 | |
| -- Event handlers for target interactions
 | |
| RegisterNetEvent('pet-bowls:client:useBowl', function(data)
 | |
|     UseBowl(data.bowlId, data.bowlType)
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('pet-bowls:client:openFillMenu', function(data)
 | |
|     OpenFillMenu(data.bowlId, data.bowlType)
 | |
| end)
 | |
| 
 | |
| -- Function to use a bowl
 | |
| function UseBowl(bowlId, bowlType)
 | |
|     local bowl = placedBowls[bowlId]
 | |
|     if not bowl then 
 | |
|         print("^1[Pet-Bowls]^7 Bowl not found: " .. bowlId)
 | |
|         return 
 | |
|     end
 | |
|     
 | |
|     -- Check if bowl has content
 | |
|     if bowl.fillLevel <= 0 then
 | |
|         lib.notify(Config.Notifications.bowlEmpty)
 | |
|         return
 | |
|     end
 | |
|     
 | |
|     -- Set animation and progress bar based on bowl type
 | |
|     local progressConfig = bowlType == 'food' and Config.ProgressBar.eating or Config.ProgressBar.drinking
 | |
|     
 | |
|     -- Start progress bar
 | |
|     if lib.progressBar(progressConfig) then
 | |
|         -- Consume from bowl
 | |
|         TriggerServerEvent('pet-bowls:server:consumeBowl', bowlId)
 | |
|     end
 | |
| end
 | |
| 
 | |
| -- Function to open fill menu
 | |
| function OpenFillMenu(bowlId, bowlType)
 | |
|     local bowl = placedBowls[bowlId]
 | |
|     if not bowl then return end
 | |
|     
 | |
|     -- Get fill items for this bowl type
 | |
|     local fillItems = Config.FillItems[bowlType]
 | |
|     local options = {}
 | |
|     
 | |
|     for _, item in pairs(fillItems) do
 | |
|         table.insert(options, {
 | |
|             title = item.label,
 | |
|             description = 'Fill Amount: ' .. item.fillAmount .. '%',
 | |
|             onSelect = function()
 | |
|                 FillBowl(bowlId, item.item, item.fillAmount)
 | |
|             end
 | |
|         })
 | |
|     end
 | |
|     
 | |
|     lib.registerContext({
 | |
|         id = 'bowl_fill_menu',
 | |
|         title = 'Fill ' .. (bowlType == 'food' and 'Food' or 'Water') .. ' Bowl (' .. bowl.fillLevel .. '%)',
 | |
|         options = options
 | |
|     })
 | |
|     
 | |
|     lib.showContext('bowl_fill_menu')
 | |
| end
 | |
| 
 | |
| -- Function to fill a bowl
 | |
| function FillBowl(bowlId, itemName, fillAmount)
 | |
|     -- Start progress bar
 | |
|     if lib.progressBar(Config.ProgressBar.filling) then
 | |
|         -- Fill the bowl
 | |
|         TriggerServerEvent('pet-bowls:server:fillBowl', bowlId, itemName, fillAmount)
 | |
|     end
 | |
| end
 | |
| 
 | |
| -- Update bowl fill level
 | |
| RegisterNetEvent('pet-bowls:client:updateBowlLevel', function(bowlId, newLevel)
 | |
|     if placedBowls[bowlId] then
 | |
|         placedBowls[bowlId].fillLevel = newLevel
 | |
|     end
 | |
| end)
 | |
| 
 | |
| -- Register a new bowl (for props placed with ProPlacer)
 | |
| RegisterNetEvent('pet-bowls:client:registerNewBowl', function(modelName, bowlType)
 | |
|     -- Check if we received parameters directly or as part of a data table
 | |
|     local model, type
 | |
|     
 | |
|     -- Handle both direct parameters and data table format
 | |
|     if type(modelName) == "table" and modelName.modelName then
 | |
|         -- We received a data table from qb-target
 | |
|         model = modelName.modelName
 | |
|         type = modelName.bowlType
 | |
|     else
 | |
|         -- We received direct parameters
 | |
|         model = modelName
 | |
|         type = bowlType
 | |
|     end
 | |
|     
 | |
|     -- Ensure we have valid parameters
 | |
|     if not model or not type then
 | |
|         print("^1[Pet-Bowls]^7 Missing model or type for bowl registration")
 | |
|         return
 | |
|     end
 | |
|     
 | |
|     -- Find the closest valid prop
 | |
|     local playerPed = PlayerPedId()
 | |
|     local playerCoords = GetEntityCoords(playerPed)
 | |
|     
 | |
|     local closestObject = nil
 | |
|     local closestDistance = 3.0 -- Maximum distance to consider
 | |
|     local validModel = GetHashKey(model)
 | |
|     
 | |
|     local objects = GetGamePool('CObject')
 | |
|     for _, object in pairs(objects) do
 | |
|         if DoesEntityExist(object) and GetEntityModel(object) == validModel then
 | |
|             local objectCoords = GetEntityCoords(object)
 | |
|             local distance = #(objectCoords - playerCoords)
 | |
|             
 | |
|             if distance < closestDistance then
 | |
|                 closestObject = object
 | |
|                 closestDistance = distance
 | |
|             end
 | |
|         end
 | |
|     end
 | |
|     
 | |
|     if closestObject then
 | |
|         -- Generate a unique ID for this bowl
 | |
|         local bowlId = 'bowl_' .. math.random(100000, 999999) .. '_' .. GetGameTimer()
 | |
|         
 | |
|         -- Save to server
 | |
|         local coords = GetEntityCoords(closestObject)
 | |
|         local heading = GetEntityHeading(closestObject)
 | |
|         
 | |
|         TriggerServerEvent('pet-bowls:server:placeBowl', bowlId, model, type, {
 | |
|             x = coords.x,
 | |
|             y = coords.y,
 | |
|             z = coords.z,
 | |
|             w = heading
 | |
|         })
 | |
|         
 | |
|         -- Store locally
 | |
|         placedBowls[bowlId] = {
 | |
|             object = closestObject,
 | |
|             id = bowlId,
 | |
|             model = model,
 | |
|             type = type,
 | |
|             fillLevel = 0
 | |
|         }
 | |
|         
 | |
|         -- Add target
 | |
|         AddTargetToBowl(closestObject, bowlId, type)
 | |
|         
 | |
|         lib.notify(Config.Notifications.bowlPlaced)
 | |
|     else
 | |
|         lib.notify({
 | |
|             title = 'Bowl System',
 | |
|             description = 'No valid bowl prop found nearby!',
 | |
|             type = 'error'
 | |
|         })
 | |
|     end
 | |
| end)
 | |
| 
 | |
| -- Command to register a bowl
 | |
| RegisterCommand('registerbowl', function()
 | |
|     OpenRegisterBowlMenu()
 | |
| end, false)
 | |
| 
 | |
| -- Function to open register bowl menu
 | |
| function OpenRegisterBowlMenu()
 | |
|     local options = {}
 | |
|     
 | |
|     for _, bowl in pairs(Config.BowlProps) do
 | |
|         table.insert(options, {
 | |
|             title = bowl.label,
 | |
|             description = 'Type: ' .. (bowl.type == 'food' and 'Food Bowl' or 'Water Bowl'),
 | |
|             onSelect = function()
 | |
|                 TriggerEvent('pet-bowls:client:registerNewBowl', bowl.model, bowl.type)
 | |
|             end
 | |
|         })
 | |
|     end
 | |
|     
 | |
|     lib.registerContext({
 | |
|         id = 'register_bowl_menu',
 | |
|         title = 'Register Bowl',
 | |
|         options = options
 | |
|     })
 | |
|     
 | |
|     lib.showContext('register_bowl_menu')
 | |
| end
 | |
| 
 | |
| -- Initialize
 | |
| Citizen.CreateThread(function()
 | |
|     -- Wait for world to load
 | |
|     Citizen.Wait(2000)
 | |
|     
 | |
|     -- Request all placed bowls from server
 | |
|     TriggerServerEvent('pet-bowls:server:requestBowls')
 | |
|     
 | |
|     -- Add target to all valid bowl props in the world that aren't registered yet
 | |
|     Citizen.Wait(5000) -- Wait for bowls to load
 | |
|     
 | |
|     local validModels = {}
 | |
|     for _, bowlConfig in pairs(Config.BowlProps) do
 | |
|         validModels[GetHashKey(bowlConfig.model)] = bowlConfig
 | |
|     end
 | |
|     
 | |
|     local objects = GetGamePool('CObject')
 | |
|     local addedTargets = 0
 | |
|     
 | |
|     for _, object in pairs(objects) do
 | |
|         if DoesEntityExist(object) then
 | |
|             local model = GetEntityModel(object)
 | |
|             
 | |
|             if validModels[model] then
 | |
|                 -- Check if this object is already registered as a bowl
 | |
|                 local isRegistered = false
 | |
|                 for _, bowl in pairs(placedBowls) do
 | |
|                     if bowl.object == object then
 | |
|                         isRegistered = true
 | |
|                         break
 | |
|                     end
 | |
|                 end
 | |
|                 
 | |
|                 if not isRegistered then
 | |
|                     -- Add a register option to this unregistered bowl prop
 | |
|                     local bowlConfig = validModels[model]
 | |
|                     
 | |
|                     exports['qb-target']:AddTargetEntity(object, {
 | |
|                         options = {
 | |
|                             {
 | |
|                                 type = "client",
 | |
|                                 event = "pet-bowls:client:registerNewBowl",
 | |
|                                 icon = "fas fa-plus",
 | |
|                                 label = "Register as " .. bowlConfig.label,
 | |
|                                 modelName = bowlConfig.model,
 | |
|                                 bowlType = bowlConfig.type
 | |
|                             }
 | |
|                         },
 | |
|                         distance = 2.0
 | |
|                     })
 | |
|                     
 | |
|                     addedTargets = addedTargets + 1
 | |
|                 end
 | |
|             end
 | |
|         end
 | |
|     end
 | |
|     
 | |
|     print("^2[Pet-Bowls]^7 Added registration targets to " .. addedTargets .. " unregistered bowl props")
 | |
| end)
 | 
