316 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			316 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local QBCore = exports['qb-core']:GetCoreObject()
 | 
						|
local Config = require 'config'
 | 
						|
 | 
						|
-- SQL Setup
 | 
						|
local function SetupDatabase()
 | 
						|
    local query = [[
 | 
						|
        CREATE TABLE IF NOT EXISTS player_vending_machines (
 | 
						|
            id INT AUTO_INCREMENT PRIMARY KEY,
 | 
						|
            owner VARCHAR(50) NOT NULL,
 | 
						|
            model_hash BIGINT NOT NULL,
 | 
						|
            location LONGTEXT NOT NULL,
 | 
						|
            stock INT DEFAULT 0,
 | 
						|
            earnings DECIMAL(10,2) DEFAULT 0,
 | 
						|
            last_collected BIGINT DEFAULT 0,
 | 
						|
            UNIQUE KEY unique_machine (owner, model_hash, location(255))
 | 
						|
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 | 
						|
    ]]
 | 
						|
    MySQL.ready(function()
 | 
						|
        MySQL.query(query, {}, function(result)
 | 
						|
            print('^2[Vending Machines]^7 Database table initialized')
 | 
						|
        end)
 | 
						|
    end)
 | 
						|
end
 | 
						|
 | 
						|
-- Global state for 24/7 machine robberies
 | 
						|
local robberyCooldown = {}
 | 
						|
 | 
						|
-- Register usable items
 | 
						|
QBCore.Functions.CreateUseableItem(Config.RobberyItem, function(source)
 | 
						|
    TriggerClientEvent('vendingmachines:client:startRobbery', source)
 | 
						|
end)
 | 
						|
 | 
						|
if Config.PlayerOwnedMachines.RestockItem then
 | 
						|
    QBCore.Functions.CreateUseableItem(Config.PlayerOwnedMachines.RestockItem, function(source)
 | 
						|
        TriggerClientEvent('vendingmachines:client:startRestock', source)
 | 
						|
    end)
 | 
						|
end
 | 
						|
 | 
						|
-- Player-owned machine management
 | 
						|
local function GetPlayerMachines(citizenid)
 | 
						|
    local result = MySQL.query.await('SELECT * FROM player_vending_machines WHERE owner = ?', { citizenid })
 | 
						|
    return result
 | 
						|
end
 | 
						|
 | 
						|
local function GetMachineById(id)
 | 
						|
    local result = MySQL.query.await('SELECT * FROM player_vending_machines WHERE id = ?', { id })
 | 
						|
    return result[1]
 | 
						|
end
 | 
						|
 | 
						|
local function RegisterPlayerMachine(source, modelHash, coords)
 | 
						|
    local Player = QBCore.Functions.GetPlayer(source)
 | 
						|
    if not Player then return false end
 | 
						|
 | 
						|
    local location = json.encode({
 | 
						|
        x = coords.x,
 | 
						|
        y = coords.y,
 | 
						|
        z = coords.z
 | 
						|
    })
 | 
						|
 | 
						|
    local result = MySQL.insert.await([[
 | 
						|
        INSERT INTO player_vending_machines (owner, model_hash, location)
 | 
						|
        VALUES (?, ?, ?)
 | 
						|
    ]], { Player.PlayerData.citizenid, modelHash, location })
 | 
						|
 | 
						|
    if result then
 | 
						|
        TriggerClientEvent('vendingmachines:client:refreshPlayerMachines', -1)
 | 
						|
        return true
 | 
						|
    end
 | 
						|
    return false
 | 
						|
end
 | 
						|
 | 
						|
local function RestockMachine(machineId, amount)
 | 
						|
    local machine = GetMachineById(machineId)
 | 
						|
    if not machine then return false end
 | 
						|
 | 
						|
    local newStock = math.min(machine.stock + amount, Config.PlayerOwnedMachines.MaxStock)
 | 
						|
    MySQL.update.await('UPDATE player_vending_machines SET stock = ? WHERE id = ?', { newStock, machineId })
 | 
						|
    return true
 | 
						|
end
 | 
						|
 | 
						|
local function CollectEarnings(machineId)
 | 
						|
    local machine = GetMachineById(machineId)
 | 
						|
    if not machine or machine.earnings <= 0 then return 0 end
 | 
						|
 | 
						|
    local amount = machine.earnings
 | 
						|
    MySQL.update.await('UPDATE player_vending_machines SET earnings = 0, last_collected = ? WHERE id = ?', 
 | 
						|
        { os.time(), machineId })
 | 
						|
    return amount
 | 
						|
end
 | 
						|
 | 
						|
-- Handle vending machine purchases
 | 
						|
local function ProcessPurchase(source, isPlayerOwned, machineId, itemName, amount)
 | 
						|
    local Player = QBCore.Functions.GetPlayer(source)
 | 
						|
    if not Player then return false end
 | 
						|
 | 
						|
    if isPlayerOwned then
 | 
						|
        -- Player-owned machine purchase
 | 
						|
        local machine = GetMachineById(machineId)
 | 
						|
        if not machine or machine.stock < amount then return false end
 | 
						|
 | 
						|
        local itemData = nil
 | 
						|
        for _, item in pairs(Config.DefaultMachines.Models[machine.model_hash].items) do
 | 
						|
            if item.name == itemName then
 | 
						|
                itemData = item
 | 
						|
                break
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        if not itemData then return false end
 | 
						|
 | 
						|
        local totalPrice = itemData.price * amount
 | 
						|
        if Player.Functions.RemoveMoney('cash', totalPrice) then
 | 
						|
            local earnings = totalPrice
 | 
						|
            local ownerCut = math.floor(earnings * Config.PlayerOwnedMachines.OwnerCut)
 | 
						|
            local businessCut = earnings - ownerCut
 | 
						|
 | 
						|
            MySQL.update.await('UPDATE player_vending_machines SET stock = stock - ?, earnings = earnings + ? WHERE id = ?', 
 | 
						|
                { amount, businessCut, machineId })
 | 
						|
 | 
						|
            -- Give item to player
 | 
						|
            Player.Functions.AddItem(itemName, amount)
 | 
						|
            return true
 | 
						|
        end
 | 
						|
    else
 | 
						|
        -- 24/7 machine purchase
 | 
						|
        local modelHash = tonumber(machineId)
 | 
						|
        local machineItems = Config.DefaultMachines.Models[modelHash]
 | 
						|
        if not machineItems then return false end
 | 
						|
 | 
						|
        local itemData = nil
 | 
						|
        for _, item in pairs(machineItems.items) do
 | 
						|
            if item.name == itemName then
 | 
						|
                itemData = item
 | 
						|
                break
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        if not itemData then return false end
 | 
						|
 | 
						|
        local totalPrice = itemData.price * amount
 | 
						|
        if Player.Functions.RemoveMoney('cash', totalPrice) then
 | 
						|
            Player.Functions.AddItem(itemName, amount)
 | 
						|
            return true
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
-- Robbery functions
 | 
						|
local function AlertPolice(machineCoords)
 | 
						|
    if Config.Dispatch == 'ps-dispatch' then
 | 
						|
        exports['ps-dispatch']:VendingMachineRobbery(machineCoords)
 | 
						|
    elseif Config.Dispatch == 'qb-dispatch' then
 | 
						|
        TriggerEvent('qb-dispatch:officerDistress', {
 | 
						|
            dispatchCode = '10-31',
 | 
						|
            firstStreet = GetStreetAndZone(machineCoords),
 | 
						|
            priority = 2,
 | 
						|
            origin = {
 | 
						|
                x = machineCoords.x,
 | 
						|
                y = machineCoords.y,
 | 
						|
                z = machineCoords.z
 | 
						|
            },
 | 
						|
            blip = {
 | 
						|
                sprite = 52,
 | 
						|
                scale = 1.5,
 | 
						|
                color = 1,
 | 
						|
                flashes = true,
 | 
						|
                text = 'Vending Machine Robbery',
 | 
						|
                time = 5
 | 
						|
            }
 | 
						|
        })
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function CompleteRobbery(source, isPlayerOwned, machineId)
 | 
						|
    local Player = QBCore.Functions.GetPlayer(source)
 | 
						|
    if not Player then return false end
 | 
						|
 | 
						|
    if robberyCooldown[source] and os.time() - robberyCooldown[source] < 300 then
 | 
						|
        TriggerClientEvent('QBCore:Notify', source, 'You need to wait before robbing again', 'error')
 | 
						|
        return false
 | 
						|
    end
 | 
						|
 | 
						|
    if Player.Functions.RemoveItem(Config.RobberyItem, 1) then
 | 
						|
        local reward = math.random(Config.MinRobberyCash, Config.MaxRobberyCash)
 | 
						|
        Player.Functions.AddMoney('cash', reward)
 | 
						|
 | 
						|
        if math.random(1, 100) <= Config.RobberyAlertChance then
 | 
						|
            local coords = GetEntityCoords(GetPlayerPed(source))
 | 
						|
            AlertPolice(coords)
 | 
						|
        end
 | 
						|
 | 
						|
        robberyCooldown[source] = os.time()
 | 
						|
        
 | 
						|
        if isPlayerOwned then
 | 
						|
            local machine = GetMachineById(machineId)
 | 
						|
            if machine then
 | 
						|
                MySQL.update.await('UPDATE player_vending_machines SET stock = GREATEST(0, stock - ?) WHERE id = ?', 
 | 
						|
                    { math.random(5, 15), machineId })
 | 
						|
            end
 | 
						|
        end
 | 
						|
        
 | 
						|
        return true
 | 
						|
    end
 | 
						|
    return false
 | 
						|
end
 | 
						|
 | 
						|
-- Server Events
 | 
						|
RegisterNetEvent('vendingmachines:server:registerMachine', function(modelHash, coords)
 | 
						|
    local src = source
 | 
						|
    if RegisterPlayerMachine(src, modelHash, coords) then
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, 'Vending machine registered successfully', 'success')
 | 
						|
    else
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, 'Failed to register vending machine', 'error')
 | 
						|
    end
 | 
						|
