Skip to content

Commit

Permalink
Update README and enhance configuration for vending machine script
Browse files Browse the repository at this point in the history
Fixed some fuctions for QB and several aditions.
  • Loading branch information
Muhaddil committed Dec 23, 2024
1 parent 6ea7cb0 commit d3ca673
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 41 deletions.
58 changes: 56 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.
27 changes: 15 additions & 12 deletions client.lua
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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,
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -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 }
Expand All @@ -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
Expand Down
91 changes: 76 additions & 15 deletions config.lua
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down Expand Up @@ -138,7 +144,6 @@ Config.machines = {
price = 40
}
},
offset = vec3(0, 0, 0)
},

{
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 = {
Expand All @@ -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
},
},
},
}
61 changes: 49 additions & 12 deletions server/server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)

Expand All @@ -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)
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)

0 comments on commit d3ca673

Please sign in to comment.