diff --git a/resources/[Developer]/[Nordi]/kyaktrailer/trailersmall.yft b/resources/[Developer]/[Nordi]/kyaktrailer/trailersmall.yft
new file mode 100644
index 000000000..0a9563fa9
Binary files /dev/null and b/resources/[Developer]/[Nordi]/kyaktrailer/trailersmall.yft differ
diff --git a/resources/[Developer]/[Nordi]/kyaktrailer/trailersmall.ytd b/resources/[Developer]/[Nordi]/kyaktrailer/trailersmall.ytd
new file mode 100644
index 000000000..49cd1207f
Binary files /dev/null and b/resources/[Developer]/[Nordi]/kyaktrailer/trailersmall.ytd differ
diff --git a/resources/[Developer]/[Nordi]/kyaktrailer/trailersmall_hi.yft b/resources/[Developer]/[Nordi]/kyaktrailer/trailersmall_hi.yft
new file mode 100644
index 000000000..0a9563fa9
Binary files /dev/null and b/resources/[Developer]/[Nordi]/kyaktrailer/trailersmall_hi.yft differ
diff --git a/resources/[Developer]/[Nordi]/snipe-sitting-master.zip b/resources/[Developer]/[Nordi]/snipe-sitting-master.zip
new file mode 100644
index 000000000..9ada85807
Binary files /dev/null and b/resources/[Developer]/[Nordi]/snipe-sitting-master.zip differ
diff --git a/resources/[inventory]/cs_shops/config/config.lua b/resources/[inventory]/cs_shops/config/config.lua
index 8799a0136..759d9b541 100644
--- a/resources/[inventory]/cs_shops/config/config.lua
+++ b/resources/[inventory]/cs_shops/config/config.lua
@@ -51,42 +51,12 @@ CodeStudio.Products = {
itemPrice = 2,
itemInfo = "Refreshing water",
},
- ['ecola_zero_dose'] = {
- itemName = "E-Cola Zero Dose",
+ ['pack_ecola'] = {
+ itemName = "Packet E-Cola Dosen",
itemStock = 50,
- itemPrice = 2,
- itemInfo = "Fizzy cola with a twist",
- },
- ['ecola_dose'] = {
- itemName = "E-Cola Dose",
- itemStock = 50,
- itemPrice = 2,
- itemInfo = "Fizzy cola with a twist",
- },
- ['sprunk_zero_dose'] = {
- itemName = "Sprunk Zero Dose",
- itemStock = 50,
- itemPrice = 2,
- itemInfo = "Fizzy cola with a twist",
- },
- ['sprunk_dose'] = {
- itemName = "Sprunk Dose",
- itemStock = 50,
- itemPrice = 2,
- itemInfo = "Fizzy cola with a twist",
- },
- ['orange_o_tang_zero_dose'] = {
- itemName = "Orange O Tang Zero Dose",
- itemStock = 50,
- itemPrice = 2,
- itemInfo = "Fizzy cola with a twist",
- },
- ['orange_o_tang_dose'] = {
- itemName = "Orange O Tang Dose",
- itemStock = 50,
- itemPrice = 2,
- itemInfo = "Fizzy cola with a twist",
- },
+ itemPrice = 20,
+ itemInfo = "",
+ },
['ecola_zero_flasche'] = {
itemName = "E-Cola Zero Flasche",
itemStock = 50,
@@ -123,12 +93,6 @@ CodeStudio.Products = {
itemPrice = 4,
itemInfo = "Fizzy cola with a twist",
},
- ['junk_energy'] = {
- itemName = "Junk Energy",
- itemStock = 50,
- itemPrice = 2,
- itemInfo = "Dose mit Engery",
- },
['munky_juice'] = {
itemName = "Munky Juice",
itemStock = 50,
diff --git a/resources/[inventory]/inventory_images/images/mimis_instant_nudeln.png b/resources/[inventory]/inventory_images/images/mimis_instant_nudeln.png
new file mode 100644
index 000000000..14a1f73b8
Binary files /dev/null and b/resources/[inventory]/inventory_images/images/mimis_instant_nudeln.png differ
diff --git a/resources/[inventory]/muhaddil-machines/.gitattributes b/resources/[inventory]/muhaddil-machines/.gitattributes
new file mode 100644
index 000000000..dfe077042
--- /dev/null
+++ b/resources/[inventory]/muhaddil-machines/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/resources/[inventory]/muhaddil-machines/LICENSE b/resources/[inventory]/muhaddil-machines/LICENSE
new file mode 100644
index 000000000..7d57900f2
--- /dev/null
+++ b/resources/[inventory]/muhaddil-machines/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Muhaddil
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/resources/[inventory]/muhaddil-machines/NUI/NUI.html b/resources/[inventory]/muhaddil-machines/NUI/NUI.html
new file mode 100644
index 000000000..be4c2f2c7
--- /dev/null
+++ b/resources/[inventory]/muhaddil-machines/NUI/NUI.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Vending Machine
+
+
+
+
Elige un producto
+
+
+
+
+
+
+
diff --git a/resources/[inventory]/muhaddil-machines/README.md b/resources/[inventory]/muhaddil-machines/README.md
new file mode 100644
index 000000000..1b8abbff6
--- /dev/null
+++ b/resources/[inventory]/muhaddil-machines/README.md
@@ -0,0 +1,61 @@
+# 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.
+
+
+
+
+
diff --git a/resources/[inventory]/muhaddil-machines/client.lua b/resources/[inventory]/muhaddil-machines/client.lua
new file mode 100644
index 000000000..3df4694ba
--- /dev/null
+++ b/resources/[inventory]/muhaddil-machines/client.lua
@@ -0,0 +1,472 @@
+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()
+elseif Config.Framework == "qb" then
+ QBCore = exports['qb-core']:GetCoreObject()
+elseif Config.Framework == "ox" then
+ Ox = require '@ox_core.lib.init'
+else
+ ESX = exports['es_extended']:getSharedObject()
+end
+
+function DebugPrint(...) -- Debug print function
+ if Config.DebugMode then
+ print(...)
+ end
+end
+
+function Notify(msgtitle, msg, time, type2) -- Notification function
+ if Config.UseOXNotifications then
+ lib.notify({
+ title = msgtitle,
+ description = msg,
+ showDuration = true,
+ type = type2,
+ style = {
+ backgroundColor = 'rgba(0, 0, 0, 0.75)',
+ color = 'rgba(255, 255, 255, 1)',
+ ['.description'] = {
+ color = '#909296',
+ backgroundColor = 'transparent'
+ }
+ }
+ })
+ else
+ if Config.Framework == 'qb' then
+ QBCore.Functions.Notify(msg, type2, time)
+ elseif Config.Framework == 'esx' then
+ TriggerEvent('esx:showNotification', msg, type2, time)
+ elseif Config.Framework == 'ox' then
+ lib.notify({
+ title = msgtitle,
+ description = msg,
+ showDuration = true,
+ type = type2,
+ style = {
+ backgroundColor = 'rgba(0, 0, 0, 0.75)',
+ color = 'rgba(255, 255, 255, 1)',
+ ['.description'] = {
+ color = '#909296',
+ backgroundColor = 'transparent'
+ }
+ }
+ })
+ end
+ end
+end
+
+RegisterNetEvent("muhaddil-machines:Notify")
+AddEventHandler("muhaddil-machines:Notify", function(msgtitle, msg, time, type)
+ Notify(msgtitle, msg, time, type)
+end)
+
+local function loadAnimDict(animDict)
+ RequestAnimDict(animDict)
+ while not HasAnimDictLoaded(animDict) do
+ Citizen.Wait(0)
+ end
+end
+
+local function playAnimation(ped, animDict, animName)
+ loadAnimDict(animDict)
+ TaskPlayAnim(ped, animDict, animName, 8.0, 5.0, -1, 1, 1, false, false, false)
+ Citizen.Wait(4500)
+ ClearPedTasks(ped)
+ RemoveAnimDict(animDict)
+end
+
+local function vendingAnimation(entity)
+ local ped = PlayerPedId()
+ local position = GetOffsetFromEntityInWorldCoords(entity, 0.0, -0.97, 0.05)
+ local heading = GetEntityHeading(entity)
+ local prop_name = 'ng_proc_sodacan_01a'
+ 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
+ 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)
+
+ Citizen.CreateThread(function()
+ local playerPed = PlayerPedId()
+ local x, y, z = table.unpack(GetEntityCoords(playerPed))
+
+ if Config.VisibleProp then
+ local prop = CreateObject(GetHashKey(prop_name), x, y, z + 0.2, true, true, true)
+ local boneIndex = GetPedBoneIndex(playerPed, 18905)
+ end
+
+ RequestAmbientAudioBank("VENDING_MACHINE", false)
+ HintAmbientAudioBank("VENDING_MACHINE", 0)
+ if Config.VisibleProp then
+ AttachEntityToEntity(prop, playerPed, boneIndex, 0.12, 0.008, 0.03, 240.0, -60.0, 0.0, true, true, false,
+ true, 1, true)
+ end
+ playAnimation(playerPed, Config.Animations.sodamachines[1], Config.Animations.sodamachines[2])
+ Citizen.Wait(1000)
+
+ ClearPedSecondaryTask(playerPed)
+ if DoesEntityExist(prop) then
+ DeleteObject(prop)
+ end
+ ReleaseAmbientAudioBank()
+
+ buying = false
+ end)
+end
+
+
+local function showQuantityDialog(item, vendingMachineName, entity)
+ local input = lib.inputDialog("Selecciona la cantidad", {
+ { type = 'number', label = 'Cantidad', min = 1, max = Config.InputMaxValue, default = 1 }
+ })
+
+ if not input then return end
+ local cantidad = input[1]
+
+ if cantidad and cantidad > 0 then
+ local ped = PlayerPedId()
+ if buying then return end
+ buying = true
+
+ vendingAnimation(entity)
+
+ Citizen.Wait(4500)
+
+ TriggerServerEvent('muhaddil-machines:buy', 'machine', vendingMachineName, item.name, cantidad)
+ else
+ Notify('Error', 'Cantidad no válida', 5000, "error")
+ end
+
+ buying = false
+end
+
+local function replacePrice(inputString, price)
+ return inputString:gsub("%%price%%", tostring(price))
+end
+
+local function showVendingMenu(vendingMachineName, entity, items)
+ local options = {}
+
+ for _, item in pairs(items) do
+ table.insert(options, {
+ title = replacePrice(item.label, item.price),
+ icon = item.icon or 'fa-solid fa-bottle-water',
+ onSelect = function()
+ if buying then return end
+ showQuantityDialog(item, vendingMachineName, entity)
+ end
+ })
+ end
+
+ lib.registerContext({
+ id = 'vending_menu_' .. vendingMachineName,
+ title = 'Máquina expendedora',
+ canClose = true,
+ options = options
+ })
+
+ lib.showContext('vending_menu_' .. vendingMachineName)
+end
+
+local function interactWithWaterCooler(entity)
+ local currentTime = GetGameTimer()
+
+ if Config.ShowWaitNotification then
+ if currentTime - LastWaterCoolerUse < TimeoutDuration then
+ DebugPrint("Debes esperar antes de usar nuevamente la fuente de agua.")
+ Notify('¡Echa el freno madaleno!', 'Relaja, espera un poco antes de volver a usar la máquina', 5000, "error")
+ return
+ end
+ end
+
+ if IsAnimated then return end
+ IsAnimated = true
+ LastWaterCoolerUse = currentTime
+
+ local prop_name = 'prop_cs_paper_cup'
+ local ped = PlayerPedId()
+ local position = GetOffsetFromEntityInWorldCoords(entity, 0.0, -0.97, 0.05)
+ local heading = GetEntityHeading(entity)
+
+ if not IsEntityAtCoord(ped, position.x, position.y, position.z, 0.1, 0.1, 0.1, false, true, 0) then
+ TaskGoStraightToCoord(ped, position.x, position.y, position.z, 1.0, 20000, heading, 0.1)
+ Wait(1000)
+ end
+ TaskTurnPedToFaceEntity(ped, entity, -1)
+
+ lib.progressBar({
+ duration = 2000,
+ label = 'Llenado Vaso',
+ })
+
+ 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.008, 0.03, 240.0, -60.0, 0.0, true, true, false, true, 1,
+ true)
+
+ local animDict = 'mp_player_intdrink'
+ local animName = 'loop_bottle'
+
+ playAnimation(playerPed, animDict, animName)
+
+ if Config.CountDrinksPlace == 'before' then
+ DrinkCount = DrinkCount + 1
+ end
+
+ if Config.KillPlayerOnExcess then
+ local warningThreshold = math.ceil(Config.MaxDrinksBeforeKill / 2)
+ print(warningThreshold)
+
+ if DrinkCount >= Config.MaxDrinksBeforeKill then
+ SetEntityHealth(playerPed, 0)
+ Notify('Demasiada agua', 'Has bebido demasiada agua y has muerto.', 5000, "error")
+ DrinkCount = 0
+ elseif DrinkCount >= warningThreshold then
+ Notify('Agua fresca', 'Sigues bebiendo... ten cuidado.', 3000, "info")
+ else
+ Notify('Agua fresca', 'Has tomado un vaso de agua.', 3000, "info")
+ end
+ else
+ Notify('Agua fresca', 'Has tomado un vaso de agua.', 3000, "info")
+ end
+
+ IsAnimated = false
+ ClearPedSecondaryTask(playerPed)
+ DeleteObject(prop)
+ RemoveAnimDict(animDict)
+
+ TriggerServerEvent('muhaddil-machines:RemoveThirst')
+
+ if Config.CountDrinksPlace == 'after' then
+ DrinkCount = DrinkCount + 1
+ end
+ end)
+end
+
+local function WaterCoolerTarget()
+ for waterCoolerName, data in pairs(Config.WaterCoolers) do
+ local options = {
+ {
+ label = "Beber Agua",
+ icon = 'fa-solid fa-glass-water',
+ onSelect = function(d)
+ local entity = d.entity
+ interactWithWaterCooler(entity)
+ end
+ }
+ }
+
+ exports.ox_target:addModel(joaat(data.model), options)
+ end
+end
+
+local function standAnimation(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
+ 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)
+
+ ClearPedSecondaryTask(ped)
+
+ 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 }
+ })
+
+ if not input then return end
+ local cantidad = input[1]
+
+ if cantidad and cantidad > 0 then
+ local ped = PlayerPedId()
+ if buying then return end
+ buying = true
+
+ standAnimation(entity)
+
+ TriggerServerEvent('muhaddil-machines:buy', 'stand', standName, item.name, cantidad)
+ else
+ Notify('Error', 'Cantidad no válida', 5000, "error")
+ end
+
+ buying = false
+end
+
+local function standMenu(standName, entity, items)
+ local options = {}
+
+ for _, item in pairs(items) do
+ table.insert(options, {
+ title = replacePrice(item.label, item.price),
+ icon = item.icon or 'fa-solid fa-bottle-water',
+ onSelect = function()
+ if buying then return end
+ showQuantityDialogStands(item, standName, entity)
+ end
+ })
+ end
+
+ lib.registerContext({
+ id = 'stand_menu_' .. standName,
+ title = 'Puesto de Comida',
+ canClose = true,
+ options = options
+ })
+
+ lib.showContext('stand_menu_' .. standName)
+end
+
+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.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)
+
+ 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 }
+ })
+
+ if not input then return end
+ local cantidad = input[1]
+
+ if cantidad and cantidad > 0 then
+ local ped = PlayerPedId()
+ if buying then return end
+ buying = true
+
+ newsAnimation(entity)
+
+ TriggerServerEvent('muhaddil-machines:buy', 'news', newsName, item.name, cantidad)
+ else
+ Notify('Error', 'Cantidad no válida', 5000, "error")
+ end
+
+ buying = false
+end
+
+local function newsMenu(newsName, entity, items)
+ local options = {}
+
+ for _, item in pairs(items) do
+ table.insert(options, {
+ title = replacePrice(item.label, item.price),
+ icon = item.icon or 'fa-solid fa-bottle-water',
+ onSelect = function()
+ if buying then return end
+ showQuantityDialogNews(item, newsName, entity)
+ end
+ })
+ end
+
+ lib.registerContext({
+ id = 'news_menu_' .. newsName,
+ title = 'Venta de Noticias',
+ canClose = true,
+ options = options
+ })
+
+ lib.showContext('news_menu_' .. newsName)
+end
+
+local function setupTargeting()
+ for vendingMachineName, data in pairs(Config.machines) do
+ local options = {
+ {
+ label = "Abrir Máquina Expendedora",
+ icon = 'fa-solid fa-basket-shopping',
+ onSelect = function(d)
+ if buying then return end
+ local entity = d.entity
+ showVendingMenu(vendingMachineName, entity, data.items)
+ end
+ }
+ }
+
+ exports.ox_target:addModel(joaat(data.model), options)
+ end
+
+ for standName, data in pairs(Config.Stands) do
+ local options = {
+ {
+ label = "Abrir Puesto de Comida",
+ icon = 'fa-solid fa-utensils',
+ onSelect = function(d)
+ if buying then return end
+ local entity = d.entity
+ standMenu(standName, entity, data.items)
+ end
+ }
+ }
+
+ exports.ox_target:addModel(joaat(data.model), options)
+ end
+
+ for newsName, data in pairs(Config.NewsSellers) do
+ local options = {
+ {
+ label = "Abrir Venta de Noticias",
+ icon = 'fa-solid fa-newspaper',
+ onSelect = function(d)
+ if buying then return end
+ local entity = d.entity
+ newsMenu(newsName, entity, data.items)
+ end
+ }
+ }
+
+ exports.ox_target:addModel(joaat(data.model), options)
+ end
+end
+
+CreateThread(function()
+ Wait(100)
+ setupTargeting()
+ WaterCoolerTarget()
+ -- standsTarget()
+end)
diff --git a/resources/[inventory]/muhaddil-machines/config.lua b/resources/[inventory]/muhaddil-machines/config.lua
new file mode 100644
index 000000000..873e7d8a4
--- /dev/null
+++ b/resources/[inventory]/muhaddil-machines/config.lua
@@ -0,0 +1,314 @@
+Config = Config or {}
+
+Config.DebugMode = true -- Enable debug mode
+Config.Framework = 'qb' -- 'esx', 'qb' or 'ox'
+Config.UseOXNotifications = true -- Use OX Notifications or framework notifications
+Config.Inventory = ''-- 'qs', 'ox' or leave blank
+Config.NewQBInventory = false -- If you're using the new QB Inventory
+
+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 = { -- Animations for the vending machines
+ stand = { -- Stand animations
+ "special_ped@baygor@monologue_2@monologue_2h",
+ "you_can_ignore_me_7"
+ },
+ 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 = { -- Vending machines
+ {
+ model = 'prop_vend_soda_02',
+ items = {
+ {
+ name = "ecola_dose",
+ label = 'E-Cola Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ {
+ name = "sprunk_dose",
+ label = 'Sprunk Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ {
+ name = "orange_o_tang_dose",
+ label = 'Orange O Tang Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ {
+ name = "ecola_zero_dose",
+ label = 'E-Cola Zero Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',',
+ price = 4
+ },
+ {
+ name = "orange_o_tang_zero_dose",
+ label = 'Orange O Tang Zero Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',',
+ price = 4
+ },
+ {
+ name = "sprunk_zero_dose",
+ label = 'Sprunk Zero Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',',
+ price = 4
+ },
+ },
+ },
+ {
+ model = 'prop_vend_soda_01',
+ items = {
+ {
+ name = "ecola_dose",
+ label = 'E-Cola Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ {
+ name = "sprunk_dose",
+ label = 'Sprunk Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ {
+ name = "orange_o_tang_dose",
+ label = 'Orange O Tang Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ {
+ name = "ecola_zero_dose",
+ label = 'E-Cola Zero Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',',
+ price = 4
+ },
+ {
+ name = "orange_o_tang_zero_dose",
+ label = 'Orange O Tang Zero Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',',
+ price = 4
+ },
+ {
+ name = "sprunk_zero_dose",
+ label = 'Sprunk Zero Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',',
+ price = 4
+ },
+ },
+ },
+ {
+ model = 'ch_chint10_vending_smallroom_01',
+ items = {
+ {
+ name = "ecola_dose",
+ label = 'E-Cola Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ {
+ name = "sprunk_dose",
+ label = 'Sprunk Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ {
+ name = "orange_o_tang_dose",
+ label = 'Orange O Tang Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ {
+ name = "ecola_zero_dose",
+ label = 'E-Cola Zero Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',',
+ price = 4
+ },
+ {
+ name = "orange_o_tang_zero_dose",
+ label = 'Orange O Tang Zero Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',',
+ price = 4
+ },
+ {
+ name = "sprunk_zero_dose",
+ label = 'Sprunk Zero Dose ($%price%)',
+ icon = 'fa-solid fa-can-food',',
+ price = 4
+ },
+ },
+ },
+ {
+ model = 'm23_2_prop_m32_vend_drink_01a',
+ items = {
+ {
+ name = "junk_energy",
+ label = 'Junk Energy ($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ },
+ },
+ {
+ model = 'sf_prop_sf_vend_drink_01a',
+ items = {
+ {
+ name = "junk_energy",
+ label = 'Junk Energy($%price%)',
+ icon = 'fa-solid fa-can-food',
+ price = 4
+ },
+ },
+ },
+ {
+ model = 'prop_vend_water_01',
+ items = {
+ {
+ name = "water",
+ label = 'Flasche Wasser ($%price%)',
+ price = 2
+ },
+ },
+ },
+ {
+ model = 'prop_vend_snak_01',
+ items = {
+ {
+ name = "mimis_instant_nudeln",
+ label = 'Mimis Instant Nudeln ($%price%)',
+ icon = 'fa-solid fa-pot-food',
+ price = 6
+ },
+ {
+ name = "twerks_candy",
+ label = 'Ego Chaser Schockriegel ($%price%)',
+ icon = 'fa-solid fa-candy-bar',
+ price = 2
+ },
+ {
+ name = "snikkel_candy",
+ label = 'EarthQuakes ($%price%)',
+ icon = 'fa-solid fa-candy-bar',
+ price = 2
+ },
+ },
+ },
+ {
+ model = 'prop_vend_snak_01_tu',
+ items = {
+ {
+ name = "mimis_instant_nudeln",
+ label = 'Mimis Instant Nudeln ($%price%)',
+ icon = 'fa-solid fa-pot-food',
+ price = 6
+ },
+ {
+ name = "twerks_candy",
+ label = 'Ego Chaser Schockriegel ($%price%)',
+ icon = 'fa-solid fa-candy-bar',
+ price = 2
+ },
+ {
+ name = "snikkel_candy",
+ label = 'EarthQuakes ($%price%)',
+ icon = 'fa-solid fa-candy-bar',
+ price = 2
+ },
+ },
+ },
+
+ {
+ model = 'prop_vend_coffe_01',
+ items = {
+ {
+ name = "coffe",
+ label = 'Kaffee Togo ($%price%)',
+ icon = 'fa fa-mug-hot',
+ price = 4
+ },
+ {
+ name = "kakao",
+ label = 'Kakao Togo ($%price%)',
+ icon = 'fa fa-mug-hot',
+ price = 4
+ },
+ },
+ },
+ {
+ model = 'prop_vend_fags_01',
+ items = {
+ {
+ name = "redwoodpack",
+ label = 'Packung Redwood Zigaretten ($%price%)',
+ icon = 'fa-solid fa-smoking',
+ price = 10
+ },
+ },
+ },
+
+
+
+
+},
+
+
+
+
+
+Config.WaterCoolers = { -- Water coolers
+ {model = 'prop_watercooler_dark',},
+ {model = 'prop_watercooler',},
+}
+
+
+Config.NewsSellers = { -- News sellers
+ {
+ model = 'prop_news_disp_06a',
+ items = {
+ {
+ name = "newspaper",
+ label = 'Periódico ($%price%)',
+ icon = 'fa fa-newspaper',
+ price = 30
+ },
+ },
+ },
+ {
+ 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/resources/[inventory]/muhaddil-machines/fxmanifest.lua b/resources/[inventory]/muhaddil-machines/fxmanifest.lua
new file mode 100644
index 000000000..c88c2bc08
--- /dev/null
+++ b/resources/[inventory]/muhaddil-machines/fxmanifest.lua
@@ -0,0 +1,16 @@
+fx_version 'cerulean'
+game 'gta5'
+lua54 'yes'
+
+author 'Muhaddil'
+description 'FiveM script that adds vending machines'
+version 'v1.0.1'
+
+shared_scripts {
+ 'config.lua',
+ '@ox_lib/init.lua',
+}
+
+client_script 'client.lua'
+
+server_script 'server/*'
\ No newline at end of file
diff --git a/resources/[inventory]/muhaddil-machines/server/autoChecker.lua b/resources/[inventory]/muhaddil-machines/server/autoChecker.lua
new file mode 100644
index 000000000..386da32f0
--- /dev/null
+++ b/resources/[inventory]/muhaddil-machines/server/autoChecker.lua
@@ -0,0 +1,120 @@
+local currentVersion = GetResourceMetadata(GetCurrentResourceName(), 'version')
+local resourceName = 'Muhaddil/muhaddil-machines'
+local githubApiUrl = 'https://api.github.com/repos/' .. resourceName .. '/releases/latest'
+
+-- Función para calcular la diferencia en días
+local function daysAgo(dateStr)
+ local year, month, day = dateStr:match("(%d+)-(%d+)-(%d+)")
+ local releaseTime = os.time({ year = year, month = month, day = day })
+ local currentTime = os.time()
+ local difference = os.difftime(currentTime, releaseTime) / (60 * 60 * 24) -- Diferencia en días
+ return math.floor(difference)
+end
+
+-- Función para convertir la fecha a "hace X días"
+local function formatDate(releaseDate)
+ local days = daysAgo(releaseDate)
+ if days < 1 then
+ return "Today"
+ elseif days == 1 then
+ return "Yesterday"
+ else
+ return days .. " days ago"
+ end
+end
+
+-- Función para acortar la URL
+local function shortenTexts(text)
+ local maxLength = 35
+ if #text > maxLength then
+ local shortened = text:sub(1, maxLength - 3) .. '...'
+ return shortened
+ else
+ return text
+ end
+end
+
+local function printWithColor(message, colorCode)
+ if type(message) ~= "string" then
+ message = tostring(message)
+ end
+ print('\27[' .. colorCode .. 'm' .. message .. '\27[0m')
+end
+
+local function printCentered(text, length, colorCode)
+ local padding = math.max(length - #text - 2, 0)
+ local leftPadding = math.floor(padding / 2)
+ local rightPadding = padding - leftPadding
+ printWithColor('│' .. string.rep(' ', leftPadding) .. text .. string.rep(' ', rightPadding) .. '│', colorCode)
+end
+
+local function printWrapped(text, length, colorCode)
+ if type(text) ~= "string" then
+ text = tostring(text)
+ end
+
+ local maxLength = length - 2
+ local pos = 1
+
+ while pos <= #text do
+ local endPos = pos + maxLength - 1
+ if endPos > #text then
+ endPos = #text
+ else
+ local spaceIndex = text:sub(pos, endPos):match('.*%s') or maxLength
+ endPos = pos + spaceIndex - 1
+ end
+
+ local line = text:sub(pos, endPos)
+ if endPos < #text then
+ line = line .. '...'
+ end
+
+ printWithColor('│' .. line .. string.rep(' ', length - #line) .. '│', colorCode)
+
+ pos = endPos + 1
+ end
+end
+
+if Config.AutoVersionChecker then
+ PerformHttpRequest(githubApiUrl, function(statusCode, response, headers)
+ if statusCode == 200 then
+ local data = json.decode(response)
+
+ if data and data.tag_name then
+ local latestVersion = data.tag_name
+ local releaseDate = data.published_at or "Unknown"
+ local formattedDate = formatDate(releaseDate)
+ local notes = data.body or "No notes available"
+ local downloadUrl = data.html_url or "No download link available"
+ local shortenedUrl = shortenTexts(downloadUrl)
+ local shortenedNotes = shortenTexts(notes)
+
+
+ local boxWidth = 52
+
+ if latestVersion ~= currentVersion then
+ print('╭────────────────────────────────────────────────────╮')
+ printWrapped('[muhaddil-machines] - New Version Available', boxWidth, '34') -- Blue
+ printWrapped('Current version: ' .. currentVersion, boxWidth, '32') -- Green
+ printWrapped('Latest version: ' .. latestVersion, boxWidth, '33') -- Yellow
+ printWrapped('Released: ' .. formattedDate, boxWidth, '33') -- Yellow
+ printWrapped('Notes: ' .. shortenedNotes, boxWidth, '33') -- Yellow
+ printWrapped('Download: ' .. shortenedUrl, boxWidth, '32') -- Green
+ print('╰────────────────────────────────────────────────────╯')
+ else
+ print('╭────────────────────────────────────────────────────╮')
+ printWrapped('[muhaddil-machines] - Up-to-date', boxWidth, '32') -- Green
+ printWrapped('Current version: ' .. currentVersion, boxWidth, '32') -- Green
+ print('╰────────────────────────────────────────────────────╯')
+ end
+ else
+ printWithColor('[muhaddil-machines] - Error: The JSON structure is not as expected.', '31') -- Red
+ printWithColor('GitHub API Response: ' .. response, '31') -- Red
+ end
+ else
+ printWithColor(
+ '[muhaddil-machines] - Failed to check for latest version. Status code: ' .. statusCode, '31') -- Red
+ end
+ end, 'GET')
+end
diff --git a/resources/[inventory]/muhaddil-machines/server/server.lua b/resources/[inventory]/muhaddil-machines/server/server.lua
new file mode 100644
index 000000000..d10af7b62
--- /dev/null
+++ b/resources/[inventory]/muhaddil-machines/server/server.lua
@@ -0,0 +1,164 @@
+if Config.Framework == "esx" then
+ ESX = exports['es_extended']:getSharedObject()
+elseif Config.Framework == "qb" then
+ QBCore = exports['qb-core']:GetCoreObject()
+elseif Config.Framework == "ox" then
+ Ox = require '@ox_core.lib.init'
+else
+ ESX = exports['es_extended']:getSharedObject()
+end
+
+local function getPlayerObject(src) -- Get the player object
+ if Config.Framework == 'qb' then
+ return QBCore.Functions.GetPlayer(src)
+ elseif Config.Framework == 'esx' then
+ return ESX.GetPlayerFromId(src)
+ elseif Config.Framework == 'ox' then
+ return Ox.GetPlayer(src)
+ end
+end
+
+function DebugPrint(...)
+ if Config.DebugMode then
+ print(...)
+ end
+end
+
+local function TakeMoney(playerObject, method, amount) -- Take money from the player
+ amount = tonumber(amount)
+
+ if Config.Framework == 'qb' then
+ return playerObject.Functions.RemoveMoney(method, amount)
+ elseif Config.Framework == 'esx' then
+ if method == 'cash' then
+ if playerObject.getMoney() >= amount then
+ playerObject.removeMoney(amount)
+ return true
+ end
+ elseif method == 'bank' then
+ if playerObject.getAccount('bank').money >= amount then
+ playerObject.removeAccountMoney('bank', amount)
+ return true
+ end
+ end
+ elseif Config.Framework == 'ox' then
+ if exports.ox_inventory:GetItemCount(source, 'money') >= amount then
+ exports.ox_inventory:RemoveItem(source, 'money', amount)
+ return true
+ end
+ end
+
+ return false
+end
+
+local function giveItem(src, playerObject, item, amount) -- Give the item to the player
+ if Config.Framework == 'qb' then
+ 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
+ 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
+ elseif Config.Framework == 'ox' then
+ exports.ox_inventory:AddItem(source, item.name, amount)
+ end
+end
+
+local function handlePurchase(src, player, item, machineName, totalPrice, cantidad) -- Handle the purchase
+ local success = false
+
+ if TakeMoney(player, 'cash', totalPrice) then
+ success = true
+ elseif TakeMoney(player, 'bank', totalPrice) then
+ success = true
+ end
+
+ if success then
+ giveItem(src, player, item, cantidad)
+ else
+ TriggerClientEvent('muhaddil-machines:Notify', src, '', 'No tienes suficiente dinero.', 'error')
+ end
+end
+
+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
+ end
+ end
+ return nil
+end
+
+RegisterNetEvent('muhaddil-machines:buy',
+ function(sourceType, sourceName, itemName, cantidad) -- Event for buying the item
+ local src = source
+ local player = getPlayerObject(src)
+
+ local sourceData
+ if sourceType == 'machine' then
+ sourceData = Config.machines[sourceName]
+ elseif sourceType == 'stand' then
+ sourceData = Config.Stands[sourceName]
+ elseif sourceType == 'news' then
+ sourceData = Config.NewsSellers[sourceName]
+ else
+ TriggerClientEvent('muhaddil-machines:Notify', src, '', 'El tipo de origen no es válido.', 'error')
+ return
+ end
+
+ local item = findItemInSource(sourceData, itemName)
+ if item then
+ local totalPrice = item.price * cantidad
+ handlePurchase(src, player, item, sourceName, totalPrice, cantidad)
+ else
+ TriggerClientEvent('muhaddil-machines:Notify', src, '', 'El artículo no está disponible', 'error')
+ end
+ end)
+
+RegisterServerEvent('muhaddil-machines:RemoveThirst') -- Event for the watercoolers to remove thirst
+AddEventHandler('muhaddil-machines:RemoveThirst', function()
+ 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)
+ elseif Config.Framework == 'ox' then
+ local player = Ox.GetPlayer(src)
+ local beforeStatus = player.getStatus('thirst')
+ player.removeStatus('thirst', Config.ThirstRemoval)
+ local afterStatus = player.getStatus('thirst')
+ DebugPrint("[Info] El jugador " ..
+ src .. " tenía " .. beforeStatus .. " de sed y ahora tiene " .. afterStatus .. ".")
+ local statuses = player.getStatuses()
+ DebugPrint("[Info] Los estados del jugador " .. src .. " son: " .. json.encode(statuses))
+ else
+ print("[Error] Configuración de framework no válida.")
+ end
+end)
diff --git a/resources/[inventory]/pickle_consumables/config.lua b/resources/[inventory]/pickle_consumables/config.lua
index b615d7430..c15f01536 100644
--- a/resources/[inventory]/pickle_consumables/config.lua
+++ b/resources/[inventory]/pickle_consumables/config.lua
@@ -197,7 +197,7 @@ Config.Items = {
stamina = 0,
}
},
- ["tims_instant_nudeln"] = {
+ ["mimis_instant_nudeln"] = {
uses = 1,
prop = { model = `v_res_fa_potnoodle`, boneId = 60309, offset = vec3(0.08, -0.04, 0.07), rotation = vec3(-30.0, 10.0, 0.0) },
idle = { dict = "anim@scripted@island@special_peds@pavel@hs4_pavel_ig5_caviar_p1", anim = "base_idle", time = 2000, params = { nil, nil, nil, 49 } },
diff --git a/resources/[inventory]/tgiann-inventory/configs/configVendingMachine.lua b/resources/[inventory]/tgiann-inventory/configs/configVendingMachine.lua
index 9e56ac360..0f5a66667 100644
--- a/resources/[inventory]/tgiann-inventory/configs/configVendingMachine.lua
+++ b/resources/[inventory]/tgiann-inventory/configs/configVendingMachine.lua
@@ -1,5 +1,5 @@
config.vendingMachine = {
- active = true,
+ active = false,
machines = {
{
objects = {
diff --git a/resources/[inventory]/tgiann-inventory/items/items.lua b/resources/[inventory]/tgiann-inventory/items/items.lua
index 647ceda90..ea76faef2 100644
--- a/resources/[inventory]/tgiann-inventory/items/items.lua
+++ b/resources/[inventory]/tgiann-inventory/items/items.lua
@@ -1590,7 +1590,7 @@ itemsData = {
},
redwoodpack = {
useable = true,
- weight = 200,
+ weight = 100,
type = 'item',
unique = false,
description = '',
@@ -8252,16 +8252,16 @@ itemsData = {
label = 'Funkgerät',
name = 'radio',
},
- tims_instant_nudeln = {
+ mimis_instant_nudeln = {
useable = true,
weight = 100,
type = 'item',
unique = false,
description = 'der Kulinarische Blitzbesuch in Fernost',
- image = 'tims_instant_nudeln.png',
+ image = 'mimis_instant_nudeln.png',
shouldClose = true,
- label = 'Tim\'s Instant Nudeln',
- name = 'tims_instant_nudeln',
+ label = 'Mimis Instant Nudeln',
+ name = 'mimis_instant_nudeln',
},
attachment_bench = {
useable = true,
@@ -10197,7 +10197,7 @@ itemsData = {
type = 'item',
description = '',
weight = 1000,
- label = 'Packung E-Cola',
+ label = 'Packet E-Cola Dosen',
unique = true,
useable = true,
image = 'pack_ecola.png',