end)
 | 
						|
 | 
						|
RegisterNetEvent('vendingmachines:server:processPurchase', function(isPlayerOwned, machineId, itemName, amount)
 | 
						|
    local src = source
 | 
						|
    if ProcessPurchase(src, isPlayerOwned, machineId, itemName, amount) then
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, 'Purchase successful', 'success')
 | 
						|
    else
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, 'Purchase failed', 'error')
 | 
						|
    end
 | 
						|
end)
 | 
						|
 | 
						|
RegisterNetEvent('vendingmachines:server:restockMachine', function(machineId)
 | 
						|
    local src = source
 | 
						|
    local Player = QBCore.Functions.GetPlayer(src)
 | 
						|
    if not Player then return end
 | 
						|
 | 
						|
    local jobName = Player.PlayerData.job.name
 | 
						|
    local allowed = false
 | 
						|
    for _, job in pairs(Config.PlayerOwnedMachines.AllowedJobs) do
 | 
						|
        if job == jobName then
 | 
						|
            allowed = true
 | 
						|
            break
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    if not allowed then
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, 'You are not authorized to restock machines', 'error')
 | 
						|
        return
 | 
						|
    end
 | 
						|
 | 
						|
    if Player.Functions.RemoveItem(Config.PlayerOwnedMachines.RestockItem, 1) then
 | 
						|
        if RestockMachine(machineId, Config.PlayerOwnedMachines.StockAmount) then
 | 
						|
            TriggerClientEvent('QBCore:Notify', src, 'Machine restocked successfully', 'success')
 | 
						|
        else
 | 
						|
            Player.Functions.AddItem(Config.PlayerOwnedMachines.RestockItem, 1)
 | 
						|
            TriggerClientEvent('QBCore:Notify', src, 'Failed to restock machine', 'error')
 | 
						|
        end
 | 
						|
    else
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, 'You don\'t have the restock item', 'error')
 | 
						|
    end
 | 
						|
