diff --git a/__resource.lua b/__resource.lua index 76528017..ebabdf34 100644 --- a/__resource.lua +++ b/__resource.lua @@ -2,7 +2,7 @@ resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937' description 'ESX Shops' -version '1.1.0' +version '1.2.0' client_scripts { '@es_extended/locale.lua', diff --git a/client/main.lua b/client/main.lua index e9392bd1..e9c5bfd2 100644 --- a/client/main.lua +++ b/client/main.lua @@ -4,15 +4,17 @@ local LastZone = nil local CurrentAction = nil local CurrentActionMsg = '' local CurrentActionData = {} +local PlayerData = {} Citizen.CreateThread(function() while ESX == nil do TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) Citizen.Wait(0) end - + Citizen.Wait(5000) - + PlayerData = ESX.GetPlayerData() + ESX.TriggerServerCallback('esx_shops:requestDBItems', function(ShopItems) for k,v in pairs(ShopItems) do Config.Zones[k].Items = v @@ -20,38 +22,55 @@ Citizen.CreateThread(function() end) end) - function OpenShopMenu(zone) + PlayerData = ESX.GetPlayerData() + local elements = {} for i=1, #Config.Zones[zone].Items, 1 do - local item = Config.Zones[zone].Items[i] + table.insert(elements, { - label = item.label .. ' - $' .. item.price .. '', - realLabel = item.label, - value = item.name, - price = item.price + label = item.label .. ' - $' .. 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(), 'shop', - { - title = _U('shop'), + ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'shop', { + title = _U('shop'), + align = 'bottom-right', + elements = elements + }, function(data, menu) + ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'shop_confirm', { + title = _U('shop_confirm', data.current.value, data.current.label_real, data.current.price * data.current.value), align = 'bottom-right', - elements = elements - }, - function(data, menu) - TriggerServerEvent('esx_shops:buyItem', data.current.value, data.current.price) - end, - function(data, menu) - menu.close() - CurrentAction = 'shop_menu' - CurrentActionMsg = _U('press_menu') - CurrentActionData = {zone = zone} - end - ) + elements = { + {label = _U('no'), value = 'no'}, + {label = _U('yes'), value = 'yes'} + } + }, function(data2, menu2) + if data2.current.value == 'yes' then + TriggerServerEvent('esx_shops:buyItem', data.current.item, data.current.value, zone) + end + + menu2.close() + end, function(data2, menu2) + menu2.close() + end) + end, function(data, menu) + menu.close() + CurrentAction = 'shop_menu' + CurrentActionMsg = _U('press_menu') + CurrentActionData = {zone = zone} + end) end AddEventHandler('esx_shops:hasEnteredMarker', function(zone) diff --git a/esx_shops.sql b/esx_shops.sql index b9f72161..0952742d 100644 --- a/esx_shops.sql +++ b/esx_shops.sql @@ -1,16 +1,15 @@ USE `essentialmode`; CREATE TABLE `shops` ( - - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(255) NOT NULL, - `item` varchar(255) NOT NULL, - `price` int(11) NOT NULL, - - PRIMARY KEY (`id`) + `id` int(11) NOT NULL AUTO_INCREMENT, + `store` varchar(100) NOT NULL, + `item` varchar(100) NOT NULL, + `price` int(11) NOT NULL, + + PRIMARY KEY (`id`) ); -INSERT INTO `shops` (name, item, price) VALUES +INSERT INTO `shops` (store, item, price) VALUES ('TwentyFourSeven','bread',30), ('TwentyFourSeven','water',15), ('RobsLiquor','bread',30), diff --git a/locales/br.lua b/locales/br.lua index 2d529545..03d5486a 100644 --- a/locales/br.lua +++ b/locales/br.lua @@ -1,8 +1,11 @@ Locales['br'] = { - ['shop'] = 'Comprar', - ['shops'] = 'Lojas', - ['press_menu'] = 'Pressione ~INPUT_CONTEXT~ para acessar a loja.', - ['bought'] = 'você comprou ~b~1x ', - ['not_enough'] = 'você não tem ~r~dinheiro suficiente~s~.', - ['player_cannot_hold'] = 'Você ~r~não~s~ tem bastante ~y~espaço livre~s~ no seu inventário!', + ['shop'] = 'comprar', + ['shops'] = 'lojas', + ['press_menu'] = 'pressione ~INPUT_CONTEXT~ para acessar a loja.', + ['bought'] = 'you just bought ~y~%sx~s~ ~b~%s~s~ for ~r~$%s~s~', + ['not_enough'] = 'você não tem ~r~dinheiro suficiente~s~: %s', + ['player_cannot_hold'] = 'você ~r~não~s~ tem bastante ~y~espaço livre~s~ no seu inventário!', + ['shop_confirm'] = 'buy %sx %s for $%s?', + ['no'] = 'no', + ['yes'] = 'yes', } diff --git a/locales/de.lua b/locales/de.lua index c34fd81c..beba2424 100644 --- a/locales/de.lua +++ b/locales/de.lua @@ -1,9 +1,11 @@ Locales['de'] = { - - ['shop'] = 'Geschäft', - ['shops'] = 'Geschäfte', - ['press_menu'] = 'Drücke ~INPUT_CONTEXT~ um auf das Geschäft zuzugreifen.', - ['bought'] = 'du kaufst ~b~1x ', - ['not_enough'] = 'du ~r~hast nicht~s~ genug Geld.' - + ['shop'] = 'geschäft', + ['shops'] = 'geschäfte', + ['press_menu'] = 'drücke ~INPUT_CONTEXT~ um auf das Geschäft zuzugreifen.', + ['bought'] = 'you just bought ~y~%sx~s~ ~b~%s~s~ for ~r~$%s~s~', + ['not_enough'] = 'du ~r~hast nicht~s~ genug geld: %s', + ['player_cannot_hold'] = 'you do ~r~not~s~ have enough ~y~free space~s~ in your inventory!', + ['shop_confirm'] = 'buy %sx %s for $%s?', + ['no'] = 'no', + ['yes'] = 'yes', } diff --git a/locales/en.lua b/locales/en.lua index f88753a4..1b02063a 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -2,7 +2,10 @@ Locales['en'] = { ['shop'] = 'shop', ['shops'] = 'shops', ['press_menu'] = 'press ~INPUT_CONTEXT~ to access the ~y~store~s~.', - ['bought'] = 'you just bought ~y~1x~s~ ~b~%s~s~ for ~r~$%s~s~', + ['bought'] = 'you just bought ~y~%sx~s~ ~b~%s~s~ for ~r~$%s~s~', ['not_enough'] = 'you do not have ~r~enough~s~ money, you\'re ~y~missing~s~ ~r~$%s~s~!', ['player_cannot_hold'] = 'you do ~r~not~s~ have enough ~y~free space~s~ in your inventory!', + ['shop_confirm'] = 'buy %sx %s for $%s?', + ['no'] = 'no', + ['yes'] = 'yes', } \ No newline at end of file diff --git a/locales/es.lua b/locales/es.lua index d25088fd..cbabfc9a 100644 --- a/locales/es.lua +++ b/locales/es.lua @@ -1,9 +1,11 @@ Locales['es'] = { - - ['shop'] = 'Tienda', - ['shops'] = 'Tiendas', - ['press_menu'] = 'Presiona ~INPUT_CONTEXT~ para acceder a la tienda.', - ['bought'] = 'Has comprado ~b~1 ', - ['not_enough'] = 'No tienes ~r~suficiente ~s~ dinero.', - + ['shop'] = 'tienda', + ['shops'] = 'tiendas', + ['press_menu'] = 'presiona ~INPUT_CONTEXT~ para acceder a la tienda.', + ['bought'] = 'you just bought ~y~%sx~s~ ~b~%s~s~ for ~r~$%s~s~', + ['not_enough'] = 'no tienes ~r~suficiente ~s~ dinero: %s', + ['player_cannot_hold'] = 'you do ~r~not~s~ have enough ~y~free space~s~ in your inventory!', + ['shop_confirm'] = 'buy %sx %s for $%s?', + ['no'] = 'no', + ['yes'] = 'yes', } diff --git a/locales/fr.lua b/locales/fr.lua index 14e9ca67..96513258 100644 --- a/locales/fr.lua +++ b/locales/fr.lua @@ -1,9 +1,11 @@ Locales['fr'] = { - ['shop'] = 'magasin', ['shops'] = 'magasins', ['press_menu'] = 'appuyez sur ~INPUT_CONTEXT~ pour accéder au magasin.', - ['bought'] = 'vous avez acheté ~b~1x ', - ['not_enough'] = 'vous n\'avez ~r~pas assez~s~ d\'argent.', - + ['bought'] = 'you just bought ~y~%sx~s~ ~b~%s~s~ for ~r~$%s~s~', + ['not_enough'] = 'vous n\'avez ~r~pas assez~s~ d\'argent: %s', + ['player_cannot_hold'] = 'you do ~r~not~s~ have enough ~y~free space~s~ in your inventory!', + ['shop_confirm'] = 'buy %sx %s for $%s?', + ['no'] = 'no', + ['yes'] = 'yes', } diff --git a/locales/sv.lua b/locales/sv.lua index 5dfbf64b..294fb21e 100644 --- a/locales/sv.lua +++ b/locales/sv.lua @@ -1,8 +1,11 @@ Locales['sv'] = { ['shop'] = 'affär', ['shops'] = 'affärer', - ['press_menu'] = 'tryck ~INPUT_CONTEXT~ för att ~y~handla prylar~w~.', - ['bought'] = 'du har köpt ~y~1x~w~ ~b~%s~w~ för ~r~$%s~w~', - ['not_enough'] = 'du har inte ~r~tillräckligt~s~ med pengar, det ~y~saknas~w~ ~r~$%s~w~!', - ['player_cannot_hold'] = 'du har ~r~inte~w~ tillräckligt med ~y~utrymme~w~ för att hålla det!', + ['press_menu'] = 'tryck ~INPUT_CONTEXT~ för att ~y~handla prylar~s~.', + ['bought'] = 'du har köpt ~y~%sx~s~ ~b~%s~s~ för ~r~$%s~s~', + ['not_enough'] = 'du har inte ~r~tillräckligt~s~ med pengar, det ~y~saknas~s~ ~r~$%s~s~!', + ['player_cannot_hold'] = 'du har ~r~inte~s~ tillräckligt med ~y~utrymme~s~ för att hålla det!', + ['shop_confirm'] = 'köpa %sx %s för $%s?', + ['no'] = 'nej', + ['yes'] = 'ja', } \ No newline at end of file diff --git a/server/main.lua b/server/main.lua index 310a7b1c..f7099870 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,70 +1,97 @@ -ESX = nil -local ItemsLabels = {} +ESX = nil +local ShopItems = {} +local hasSqlRun = false TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) --- Load item labels -Citizen.CreateThread(function() - Citizen.Wait(5000) - - MySQL.Async.fetchAll( - 'SELECT * FROM items', - {}, - function(result) - for i=1, #result, 1 do - ItemsLabels[result[i].name] = result[i].label - end - end) +-- Load items +AddEventHandler('onMySQLReady', function() + hasSqlRun = true + LoadShop() end) -ESX.RegisterServerCallback('esx_shops:requestDBItems', function(source, cb) +-- extremely useful when restarting script mid-game +Citizen.CreateThread(function() + Citizen.Wait(20000) -- hopefully enough for connection to the SQL server - MySQL.Async.fetchAll( - 'SELECT * FROM shops', - {}, - function(result) - local shopItems = {} - for i=1, #result, 1 do - if shopItems[result[i].name] == nil then - shopItems[result[i].name] = {} - end - - table.insert(shopItems[result[i].name], { - name = result[i].item, - price = result[i].price, - label = ItemsLabels[result[i].item] - }) + if not hasSqlRun then + LoadShop() + hasSqlRun = true + end +end) + +function LoadShop() + local itemResult = MySQL.Sync.fetchAll('SELECT * FROM items') + local shopResult = MySQL.Sync.fetchAll('SELECT * FROM shops') + + local itemInformation = {} + for i=1, #itemResult, 1 do + if itemInformation[itemResult[i].name] == nil then + itemInformation[itemResult[i].name] = {} end - cb(shopItems) - end) + + itemInformation[itemResult[i].name].label = itemResult[i].label + itemInformation[itemResult[i].name].limit = itemResult[i].limit + end + + for i=1, #shopResult, 1 do + if ShopItems[shopResult[i].store] == nil then + ShopItems[shopResult[i].store] = {} + end + + table.insert(ShopItems[shopResult[i].store], { + label = itemInformation[shopResult[i].item].label, + item = shopResult[i].item, + price = shopResult[i].price, + limit = itemInformation[shopResult[i].item].limit + }) + end +end + +ESX.RegisterServerCallback('esx_shops:requestDBItems', function(source, cb) + if not hasSqlRun then + TriggerClientEvent('esx:showNotification', source, 'The shop database has not been loaded yet, try again in a few moments.') + end + + cb(ShopItems) end) RegisterServerEvent('esx_shops:buyItem') -AddEventHandler('esx_shops:buyItem', function(itemName, price) +AddEventHandler('esx_shops:buyItem', function(itemName, amount, zone) local _source = source local xPlayer = ESX.GetPlayerFromId(_source) local sourceItem = xPlayer.getInventoryItem(itemName) - - -- is the player trying to cheat? - if price < 0 then - print('esx_shops: ' .. xPlayer.identifier .. ' attempted to cheat money!') + + -- is the player trying to exploit? + if amount < 0 then + print('esx_shops: ' .. xPlayer.identifier .. ' attempted to exploit the shop!') 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 + 1) > sourceItem.limit then + 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, 1) - TriggerClientEvent('esx:showNotification', _source, _U('bought', ItemsLabels[itemName], price)) + xPlayer.addInventoryItem(itemName, amount) + TriggerClientEvent('esx:showNotification', _source, _U('bought', amount, itemLabel, price)) end else local missingMoney = price - xPlayer.getMoney() TriggerClientEvent('esx:showNotification', _source, _U('not_enough', missingMoney)) end - end) \ No newline at end of file