From d3ca673dfb32930ec1a7446c17207fbac2c0376c Mon Sep 17 00:00:00 2001 From: Muhaddil <151466679+Muhaddil@users.noreply.github.com> Date: Mon, 23 Dec 2024 01:48:44 +0100 Subject: [PATCH] Update README and enhance configuration for vending machine script Fixed some fuctions for QB and several aditions. --- README.md | 58 ++++++++++++++++++++++++++++-- client.lua | 27 +++++++------- config.lua | 91 +++++++++++++++++++++++++++++++++++++++-------- server/server.lua | 61 ++++++++++++++++++++++++------- 4 files changed, 196 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 7291372..33d32d0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,56 @@ -# muhaddil_machines - A FiveM script that adds several machines to improve user experience. +# muhaddil_machines (FiveM) + +A FiveM script that adds several machines to improve user experience. + +## Features + +- Vending machines for drinks and snacks +- Water coolers with a timeout feature +- Food stands with various items +- News sellers with newspapers +- Custom animations for interactions +- Configurable framework support (ESX and QBCore) +- Debug mode for development +- Auto version checker + +## Installation + +1. Clone or download the repository. +2. Add the resource to your `resources` folder. +3. Add `start muhaddil_machines` to your `server.cfg`. + +## Configuration + +You can configure the script by editing the [config.lua](config.lua) file. Here are some of the options available: + +- `DebugMode`: Enable or disable debug mode. +- `Framework`: Choose between 'esx' or 'qb'. +- `UseOXNotifications`: Use OX notifications or frameworks. +- `ThirstRemoval`: Amount of thirst removed by water coolers. +- `WaterCoolerTimeout`: Timeout duration for water coolers. +- `VisibleProp`: Show or hide props during animations. +- `ShowWaitNotification`: Show notification when water cooler is on timeout. +- `MaxDrinksBeforeKill`: Maximum drinks before player death. +- `CountDrinksPlace`: Count drinks before or after drinking. + +## Usage + +### Vending Machines + +Interact with vending machines to buy drinks and snacks. The available items and their prices are configured in the [config.lua](config.lua) file. + +### Water Coolers + +Interact with water coolers to drink water. The script includes a timeout feature to prevent excessive use. + +### Food Stands + +Interact with food stands to buy various food items. The available items and their prices are configured in the [config.lua](config.lua) file. + +### News Sellers + +Interact with news sellers to buy newspapers. The available items and their prices are configured in the [config.lua](config.lua) file. + +## License + +This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. \ No newline at end of file diff --git a/client.lua b/client.lua index 1b44420..d35caf1 100644 --- a/client.lua +++ b/client.lua @@ -1,7 +1,7 @@ -local buying = false -local LastWaterCoolerUse = 0 -local TimeoutDuration = Config.WaterCoolerTimeout * 1000 -local DrinkCount = 0 +local buying = false -- Variable to prevent multiple purchases +local LastWaterCoolerUse = 0 -- Variable to prevent multiple uses of the water cooler +local TimeoutDuration = Config.WaterCoolerTimeout * 1000 -- Timeout duration for water coolers in milliseconds +local DrinkCount = 0 -- Variable to count the number of drinks if Config.Framework == "esx" then ESX = exports['es_extended']:getSharedObject() @@ -11,13 +11,13 @@ else ESX = exports['es_extended']:getSharedObject() end -function DebugPrint(...) +function DebugPrint(...) -- Debug print function if Config.DebugMode then print(...) end end -function Notify(msgtitle, msg, time, type2) +function Notify(msgtitle, msg, time, type2) -- Notification function if Config.UseOXNotifications then lib.notify({ title = msgtitle, @@ -277,7 +277,6 @@ local function standAnimation(entity) buying = false end - local function showQuantityDialogStands(item, standName, entity) local input = lib.inputDialog("Selecciona la cantidad", { { type = 'number', label = 'Cantidad', min = 1, max = Config.InputMaxValue, default = 1 } @@ -325,28 +324,32 @@ local function standMenu(standName, entity, items) lib.showContext('stand_menu_' .. standName) end -local function standAnimation(entity) +local function newsAnimation(entity) local ped = PlayerPedId() local position = GetOffsetFromEntityInWorldCoords(entity, 0.0, -0.97, 0.05) local heading = GetEntityHeading(entity) buying = true TaskTurnPedToFaceEntity(ped, entity, -1) - if not IsEntityAtCoord(ped, position.x, position.y, position.z, 0.1, 0.0, 0.1, false, true, 0) then + if not IsEntityAtCoord(ped, position.x, position.y, position.z, 0.0, 0.0, 0.0, false, true, 0) then TaskGoStraightToCoord(ped, position.x, position.y, position.z, 1.0, 20000, heading, 0.1) Citizen.Wait(1000) end TaskTurnPedToFaceEntity(ped, entity, -1) Citizen.Wait(1000) - playAnimation(ped, Config.Animations.stand[1], Config.Animations.stand[2]) - Citizen.Wait(1000) + loadAnimDict(Config.Animations.newsSellers[1]) + TaskPlayAnim(ped, Config.Animations.newsSellers[1], Config.Animations.newsSellers[2], 8.0, 5.0, -1, 1, 1, false, false, false) + Citizen.Wait(2500) + ClearPedTasks(ped) + RemoveAnimDict(Config.Animations.newsSellers[1]) ClearPedSecondaryTask(ped) buying = false end + local function showQuantityDialogNews(item, newsName, entity) local input = lib.inputDialog("Selecciona la cantidad", { { type = 'number', label = 'Cantidad', min = 1, max = Config.InputMaxValue, default = 1 } @@ -360,7 +363,7 @@ local function showQuantityDialogNews(item, newsName, entity) if buying then return end buying = true - standAnimation(entity) + newsAnimation(entity) TriggerServerEvent('muhaddil-machines:buy', 'news', newsName, item.name, cantidad) else diff --git a/config.lua b/config.lua index af4ea25..9f86e13 100644 --- a/config.lua +++ b/config.lua @@ -1,30 +1,36 @@ Config = Config or {} -Config.DebugMode = true -Config.Framework = 'esx' -Config.UseOXNotifications = true +Config.DebugMode = true -- Enable debug mode +Config.Framework = 'esx' -- 'esx' or 'qb' +Config.UseOXNotifications = true -- Use OX Notifications or framework notifications +Config.Inventory = 'ox'-- 'qs', 'ox' or leave blank +Config.NewQBInventory = false -- If you're using the new QB Inventory -Config.ThirstRemoval = 150000 -- (WaterCooler) -Config.WaterCoolerTimeout = 30 -Config.VisibleProp = false -Config.InputMaxValue = 10 -- +Config.ThirstRemoval = 150000 -- Amount of thirst removed by water coolers +Config.WaterCoolerTimeout = 30 -- Timeout duration for water coolers in seconds +Config.VisibleProp = false -- Show the prop when buying a drink +Config.InputMaxValue = 10 -- Maximum value for the input Config.KillPlayerOnExcess = true -- Enable one of the two (WaterCooler) Config.ShowWaitNotification = false -- Enable one of the two (WaterCooler) Config.MaxDrinksBeforeKill = 3 -- (WaterCooler) Config.CountDrinksPlace = 'before' -- 'before' or 'after', it varies in the result of the Config.MaxDrinksBeforeKill (WaterCooler) -Config.Animations = { - stand = { +Config.Animations = { -- Animations for the vending machines + stand = { -- Stand animations "special_ped@baygor@monologue_2@monologue_2h", "you_can_ignore_me_7" }, - sodamachines = { + sodamachines = { -- Soda machine animations "mini@sprunk@first_person", "plyr_buy_drink_pt1" }, + newsSellers = { -- News seller animations + "anim@amb@nightclub@mini@drinking@drinking_shots@ped_c@normal", + "pickup" + }, } -Config.machines = { +Config.machines = { -- Vending machines { model = 'prop_vend_soda_02', items = { @@ -138,7 +144,6 @@ Config.machines = { price = 40 } }, - offset = vec3(0, 0, 0) }, { @@ -166,12 +171,12 @@ Config.machines = { } } -Config.WaterCoolers = { +Config.WaterCoolers = { -- Water coolers {model = 'prop_watercooler_dark',}, {model = 'prop_watercooler',}, } -Config.Stands = { +Config.Stands = { -- Stands { model = 'prop_hotdogstand_01', items = { @@ -218,9 +223,43 @@ Config.Stands = { }, }, }, + { + model = 'prop_fruitstand_b', + items = { + { + name = "apple", + label = 'Manzana ($%price%)', + icon = 'fa fa-apple', + price = 10 + }, + { + name = "banana", + label = 'Banana ($%price%)', + icon = 'fa fa-banana', + price = 12 + }, + }, + }, + { + model = 'prop_tool_bench02', + items = { + { + name = "repairkit", + label = 'Kit de Reparación ($%price%)', + icon = 'fa fa-wrench', + price = 1000 + }, + { + name = "bodykit", + label = 'Body Kit ($%price%)', + icon = 'fa fa-screwdriver', + price = 250 + }, + }, + }, } -Config.NewsSellers = { +Config.NewsSellers = { -- News sellers { model = 'prop_news_disp_06a', items = { @@ -232,4 +271,26 @@ Config.NewsSellers = { }, }, }, + { + model = 'prop_news_disp_01a', + items = { + { + name = "newspaper", + label = 'Periódico ($%price%)', + icon = 'fa fa-newspaper', + price = 30 + }, + }, + }, + { + model = 'prop_news_disp_03a', + items = { + { + name = "newspaper", + label = 'Periódico ($%price%)', + icon = 'fa fa-newspaper', + price = 30 + }, + }, + }, } diff --git a/server/server.lua b/server/server.lua index f0be608..4915f05 100644 --- a/server/server.lua +++ b/server/server.lua @@ -6,7 +6,7 @@ else ESX = exports['es_extended']:getSharedObject() end -local function getPlayerObject(src) +local function getPlayerObject(src) -- Get the player object if Config.Framework == 'qb' then return QBCore.Functions.GetPlayer(src) elseif Config.Framework == 'esx' then @@ -20,7 +20,7 @@ function DebugPrint(...) end end -local function TakeMoney(playerObject, method, amount) +local function TakeMoney(playerObject, method, amount) -- Take money from the player amount = tonumber(amount) if Config.Framework == 'qb' then @@ -42,16 +42,31 @@ local function TakeMoney(playerObject, method, amount) return false end -local function giveItem(src, playerObject, item, amount) +local function giveItem(src, playerObject, item, amount) -- Give the item to the player if Config.Framework == 'qb' then - return playerObject.Functions.AddItem(item.name, amount, false) + if Config.Inventory == 'qs' then + exports['qs-inventory']:AddItem(src, item.name, amount) + elseif Config.Inventory == 'ox' then + exports.ox_inventory:AddItem(src, item.name, amount) + else + if Config.NewQBInventory then + exports['qb-inventory']:AddItem(source, item.name, amount, false, false, 'Machines') + else + playerObject.Functions.AddItem(item.name, amount) + end + end elseif Config.Framework == 'esx' then - DebugPrint('Give item ' .. item.name .. ' to player ' .. src) - return exports.ox_inventory:AddItem(src, item.name, amount) + if Config.Inventory == 'qs' then + exports['qs-inventory']:AddItem(src, item.name, amount) + elseif Config.Inventory == 'ox' then + exports.ox_inventory:AddItem(src, item.name, amount) + else + playerObject.addInventoryItem(item.name, amount) + end end end -local function handlePurchase(src, player, item, machineName, totalPrice, cantidad) +local function handlePurchase(src, player, item, machineName, totalPrice, cantidad) -- Handle the purchase local success = false if TakeMoney(player, 'cash', totalPrice) then @@ -67,7 +82,7 @@ local function handlePurchase(src, player, item, machineName, totalPrice, cantid end end -local function findItemInSource(sourceData, itemName) +local function findItemInSource(sourceData, itemName) -- Find the item in the source data for _, item in ipairs(sourceData.items) do if item.name == itemName then return item @@ -76,7 +91,7 @@ local function findItemInSource(sourceData, itemName) return nil end -RegisterNetEvent('muhaddil-machines:buy', function(sourceType, sourceName, itemName, cantidad) +RegisterNetEvent('muhaddil-machines:buy', function(sourceType, sourceName, itemName, cantidad) -- Event for buying the item local src = source local player = getPlayerObject(src) @@ -101,7 +116,29 @@ RegisterNetEvent('muhaddil-machines:buy', function(sourceType, sourceName, itemN end end) -RegisterServerEvent('muhaddil-machines:RemoveThirst') +RegisterServerEvent('muhaddil-machines:RemoveThirst') -- Event for the watercoolers to remove thirst AddEventHandler('muhaddil-machines:RemoveThirst', function() - TriggerClientEvent('esx_status:add', source, 'thirst', Config.ThirstRemoval) -end) \ No newline at end of file + local src = source + + if Config.Framework == 'qb' then + local player = QBCore.Functions.GetPlayer(src) + if player then + local currentThirst = player.PlayerData.metadata['thirst'] or 0 + if currentThirst < 100 then + local newThirst = math.min(currentThirst + Config.ThirstRemoval, 100) + player.Functions.SetMetaData('thirst', newThirst) + + TriggerClientEvent('hud:client:UpdateNeeds', src, player.PlayerData.metadata.hunger or 50, newThirst) + else + print("[Info] El jugador " .. src .. " ya tiene la sed máxima (100).") + end + else + print("[Error] No se pudo obtener el jugador para src: " .. tostring(src)) + end + + elseif Config.Framework == 'esx' then + TriggerClientEvent('esx_status:add', src, 'thirst', Config.ThirstRemoval) + else + print("[Error] Configuración de framework no válida.") + end +end)