end)
 | 
						|
 | 
						|
RegisterNetEvent('vendingmachines:server:collectEarnings', function(machineId)
 | 
						|
    local src = source
 | 
						|
    local Player = QBCore.Functions.GetPlayer(src)
 | 
						|
    if not Player then return end
 | 
						|
 | 
						|
    local jobName = Player.PlayerData.job.name
 | 
						|
    local allowed = false
 | 
						|
    for _, job in pairs(Config.PlayerOwnedMachines.AllowedJobs) do
 | 
						|
        if job == jobName then
 | 
						|
            allowed = true
 | 
						|
            break
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    if not allowed then
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, 'You are not authorized to collect earnings', 'error')
 | 
						|
        return
 | 
						|
    end
 | 
						|
 | 
						|
    local amount = CollectEarnings(machineId)
 | 
						|
    if amount > 0 then
 | 
						|
        Player.Functions.AddMoney('cash', amount)
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, string.format('Collected $%d from the machine', amount), 'success')
 | 
						|
    else
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, 'No earnings to collect', 'error')
 | 
						|
    end
 | 
						|
end)
 | 
						|
 | 
						|
RegisterNetEvent('vendingmachines:server:completeRobbery', function(isPlayerOwned, machineId)
 | 
						|
    local src = source
 | 
						|
    if CompleteRobbery(src, isPlayerOwned, machineId) then
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, 'Robbery successful', 'success')
 | 
						|
    else
 | 
						|
        TriggerClientEvent('QBCore:Notify', src, 'Robbery failed', 'error')
 | 
						|
    end
 | 
						|
end)
 | 
						|
 | 
						|
-- Commands
 | 
						|
QBCore.Commands.Add('registermachine', 'Register a player-owned vending machine', {}, false, function(source)
 | 
						|
    TriggerClientEvent('vendingmachines:client:registerMachine', source)
 | 
						|
end, 'admin')
 | 
						|
 | 
						|
QBCore.Commands.Add('mymachines', 'View your owned vending machines', {}, false, function(source)
 | 
						|
    local Player = QBCore.Functions.GetPlayer(source)
 | 
						|
    if not Player then return end
 | 
						|
 | 
						|
    local machines = GetPlayerMachines(Player.PlayerData.citizenid)
 | 
						|
    if #machines > 0 then
 | 
						|
        local msg = "Your machines:\n"
 | 
						|
        for _, machine in pairs(machines) do
 | 
						|
            msg = msg .. string.format("ID: %d | Stock: %d | Earnings: $%.2f\n", machine.id, machine.stock, machine.earnings)
 | 
						|
        end
 | 
						|
        TriggerClientEvent('chat:addMessage', source, { args = { 'SYSTEM', msg } })
 | 
						|
    else
 | 
						|
        TriggerClientEvent('QBCore:Notify', source, 'You don\'t own any machines', 'error')
 | 
						|
    end
 | 
						|
end)
 | 
						|
 | 
						|
-- Initialize
 | 
						|
SetupDatabase()
 |