2025-07-29 07:30:32 +02:00
local QBCore = exports [ ' qb-core ' ] : GetCoreObject ( )
local vendingMachines = { }
local robberyInProgress = { }
-- Load vending machines from database
CreateThread ( function ( )
local result = MySQL.Sync . fetchAll ( ' SELECT * FROM vending_machines ' )
if result then
for i = 1 , # result do
local data = result [ i ]
vendingMachines [ data.id ] = {
id = data.id ,
owner = data.owner ,
coords = json.decode ( data.coords ) ,
prop = data.prop ,
money = data.money ,
items = json.decode ( data.items ) or { } ,
prices = json.decode ( data.prices ) or { } ,
stash = ' vending_ ' .. data.id
}
end
end
end )
-- Register vending machine (when player buys it)
RegisterNetEvent ( ' vending:server:registerMachine ' , function ( coords , prop )
local src = source
local Player = QBCore.Functions . GetPlayer ( src )
if not Player then return end
-- Check if there's already a machine at these coords
for id , machine in pairs ( vendingMachines ) do
local dist = # ( vector3 ( coords.x , coords.y , coords.z ) - vector3 ( machine.coords . x , machine.coords . y , machine.coords . z ) )
if dist < 2.0 then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Hier ist bereits ein Automat registriert! ' , ' error ' )
return
end
end
-- Check if player has enough money
if Player.PlayerData . money.cash < Config.VendingMachinePrice then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Du benötigst $ ' .. Config.VendingMachinePrice .. ' um diesen Automaten zu kaufen! ' , ' error ' )
return
end
-- Remove money
Player.Functions . RemoveMoney ( ' cash ' , Config.VendingMachinePrice )
-- Create machine in database
local machineId = MySQL.insert . await ( ' INSERT INTO vending_machines (owner, coords, prop, money, items, prices) VALUES (?, ?, ?, ?, ?, ?) ' , {
Player.PlayerData . citizenid ,
json.encode ( coords ) ,
prop ,
0 ,
json.encode ( { } ) ,
json.encode ( { } )
} )
-- Add to memory
vendingMachines [ machineId ] = {
id = machineId ,
owner = Player.PlayerData . citizenid ,
coords = coords ,
prop = prop ,
money = 0 ,
items = { } ,
prices = { } ,
stash = ' vending_ ' .. machineId
}
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Verkaufsautomat erfolgreich gekauft für $ ' .. Config.VendingMachinePrice .. ' ! ' , ' success ' )
TriggerClientEvent ( ' vending:client:refreshTargets ' , - 1 )
end )
-- Open management menu
RegisterNetEvent ( ' vending:server:openManagement ' , function ( coords )
local src = source
local Player = QBCore.Functions . GetPlayer ( src )
if not Player then return end
local machineId = getMachineIdByCoords ( coords )
if not machineId then return end
local machine = vendingMachines [ machineId ]
if machine.owner ~= Player.PlayerData . citizenid then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Das ist nicht dein Verkaufsautomat! ' , ' error ' )
return
end
TriggerClientEvent ( ' vending:client:openManagement ' , src , machine )
end )
2025-07-29 08:39:45 +02:00
-- Open stash (mit mehreren Methoden versuchen)
2025-07-29 07:30:32 +02:00
RegisterNetEvent ( ' vending:server:openStash ' , function ( coords )
local src = source
local Player = QBCore.Functions . GetPlayer ( src )
if not Player then return end
local machineId = getMachineIdByCoords ( coords )
if not machineId then return end
local machine = vendingMachines [ machineId ]
if machine.owner ~= Player.PlayerData . citizenid then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Das ist nicht dein Verkaufsautomat! ' , ' error ' )
return
end
2025-07-29 08:39:45 +02:00
-- Versuche verschiedene Methoden für tgiann-inventory
local success = false
-- Methode 1: TriggerClientEvent
if not success then
local clientSuccess = pcall ( function ( )
TriggerClientEvent ( ' tgiann-inventory:client:openStash ' , src , {
stashId = machine.stash ,
stashLabel = ' Vending Machine # ' .. machine.id ,
maxweight = Config.MaxWeight ,
slots = Config.MaxSlots
} )
end )
if clientSuccess then
success = true
print ( ' [Vending] Opened stash via client event ' )
end
end
-- Methode 2: Server Event
if not success then
local eventSuccess = pcall ( function ( )
TriggerEvent ( ' tgiann-inventory:server:openStash ' , src , machine.stash , {
maxweight = Config.MaxWeight ,
slots = Config.MaxSlots ,
label = ' Vending Machine # ' .. machine.id
} )
end )
if eventSuccess then
success = true
print ( ' [Vending] Opened stash via server event ' )
end
end
-- Methode 3: Fallback - eigenes Stash-System
if not success then
TriggerClientEvent ( ' vending:client:openCustomStash ' , src , machine )
print ( ' [Vending] Opened custom stash fallback ' )
end
2025-07-29 07:30:32 +02:00
end )
-- Set item price
RegisterNetEvent ( ' vending:server:setItemPrice ' , function ( coords , itemName , price )
local src = source
local Player = QBCore.Functions . GetPlayer ( src )
if not Player then return end
local machineId = getMachineIdByCoords ( coords )
if not machineId then return end
local machine = vendingMachines [ machineId ]
if machine.owner ~= Player.PlayerData . citizenid then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Das ist nicht dein Verkaufsautomat! ' , ' error ' )
return
end
-- Update price
machine.prices [ itemName ] = price
MySQL.update ( ' UPDATE vending_machines SET prices = ? WHERE id = ? ' , { json.encode ( machine.prices ) , machineId } )
2025-07-29 08:25:12 +02:00
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Preis für ' .. ( QBCore.Shared . Items [ itemName ] and QBCore.Shared . Items [ itemName ] . label or itemName ) .. ' auf $ ' .. price .. ' gesetzt! ' , ' success ' )
2025-07-29 07:30:32 +02:00
end )
-- Withdraw money
RegisterNetEvent ( ' vending:server:withdrawMoney ' , function ( coords , amount )
local src = source
local Player = QBCore.Functions . GetPlayer ( src )
if not Player then return end
local machineId = getMachineIdByCoords ( coords )
if not machineId then return end
local machine = vendingMachines [ machineId ]
if machine.owner ~= Player.PlayerData . citizenid then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Das ist nicht dein Verkaufsautomat! ' , ' error ' )
return
end
if machine.money < amount then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Nicht genug Geld im Automaten! ' , ' error ' )
return
end
-- Update machine money
machine.money = machine.money - amount
MySQL.update ( ' UPDATE vending_machines SET money = ? WHERE id = ? ' , { machine.money , machineId } )
-- Give money to player
Player.Functions . AddMoney ( ' cash ' , amount )
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Du hast $ ' .. amount .. ' abgehoben! ' , ' success ' )
end )
2025-07-29 08:39:45 +02:00
-- Buy item from vending machine (mit Fallback-System)
2025-07-29 07:30:32 +02:00
RegisterNetEvent ( ' vending:server:buyItem ' , function ( coords , itemName )
local src = source
local Player = QBCore.Functions . GetPlayer ( src )
if not Player then return end
local machineId = getMachineIdByCoords ( coords )
if not machineId then return end
local machine = vendingMachines [ machineId ]
local price = machine.prices [ itemName ] or Config.DefaultPrice
-- Check if player has enough money
if Player.PlayerData . money.cash < price then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Du hast nicht genug Geld! ' , ' error ' )
return
end
2025-07-29 08:39:45 +02:00
-- Check if item is available in our database
if not machine.items [ itemName ] or machine.items [ itemName ] <= 0 then
2025-07-29 08:35:03 +02:00
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Artikel nicht verfügbar! ' , ' error ' )
return
end
-- Remove money from player
Player.Functions . RemoveMoney ( ' cash ' , price )
-- Add money to machine
machine.money = machine.money + price
2025-07-29 08:39:45 +02:00
-- Remove item from machine
machine.items [ itemName ] = machine.items [ itemName ] - 1
-- Update database
MySQL.update ( ' UPDATE vending_machines SET money = ?, items = ? WHERE id = ? ' , {
machine.money ,
json.encode ( machine.items ) ,
machineId
} )
-- Add item to player using QBCore
Player.Functions . AddItem ( itemName , 1 )
TriggerClientEvent ( ' inventory:client:ItemBox ' , src , QBCore.Shared . Items [ itemName ] , ' add ' )
2025-07-29 08:35:03 +02:00
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Artikel gekauft für $ ' .. price .. ' ! ' , ' success ' )
2025-07-29 07:30:32 +02:00
end )
2025-07-29 08:39:45 +02:00
-- Add item to vending machine (for stocking)
RegisterNetEvent ( ' vending:server:addItem ' , function ( coords , itemName , amount )
local src = source
local Player = QBCore.Functions . GetPlayer ( src )
if not Player then return end
local machineId = getMachineIdByCoords ( coords )
if not machineId then return end
local machine = vendingMachines [ machineId ]
if machine.owner ~= Player.PlayerData . citizenid then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Das ist nicht dein Verkaufsautomat! ' , ' error ' )
return
end
-- Check if player has the item
local item = Player.Functions . GetItemByName ( itemName )
if not item or item.amount < amount then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Du hast nicht genug von diesem Item! ' , ' error ' )
return
end
-- Remove item from player
Player.Functions . RemoveItem ( itemName , amount )
TriggerClientEvent ( ' inventory:client:ItemBox ' , src , QBCore.Shared . Items [ itemName ] , ' remove ' )
-- Add item to machine
if not machine.items [ itemName ] then
machine.items [ itemName ] = 0
end
machine.items [ itemName ] = machine.items [ itemName ] + amount
-- Update database
MySQL.update ( ' UPDATE vending_machines SET items = ? WHERE id = ? ' , { json.encode ( machine.items ) , machineId } )
TriggerClientEvent ( ' QBCore:Notify ' , src , amount .. ' x ' .. ( QBCore.Shared . Items [ itemName ] and QBCore.Shared . Items [ itemName ] . label or itemName ) .. ' zum Automaten hinzugefügt! ' , ' success ' )
end )
-- Remove item from vending machine
RegisterNetEvent ( ' vending:server:removeItem ' , function ( coords , itemName , amount )
local src = source
local Player = QBCore.Functions . GetPlayer ( src )
if not Player then return end
local machineId = getMachineIdByCoords ( coords )
if not machineId then return end
local machine = vendingMachines [ machineId ]
if machine.owner ~= Player.PlayerData . citizenid then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Das ist nicht dein Verkaufsautomat! ' , ' error ' )
return
end
-- Check if machine has the item
if not machine.items [ itemName ] or machine.items [ itemName ] < amount then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Nicht genug Items im Automaten! ' , ' error ' )
return
end
-- Remove item from machine
machine.items [ itemName ] = machine.items [ itemName ] - amount
-- Add item to player
Player.Functions . AddItem ( itemName , amount )
TriggerClientEvent ( ' inventory:client:ItemBox ' , src , QBCore.Shared . Items [ itemName ] , ' add ' )
-- Update database
MySQL.update ( ' UPDATE vending_machines SET items = ? WHERE id = ? ' , { json.encode ( machine.items ) , machineId } )
TriggerClientEvent ( ' QBCore:Notify ' , src , amount .. ' x ' .. ( QBCore.Shared . Items [ itemName ] and QBCore.Shared . Items [ itemName ] . label or itemName ) .. ' aus dem Automaten entfernt! ' , ' success ' )
end )
2025-07-29 08:25:12 +02:00
-- Start robbery
RegisterNetEvent ( ' vending:server:startRobbery ' , function ( coords )
local src = source
local Player = QBCore.Functions . GetPlayer ( src )
if not Player then return end
local machineId = getMachineIdByCoords ( coords )
if not machineId then return end
local machine = vendingMachines [ machineId ]
2025-07-29 08:39:45 +02:00
-- Check if player has required item
local hasItem = Player.Functions . GetItemByName ( Config.RobberyItem )
2025-07-29 08:35:03 +02:00
if not hasItem or hasItem.amount < 1 then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Du benötigst einen ' .. Config.RobberyItem , ' error ' )
return
end
-- Check if already being robbed
if robberyInProgress [ machineId ] then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Dieser Automat wird bereits aufgebrochen! ' , ' error ' )
return
end
-- Check if machine has money
if machine.money < Config.MinRobberyAmount then
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Nicht genug Geld im Automaten! ' , ' error ' )
return
end
robberyInProgress [ machineId ] = true
-- Alert police
local streetHash = GetStreetNameAtCoord ( coords.x , coords.y , coords.z )
local streetName = GetStreetNameFromHashKey ( streetHash )
local players = QBCore.Functions . GetQBPlayers ( )
for k , v in pairs ( players ) do
if v.PlayerData . job.name == ' police ' and v.PlayerData . job.onduty then
TriggerClientEvent ( ' vending:client:policeAlert ' , v.PlayerData . source , coords , streetName )
2025-07-29 08:30:01 +02:00
end
2025-07-29 08:35:03 +02:00
end
TriggerClientEvent ( ' vending:client:startRobbery ' , src , coords )
2025-07-29 08:25:12 +02:00
end )
-- Complete robbery
RegisterNetEvent ( ' vending:server:completeRobbery ' , function ( coords , success )
local src = source
local Player = QBCore.Functions . GetPlayer ( src )
if not Player then return end
local machineId = getMachineIdByCoords ( coords )
if not machineId then return end
local machine = vendingMachines [ machineId ]
robberyInProgress [ machineId ] = false
if success then
local stolenAmount = math.random ( Config.MinRobberyAmount , math.min ( machine.money , Config.MaxRobberyAmount ) )
-- Remove money from machine
machine.money = machine.money - stolenAmount
MySQL.update ( ' UPDATE vending_machines SET money = ? WHERE id = ? ' , { machine.money , machineId } )
-- Give money to player
Player.Functions . AddMoney ( ' cash ' , stolenAmount )
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Du hast $ ' .. stolenAmount .. ' gestohlen! ' , ' success ' )
-- Remove robbery item with chance
if math.random ( 1 , 100 ) <= Config.RobberyItemBreakChance then
2025-07-29 08:39:45 +02:00
Player.Functions . RemoveItem ( Config.RobberyItem , 1 )
TriggerClientEvent ( ' inventory:client:ItemBox ' , src , QBCore.Shared . Items [ Config.RobberyItem ] , ' remove ' )
2025-07-29 08:25:12 +02:00
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Dein ' .. Config.RobberyItem .. ' ist kaputt gegangen! ' , ' error ' )
end
else
TriggerClientEvent ( ' QBCore:Notify ' , src , ' Aufbruch fehlgeschlagen! ' , ' error ' )
end
end )
2025-07-29 07:30:32 +02:00
-- Helper function to get machine ID by coordinates
function getMachineIdByCoords ( coords )
for id , machine in pairs ( vendingMachines ) do
local dist = # ( vector3 ( coords.x , coords.y , coords.z ) - vector3 ( machine.coords . x , machine.coords . y , machine.coords . z ) )
if dist < 2.0 then
return id
end
end
return nil
end
-- Get machine data by coordinates
QBCore.Functions . CreateCallback ( ' vending:server:getMachineByCoords ' , function ( source , cb , coords )
local machineId = getMachineIdByCoords ( coords )
if machineId then
cb ( vendingMachines [ machineId ] )
else
cb ( nil )
end
end )
2025-07-29 08:39:45 +02:00
-- Get stash items for vending machine menu (using our database)
2025-07-29 07:30:32 +02:00
QBCore.Functions . CreateCallback ( ' vending:server:getStashItems ' , function ( source , cb , coords )
local machineId = getMachineIdByCoords ( coords )
if not machineId then
cb ( { } )
return
end
local machine = vendingMachines [ machineId ]
2025-07-29 08:35:03 +02:00
local items = { }
2025-07-29 08:39:45 +02:00
for itemName , amount in pairs ( machine.items ) do
if amount > 0 then
local itemData = QBCore.Shared . Items [ itemName ]
if itemData then
table.insert ( items , {
name = itemName ,
label = itemData.label ,
amount = amount ,
price = machine.prices [ itemName ] or Config.DefaultPrice ,
image = itemData.image
} )
2025-07-29 08:25:12 +02:00
end
end
2025-07-29 08:35:03 +02:00
end
cb ( items )
2025-07-29 07:30:32 +02:00
end )
-- Check if player owns machine
QBCore.Functions . CreateCallback ( ' vending:server:isOwner ' , function ( source , cb , coords )
local Player = QBCore.Functions . GetPlayer ( source )
if not Player then
cb ( false )
return
end
local machineId = getMachineIdByCoords ( coords )
if not machineId then
cb ( false )
return
end
local machine = vendingMachines [ machineId ]
cb ( machine.owner == Player.PlayerData . citizenid )
end )
-- Check if machine exists at coords
QBCore.Functions . CreateCallback ( ' vending:server:machineExists ' , function ( source , cb , coords )
local machineId = getMachineIdByCoords ( coords )
cb ( machineId ~= nil )
end )