local QBCore = exports['qb-core']:GetCoreObject() -- List of prop models that should be targetable as immediate shredders local shredderPropModels = { 'p_secret_weapon_02', 'prop_bin_08a' } -- List of prop models that should be targetable as trash bins with delayed deletion local trashBinPropModels = { 'prop_bin_01a', 'prop_bin_03a', 'prop_bin_04a', 'prop_bin_07a', 'prop_dumpster_01a', 'prop_dumpster_02a', 'prop_dumpster_02b', 'prop_dumpster_3a' } -- Variable to store the current entity being interacted with local currentEntity = nil local currentType = nil -- Add QB-Target to all matching props in the world Citizen.CreateThread(function() -- Add target to shredder props exports['qb-target']:AddTargetModel(shredderPropModels, { options = { { type = "client", event = "disposal:openInventory", icon = "fas fa-dumpster", label = "Müllschredder öffnen", action = function(entity) currentEntity = entity currentType = "shredder" TriggerEvent('disposal:openInventory') end, canInteract = function() return true end, }, { type = "client", event = "disposal:openMenu", icon = "fas fa-fire", label = "Items vernichten", action = function(entity) currentEntity = entity currentType = "shredder" TriggerEvent('disposal:openMenu') end, canInteract = function() return true end, } }, distance = 2.0 }) -- Add target to trash bin props exports['qb-target']:AddTargetModel(trashBinPropModels, { options = { { type = "client", event = "disposal:openInventory", icon = "fas fa-trash", label = "Mülltonne öffnen", action = function(entity) currentEntity = entity currentType = "trash" TriggerEvent('disposal:openInventory') end, canInteract = function() return true end, }, { type = "client", event = "disposal:openMenu", icon = "fas fa-clock", label = "Müll entsorgen", action = function(entity) currentEntity = entity currentType = "trash" TriggerEvent('disposal:openMenu') end, canInteract = function() return true end, } }, distance = 2.0 }) print("^2[DISPOSAL]^7 Added QB-Target to " .. #shredderPropModels .. " shredder models and " .. #trashBinPropModels .. " trash bin models") end) -- Function to get container ID from entity function GetContainerIDFromEntity(entity, type) if not entity or not DoesEntityExist(entity) then return nil end local model = GetEntityModel(entity) local entityCoords = GetEntityCoords(entity) return type .. "_" .. model .. "_" .. math.floor(entityCoords.x) .. "_" .. math.floor(entityCoords.y) .. "_" .. math.floor(entityCoords.z) end -- Open container inventory RegisterNetEvent('disposal:openInventory', function() local playerPed = PlayerPedId() local coords = GetEntityCoords(playerPed) if not currentEntity or not DoesEntityExist(currentEntity) then lib.notify({ title = currentType == "shredder" and 'Müllschredder' or 'Mülltonne', description = currentType == "shredder" and 'Kein Schredder gefunden!' or 'Keine Mülltonne gefunden!', type = 'error' }) return end -- Get container ID local containerID = GetContainerIDFromEntity(currentEntity, currentType) if not containerID then return end -- Open inventory with this unique ID TriggerServerEvent('disposal:server:openInventory', containerID, currentType) end) -- Open disposal menu RegisterNetEvent('disposal:openMenu', function() local playerPed = PlayerPedId() local coords = GetEntityCoords(playerPed) if not currentEntity or not DoesEntityExist(currentEntity) then lib.notify({ title = currentType == "shredder" and 'Müllschredder' or 'Mülltonne', description = currentType == "shredder" and 'Kein Schredder gefunden!' or 'Keine Mülltonne gefunden!', type = 'error' }) return end -- Get container ID local containerID = GetContainerIDFromEntity(currentEntity, currentType) if not containerID then return end -- Get items in this container TriggerServerEvent('disposal:server:getItems', containerID, currentType) end) -- Show menu with items RegisterNetEvent('disposal:client:showMenu', function(items, containerID, type, timeRemaining) -- Make sure items is a table items = items or {} -- Check if items is empty if next(items) == nil then lib.notify({ title = type == "shredder" and 'Müllschredder' or 'Mülltonne', description = type == "shredder" and 'Der Schredder ist leer!' or 'Die Mülltonne ist leer!', type = 'error' }) return end local menuOptions = {} -- All items action option local actionText = type == "shredder" and "ALLE ITEMS VERNICHTEN" or "ALLE ITEMS ENTSORGEN" local actionDesc = type == "shredder" and 'Vernichtet alle Items im Schredder permanent!' or 'Entsorgt alle Items in der Mülltonne (automatische Löschung nach Zeit)!' table.insert(menuOptions, { title = '🔥 ' .. actionText, description = actionDesc, icon = type == "shredder" and 'fire' or 'trash', onSelect = function() confirmDestroyAll(containerID, type) end }) -- If it's a trash bin with scheduled deletion, show the time remaining if type == "trash" and timeRemaining then local minutes = math.floor(timeRemaining / 60) local seconds = timeRemaining % 60 table.insert(menuOptions, { title = '⏱️ Automatische Leerung', description = string.format('In %d Minuten und %d Sekunden', minutes, seconds), disabled = true }) end table.insert(menuOptions, { title = '─────────────────', description = 'Einzelne Items:', disabled = true }) -- Add individual items to menu local hasItems = false for slot, item in pairs(items) do if item and item.amount and item.amount > 0 then hasItems = true table.insert(menuOptions, { title = (item.label or item.name), description = 'Anzahl: ' .. item.amount .. ' | Slot: ' .. slot, icon = 'trash', onSelect = function() confirmDestroySingle(item.name, item.amount, slot, containerID, type) end }) end end if not hasItems then lib.notify({ title = type == "shredder" and 'Müllschredder' or 'Mülltonne', description = type == "shredder" and 'Der Schredder ist leer!' or 'Die Mülltonne ist leer!', type = 'error' }) return end lib.registerContext({ id = 'disposal_menu', title = type == "shredder" and '🗑️ Müllschredder Verwaltung' or '🗑️ Mülltonne Verwaltung', options = menuOptions }) lib.showContext('disposal_menu') end) -- Confirm single item disposal function confirmDestroySingle(itemName, amount, slot, containerID, type) local actionText = type == "shredder" and "vernichten" or "entsorgen" local actionDesc = type == "shredder" and (itemName .. ' (' .. amount .. 'x) wird permanent gelöscht!') or (itemName .. ' (' .. amount .. 'x) wird entsorgt und nach Zeit gelöscht!') lib.registerContext({ id = 'dispose_single_confirm', title = '⚠️ Item ' .. actionText .. '?', options = { { title = type == "shredder" and '🔥 Ja, vernichten' or '🗑️ Ja, entsorgen', description = actionDesc, icon = 'check', onSelect = function() TriggerServerEvent('disposal:server:disposeSingle', itemName, amount, slot, containerID, type) end }, { title = '❌ Abbrechen', description = 'Zurück zum Hauptmenü', icon = 'times', onSelect = function() TriggerServerEvent('disposal:server:getItems', containerID, type) end } } }) lib.showContext('dispose_single_confirm') end -- Confirm all items disposal function confirmDestroyAll(containerID, type) local actionText = type == "shredder" and "VERNICHTEN" or "ENTSORGEN" local actionDesc = type == "shredder" and 'ALLE Items im Schredder werden permanent gelöscht!' or 'ALLE Items in der Mülltonne werden nach Zeit automatisch gelöscht!' lib.registerContext({ id = 'dispose_all_confirm', title = '⚠️ WARNUNG ⚠️', options = { { title = type == "shredder" and '🔥 JA, ALLES VERNICHTEN' or '🗑️ JA, ALLES ENTSORGEN', description = actionDesc, icon = type == "shredder" and 'fire' or 'trash', onSelect = function() TriggerServerEvent('disposal:server:disposeAll', containerID, type) end }, { title = '❌ Abbrechen', description = 'Zurück zum Hauptmenü', icon = 'times', onSelect = function() TriggerServerEvent('disposal:server:getItems', containerID, type) end } } }) lib.showContext('dispose_all_confirm') end -- Success notification with effect RegisterNetEvent('disposal:client:itemDisposed', function(message, type) lib.notify({ title = type == "shredder" and 'Müllschredder' or 'Mülltonne', description = message, type = 'success', duration = 4000 }) -- Particle effect local playerPed = PlayerPedId() local coords = GetEntityCoords(playerPed) RequestNamedPtfxAsset("core") while not HasNamedPtfxAssetLoaded("core") do Wait(1) end UseParticleFxAssetNextCall("core") -- Different effects for shredder vs trash if type == "shredder" then -- More intense effect for shredder StartParticleFxNonLoopedAtCoord("ent_sht_flame", coords.x, coords.y, coords.z + 1.0, 0.0, 0.0, 0.0, 1.0, false, false, false) PlaySoundFrontend(-1, "CHECKPOINT_PERFECT", "HUD_MINI_GAME_SOUNDSET", 1) else -- Subtle effect for trash StartParticleFxNonLoopedAtCoord("ent_sht_dust", coords.x, coords.y, coords.z + 0.5, 0.0, 0.0, 0.0, 1.0, false, false, false) PlaySoundFrontend(-1, "PICK_UP", "HUD_FRONTEND_DEFAULT_SOUNDSET", 1) end end)