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)
 |