forked from Simnation/Main
317 lines
11 KiB
Lua
317 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()
|