299 lines
9.2 KiB
Lua
299 lines
9.2 KiB
Lua
ESX = exports['es_extended']:getSharedObject()
|
|
|
|
local spawnedPeds = {}
|
|
local isNuiOpen = false
|
|
local currentStation = nil
|
|
|
|
-- =====================
|
|
-- DRAW TEXT 3D
|
|
-- =====================
|
|
|
|
function DrawText3D(x, y, z, text)
|
|
SetTextScale(0.35, 0.35)
|
|
SetTextFont(4)
|
|
SetTextProportional(1)
|
|
SetTextColour(255, 255, 255, 215)
|
|
SetTextEntry('STRING')
|
|
SetTextCentre(true)
|
|
AddTextComponentString(text)
|
|
SetDrawOrigin(x, y, z, 0)
|
|
DrawText(0.0, 0.0)
|
|
local factor = (string.len(text)) / 370
|
|
DrawRect(0.0, 0.0 + 0.0125, 0.017 + factor, 0.03, 0, 0, 0, 100)
|
|
ClearDrawOrigin()
|
|
end
|
|
|
|
-- =====================
|
|
-- NPC SPAWNING
|
|
-- =====================
|
|
|
|
Citizen.CreateThread(function()
|
|
for stationId, station in pairs(Config.CraftingStations) do
|
|
local npc = station.npc
|
|
local hash = GetHashKey(npc.model)
|
|
|
|
RequestModel(hash)
|
|
while not HasModelLoaded(hash) do
|
|
Citizen.Wait(10)
|
|
end
|
|
|
|
local ped = CreatePed(4, hash, npc.coords.x, npc.coords.y, npc.coords.z - 1.0, npc.coords.w, false, true)
|
|
SetEntityHeading(ped, npc.coords.w)
|
|
FreezeEntityPosition(ped, true)
|
|
SetEntityInvincible(ped, true)
|
|
SetBlockingOfNonTemporaryEvents(ped, true)
|
|
|
|
if npc.scenario then
|
|
TaskStartScenarioInPlace(ped, npc.scenario, 0, true)
|
|
end
|
|
|
|
spawnedPeds[stationId] = ped
|
|
SetModelAsNoLongerNeeded(hash)
|
|
end
|
|
end)
|
|
|
|
-- Cleanup on resource stop
|
|
AddEventHandler('onResourceStop', function(resourceName)
|
|
if resourceName ~= GetCurrentResourceName() then return end
|
|
for _, ped in pairs(spawnedPeds) do
|
|
if DoesEntityExist(ped) then
|
|
DeleteEntity(ped)
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- =====================
|
|
-- NPC INTERACTION
|
|
-- =====================
|
|
|
|
Citizen.CreateThread(function()
|
|
while true do
|
|
local sleep = 1000
|
|
local playerCoords = GetEntityCoords(PlayerPedId())
|
|
local closestStation = nil
|
|
local closestDist = Config.InteractionDistance + 1
|
|
|
|
for stationId, station in pairs(Config.CraftingStations) do
|
|
local dist = #(playerCoords - vector3(station.npc.coords.x, station.npc.coords.y, station.npc.coords.z))
|
|
if dist < closestDist then
|
|
closestDist = dist
|
|
closestStation = stationId
|
|
end
|
|
end
|
|
|
|
if closestStation and closestDist <= Config.InteractionDistance then
|
|
sleep = 0
|
|
local station = Config.CraftingStations[closestStation]
|
|
|
|
if not isNuiOpen then
|
|
-- Draw 3D text above NPC instead of spamming notifications
|
|
local npcCoords = station.npc.coords
|
|
DrawText3D(npcCoords.x, npcCoords.y, npcCoords.z + 1.0, '[E] ' .. station.label)
|
|
|
|
if IsControlJustReleased(0, 38) then -- E key
|
|
OpenCraftingMenu(closestStation)
|
|
end
|
|
end
|
|
end
|
|
|
|
Citizen.Wait(sleep)
|
|
end
|
|
end)
|
|
|
|
-- =====================
|
|
-- VEHICLE DETECTION
|
|
-- =====================
|
|
|
|
function GetOwnedVehiclesNearby(cb)
|
|
local playerCoords = GetEntityCoords(PlayerPedId())
|
|
local vehicles = GetGamePool('CVehicle')
|
|
local nearbyOwned = {}
|
|
local checksRemaining = 0
|
|
|
|
for _, vehicle in ipairs(vehicles) do
|
|
local vehCoords = GetEntityCoords(vehicle)
|
|
local dist = #(playerCoords - vehCoords)
|
|
|
|
if dist <= Config.VehicleSearchRadius then
|
|
local plate = string.gsub(GetVehicleNumberPlateText(vehicle), '%s+', '')
|
|
checksRemaining = checksRemaining + 1
|
|
|
|
ESX.TriggerServerCallback('mercyv-crafting:checkVehicleOwnership', function(isOwned)
|
|
if isOwned then
|
|
nearbyOwned[#nearbyOwned + 1] = {
|
|
vehicle = vehicle,
|
|
plate = plate,
|
|
model = GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)),
|
|
}
|
|
end
|
|
checksRemaining = checksRemaining - 1
|
|
if checksRemaining <= 0 then
|
|
cb(nearbyOwned)
|
|
end
|
|
end, plate)
|
|
end
|
|
end
|
|
|
|
if checksRemaining == 0 then
|
|
cb(nearbyOwned)
|
|
end
|
|
end
|
|
|
|
-- =====================
|
|
-- NUI MANAGEMENT
|
|
-- =====================
|
|
|
|
function OpenCraftingMenu(stationId)
|
|
if isNuiOpen then return end
|
|
|
|
currentStation = stationId
|
|
local station = Config.CraftingStations[stationId]
|
|
|
|
-- Get station recipes
|
|
local recipes = {}
|
|
for _, recipeId in ipairs(station.recipes) do
|
|
local recipe = Config.Recipes[recipeId]
|
|
if recipe then
|
|
recipes[#recipes + 1] = {
|
|
id = recipeId,
|
|
label = recipe.label,
|
|
category = recipe.category,
|
|
result = recipe.result,
|
|
ingredients = recipe.ingredients,
|
|
duration = recipe.duration,
|
|
}
|
|
end
|
|
end
|
|
|
|
-- Get player inventory
|
|
ESX.TriggerServerCallback('mercyv-crafting:getPlayerItems', function(playerItems)
|
|
-- Get nearby owned vehicles and their trunk items
|
|
GetOwnedVehiclesNearby(function(vehicles)
|
|
local vehicleData = {}
|
|
|
|
if #vehicles > 0 then
|
|
local trunkChecks = #vehicles
|
|
for _, veh in ipairs(vehicles) do
|
|
ESX.TriggerServerCallback('mercyv-crafting:getTrunkItems', function(trunkItems)
|
|
vehicleData[#vehicleData + 1] = {
|
|
plate = veh.plate,
|
|
model = veh.model,
|
|
items = trunkItems,
|
|
}
|
|
trunkChecks = trunkChecks - 1
|
|
if trunkChecks <= 0 then
|
|
SendCraftingData(station, recipes, playerItems, vehicleData)
|
|
end
|
|
end, veh.plate)
|
|
end
|
|
else
|
|
SendCraftingData(station, recipes, playerItems, vehicleData)
|
|
end
|
|
end)
|
|
end)
|
|
end
|
|
|
|
function SendCraftingData(station, recipes, playerItems, vehicleData)
|
|
isNuiOpen = true
|
|
SetNuiFocus(true, true)
|
|
|
|
SendNUIMessage({
|
|
type = 'open',
|
|
stationLabel = station.label,
|
|
stationImage = station.image or '',
|
|
stationColor = station.color or '',
|
|
recipes = recipes,
|
|
playerItems = playerItems,
|
|
vehicles = vehicleData,
|
|
})
|
|
|
|
-- Check if there are active crafts running at this station
|
|
ESX.TriggerServerCallback('mercyv-crafting:getCraftStatus', function(statuses)
|
|
if statuses then
|
|
SendNUIMessage({
|
|
type = 'craftStatuses',
|
|
statuses = statuses,
|
|
})
|
|
end
|
|
end)
|
|
end
|
|
|
|
function CloseCraftingMenu()
|
|
if not isNuiOpen then return end
|
|
isNuiOpen = false
|
|
currentStation = nil
|
|
SetNuiFocus(false, false)
|
|
SendNUIMessage({ type = 'close' })
|
|
end
|
|
|
|
-- =====================
|
|
-- NUI CALLBACKS
|
|
-- =====================
|
|
|
|
RegisterNUICallback('close', function(_, cb)
|
|
CloseCraftingMenu()
|
|
cb('ok')
|
|
end)
|
|
|
|
RegisterNUICallback('startCraft', function(data, cb)
|
|
if not currentStation then
|
|
cb({ success = false, message = 'Keine Station ausgewaehlt' })
|
|
return
|
|
end
|
|
|
|
TriggerServerEvent('mercyv-crafting:startCraft', data.recipeId, currentStation, data.useSource, data.craftAmount or 1)
|
|
cb('ok')
|
|
end)
|
|
|
|
RegisterNUICallback('collectCraft', function(data, cb)
|
|
TriggerServerEvent('mercyv-crafting:collectCraft', data.recipeId, data.collectTo, data.collectAmount or 1)
|
|
cb('ok')
|
|
end)
|
|
|
|
-- =====================
|
|
-- SERVER EVENT HANDLERS
|
|
-- =====================
|
|
|
|
RegisterNetEvent('mercyv-crafting:craftResult')
|
|
AddEventHandler('mercyv-crafting:craftResult', function(success, message)
|
|
if success then
|
|
TriggerEvent('hex_4_hud:notify', 'Crafting', message, 'success', 3000)
|
|
else
|
|
TriggerEvent('hex_4_hud:notify', 'Crafting', message, 'error', 3000)
|
|
SendNUIMessage({ type = 'craftingFailed', message = message })
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent('mercyv-crafting:craftStatusUpdate')
|
|
AddEventHandler('mercyv-crafting:craftStatusUpdate', function(status)
|
|
SendNUIMessage({ type = 'craftStatusUpdate', status = status })
|
|
end)
|
|
|
|
RegisterNetEvent('mercyv-crafting:collectResult')
|
|
AddEventHandler('mercyv-crafting:collectResult', function(success, message, recipeId)
|
|
if success then
|
|
TriggerEvent('hex_4_hud:notify', 'Crafting', message, 'success', 3000)
|
|
SendNUIMessage({ type = 'collectSuccess', recipeId = recipeId })
|
|
else
|
|
TriggerEvent('hex_4_hud:notify', 'Crafting', message, 'error', 3000)
|
|
end
|
|
end)
|
|
|
|
|
|
CreateThread(function()
|
|
for k, station in pairs(Config.CraftingStations) do
|
|
if station.blip and station.blip.enabled then
|
|
local blip = AddBlipForCoord(station.npc.coords.x, station.npc.coords.y, station.npc.coords.z)
|
|
|
|
SetBlipSprite(blip, station.blip.sprite or 1)
|
|
SetBlipDisplay(blip, 4)
|
|
SetBlipScale(blip, station.blip.scale or 1.0)
|
|
SetBlipColour(blip, station.blip.color or 1)
|
|
SetBlipAsShortRange(blip, true)
|
|
|
|
BeginTextCommandSetBlipName("STRING")
|
|
AddTextComponentString(station.label)
|
|
EndTextCommandSetBlipName(blip)
|
|
end
|
|
end
|
|
end) |