I’m trying to mod esx_shops with esx_hotdog to create a prop based shop (food carts) script. I was able to convert esx_atm to be prop based fairly easily but having trouble with this. The use of zones and database call backs have proven a bit too complex for me. Any feedback or help would be greatly appreciated. This is what I have so far.
CONFIG
Config = {}
Config.DrawDistance = 25
Config.Size = {x = 1.5, y = 1.5, z = 1.5}
Config.Color = {r = 0, g = 128, b = 255}
Config.Type = 1
Config.Locale = 'en'
Config.Zones = {
CheapShotCoffeeCarts = {
Items = {},
Prop = {"p_ld_coffee_vend_01"}
}
}
CLIENT
local Keys = {
["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57,
["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177,
["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18,
["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182,
["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81,
["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70,
["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178,
["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173,
["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118
}
ESX = nil
local HasAlreadyEnteredMarker = false
local LastZone = nil
local CurrentAction = nil
local CurrentActionMsg = ''
local CurrentActionData = {}
local cam = -1
local CurrentStand = nil
Citizen.CreateThread(function()
while ESX == nil do
TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end)
Citizen.Wait(0)
end
Citizen.Wait(5000)
ESX.TriggerServerCallback('tresh_foodstands:requestDBItems', function(ShopItems)
for k,v in pairs(ShopItems) do
Config.Zones[k].Items = v
end
end)
end)
function OpenStandMenu(zone)
local elements = {}
for i=1, #Config.Zones[zone].Items, 1 do
local item = Config.Zones[zone].Items[i]
if item.limit == -1 then
item.limit = 1
end
table.insert(elements, {
label = ('%s - <span style="color:green;">%s</span>'):format(item.label, _U('stand_item', ESX.Math.GroupDigits(item.price))),
label_real = item.label,
item = item.item,
price = item.price,
-- menu properties
value = 1,
type = 'slider',
min = 1,
max = item.limit
})
end
ESX.UI.Menu.CloseAll()
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'stand', {
title = _U('stand'),
align = 'bottom-right',
elements = elements
}, function(data, menu)
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'stand_confirm', {
title = _U('stand_confirm', data.current.value, data.current.label_real, ESX.Math.GroupDigits(data.current.price * data.current.value)),
align = 'bottom-right',
elements = {
{label = _U('no'), value = 'no'},
{label = _U('yes'), value = 'yes'}
}
}, function(data2, menu2)
if data2.current.value == 'yes' then
TriggerServerEvent('tresh_foodstands:buyItem', data.current.item, data.current.value, zone)
end
menu2.close()
menu.close()
end, function(data2, menu2)
menu2.close()
end)
end, function(data, menu)
menu.close()
CurrentAction = 'stand_menu'
CurrentActionMsg = _U('press_menu')
CurrentActionData = {zone = zone}
end)
end
AddEventHandler('tresh_foodstands:hasEnteredMarker', function(zone)
CurrentAction = 'stand_menu'
CurrentActionMsg = _U('press_menu')
CurrentActionData = {zone = zone}
end)
AddEventHandler('tresh_foodstands:hasExitedMarker', function(zone)
CurrentAction = nil
ESX.UI.Menu.CloseAll()
end)
-- Enter / Exit marker events
Citizen.CreateThread(function()
while true do
Citizen.Wait(0)
local PlayerCoords = GetEntityCoords(PlayerPedId())
local isInMarker = false
local currentZone = nil
for k,v in pairs(Config.Zones) do
local ClosestStand = GetClosestObjectOfType(PlayerCoords, 0.7, GetHashKey(v.Prop), false, false)
if ClosestStand ~= 0 and ClosestStand ~= nil then
CurrentStand = ClosestStand
isInMarker = true
ShopItems = v.Items
currentZone = k
LastZone = k
break
else
CurrentStand = nil
end
end
if isInMarker and not HasAlreadyEnteredMarker then
HasAlreadyEnteredMarker = true
TriggerEvent('tresh_foodstands:hasEnteredMarker', currentZone)
end
if not isInMarker and HasAlreadyEnteredMarker then
HasAlreadyEnteredMarker = false
TriggerEvent('tresh_foodstands:hasExitedMarker', LastZone)
end
end
end)
--Key Controls--
Citizen.CreateThread(function()
while true do
Citizen.Wait(5)
if CurrentStand ~= nil and IsControlJustReleased(0, 38) then
if CurrentAction == 'stand_menu' then
OpenStandMenu(CurrentActionData.zone)
end
CurrentAction = nil
end
end
end)
--Items--
RegisterNetEvent('tresh_coffee_stand:onDrink')
AddEventHandler('tresh_coffee_stand:onDrink', function(prop_name)
if not IsAnimated then
prop_name = prop_name or 'p_ing_coffeecup_01'
IsAnimated = true
Citizen.CreateThread(function()
local playerPed = PlayerPedId()
local x,y,z = table.unpack(GetEntityCoords(playerPed))
local prop = CreateObject(GetHashKey(prop_name), x, y, z + 0.2, true, true, true)
local boneIndex = GetPedBoneIndex(playerPed, 18905)
AttachEntityToEntity(prop, playerPed, boneIndex, 0.12, 0.028, 0.001, 10.0, 175.0, 0.0, true, true, false, true, 1, true)
ESX.Streaming.RequestAnimDict('mp_player_intdrink', function()
TaskPlayAnim(playerPed, 'mp_player_intdrink', 'loop_bottle', 1.0, -1.0, 2000, 0, 1, true, true, true)
Citizen.Wait(3000)
IsAnimated = false
ClearPedSecondaryTask(playerPed)
DeleteObject(prop)
end)
end)
end
end)
SERVER
ESX = nil
local ShopItems = {}
TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end)
MySQL.ready(function()
MySQL.Async.fetchAll('SELECT * FROM food_stands LEFT JOIN items ON items.name = food_stands.item', {}, function(shopResult)
for i=1, #shopResult, 1 do
if shopResult[i].name then
if ShopItems[shopResult[i].stand] == nil then
ShopItems[shopResult[i].stand] = {}
end
if shopResult[i].limit == -1 then
shopResult[i].limit = 1
end
table.insert(ShopItems[shopResult[i].stand], {
label = shopResult[i].label,
item = shopResult[i].item,
price = shopResult[i].price,
limit = shopResult[i].limit
})
else
print(('tresh_foodstands: invalid item "%s" found!'):format(shopResult[i].item))
end
end
end)
end)
ESX.RegisterServerCallback('tresh_foodstands:requestDBItems', function(source, cb)
cb(ShopItems)
end)
RegisterServerEvent('tresh_foodstands:buyItem')
AddEventHandler('tresh_foodstands:buyItem', function(itemName, amount, zone)
local _source = source
local xPlayer = ESX.GetPlayerFromId(_source)
local sourceItem = xPlayer.getInventoryItem(itemName)
amount = ESX.Math.Round(amount)
-- is the player trying to exploit?
if amount < 0 then
print('tresh_foodstands: ' .. xPlayer.identifier .. ' attempted to exploit the stand!')
return
end
-- get price
local price = 0
local itemLabel = ''
for i=1, #ShopItems[zone], 1 do
if ShopItems[zone][i].item == itemName then
price = ShopItems[zone][i].price
itemLabel = ShopItems[zone][i].label
break
end
end
price = price * amount
-- can the player afford this item?
if xPlayer.getMoney() >= price then
-- can the player carry the said amount of x item?
if sourceItem.limit ~= -1 and (sourceItem.count + amount) > sourceItem.limit then
TriggerClientEvent('esx:showNotification', _source, _U('player_cannot_hold'))
else
xPlayer.removeMoney(price)
xPlayer.addInventoryItem(itemName, amount)
TriggerClientEvent('esx:showNotification', _source, _U('bought', amount, itemLabel, ESX.Math.GroupDigits(price)))
end
else
local missingMoney = price - xPlayer.getMoney()
TriggerClientEvent('esx:showNotification', _source, _U('not_enough', ESX.Math.GroupDigits(missingMoney)))
end
end)
--items--
ESX.RegisterUsableItem('cafeaulait', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('cafeaulait', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_cafeaulait'))
end)
ESX.RegisterUsableItem('latte', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('latte', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_latte'))
end)
ESX.RegisterUsableItem('cappucino', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('cappucino', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_cappucino'))
end)
ESX.RegisterUsableItem('espresso', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('espresso', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_espresso'))
end)
ESX.RegisterUsableItem('americano', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('americano', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_americano'))
end)
ESX.RegisterUsableItem('chocamocha', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('chocamocha', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_chocamocha'))
end)
ESX.RegisterUsableItem('macchiato', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('macchiato', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_macchiato'))
end)
ESX.RegisterUsableItem('cocoa', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('cocoa', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_cocoa'))
end)
ESX.RegisterUsableItem('hottea', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('hottea', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_hottea'))
end)
ESX.RegisterUsableItem('chaitea', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('chaitea', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_chaitea'))
end)
ESX.RegisterUsableItem('fruitjuice', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('fruitjuice', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_fruitjuice'))
end)
ESX.RegisterUsableItem('milkshake', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('milkshake', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_milkshake'))
end)
ESX.RegisterUsableItem('smoothie', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('smoothie', 1)
TriggerClientEvent('esx_status:add', source, 'thirst', 300000)
TriggerClientEvent('tresh_coffee_stand:onDrink', source)
TriggerClientEvent('esx:showNotification', source, _U('use_smoothie'))
end)```