local progress = { drill = false, card = false, vehicle = false } local robberyRunning = false local activePrep = nil local objectiveProp = nil local plannerPeds = {} local warehousePeds = {} local missionProps = {} local missionVehicles = {} local cutsceneRunning = false local function notify(message) exports.devx_core:Notify(message) end local function help(message) BeginTextCommandDisplayHelp('STRING') AddTextComponentSubstringPlayerName(message) EndTextCommandDisplayHelp(0, false, true, -1) end local function subtitle(message, duration) BeginTextCommandPrint('STRING') AddTextComponentSubstringPlayerName(message) EndTextCommandPrint(duration or 3500, true) end local function drawMarkerAt(coords, red, green, blue) DrawMarker(1, coords.x, coords.y, coords.z - 1.0, 0.0,0.0,0.0,0.0,0.0,0.0, 1.2,1.2,0.35,red,green,blue,130,false,false,2,false,nil,nil,false) end local function loadModel(name) local hash = GetHashKey(name) RequestModel(hash) local deadline = GetGameTimer() + 7000 while not HasModelLoaded(hash) and GetGameTimer() < deadline do Wait(0) end return HasModelLoaded(hash) and hash or nil end local function loadAnim(dict) RequestAnimDict(dict) local deadline = GetGameTimer() + 5000 while not HasAnimDictLoaded(dict) and GetGameTimer() < deadline do Wait(0) end return HasAnimDictLoaded(dict) end local function teleport(coords) DoScreenFadeOut(250) local deadline = GetGameTimer() + 1500 while not IsScreenFadedOut() and GetGameTimer() < deadline do Wait(0) end local ped = PlayerPedId() FreezeEntityPosition(ped, true) if coords.z < -10.0 then SetFocusPosAndVel(coords.x, coords.y, coords.z, 0.0, 0.0, 0.0) end RequestCollisionAtCoord(coords.x, coords.y, coords.z) SetEntityCoordsNoOffset(ped, coords.x, coords.y, coords.z, false, false, false) SetEntityHeading(ped, coords.w or coords.heading or 0.0) local collisionDeadline = GetGameTimer() + 5000 while not HasCollisionLoadedAroundEntity(ped) and GetGameTimer() < collisionDeadline do RequestCollisionAtCoord(coords.x, coords.y, coords.z) Wait(0) end ClearPedTasksImmediately(ped) FreezeEntityPosition(ped, false) Wait(180) ClearFocus() DoScreenFadeIn(350) end local function ensurePrepInterior() local interior = DevXCrimeConfig.PrepInterior local position = interior.loadAt or interior.spawn SetFocusPosAndVel(position.x, position.y, position.z, 0.0, 0.0, 0.0) RequestCollisionAtCoord(position.x, position.y, position.z) RequestAdditionalCollisionAtCoord(position.x, position.y, position.z) NewLoadSceneStartSphere(position.x, position.y, position.z, 110.0, 0) local interiorId = GetInteriorAtCoords(position.x, position.y, position.z) if interiorId ~= 0 then PinInteriorInMemory(interiorId) RefreshInterior(interiorId) end local deadline = GetGameTimer() + 12000 local ready = false while GetGameTimer() < deadline do local sceneReady = IsNewLoadSceneLoaded() local interiorReady = interiorId == 0 or IsInteriorReady(interiorId) if sceneReady and interiorReady then ready = true break end RequestCollisionAtCoord(position.x, position.y, position.z) Wait(0) end NewLoadSceneStop() ClearFocus() if not ready then print(('[devx_crime] Sicheres Garagen-Interior nicht bereit. interior=%s'):format(interiorId)) end return ready end local function deletePeds(list) for _, ped in pairs(list) do if ped and DoesEntityExist(ped) then DeleteEntity(ped) end end for key in pairs(list) do list[key] = nil end end local function deleteProps(list) for _, object in pairs(list) do if object and DoesEntityExist(object) then DeleteEntity(object) end end for key in pairs(list) do list[key] = nil end end local function deleteVehicles(list) for _, vehicle in pairs(list) do if vehicle and DoesEntityExist(vehicle) then DeleteEntity(vehicle) end end for key in pairs(list) do list[key] = nil end end local function spawnMissionVehicles() deleteVehicles(missionVehicles) for index, entry in ipairs(DevXCrimeConfig.PrepInterior.vehicles or {}) do local model = loadModel(entry.model) if model then local c = entry.coords local vehicle = CreateVehicle(model, c.x, c.y, c.z, c.w or 0.0, false, false) SetVehicleOnGroundProperly(vehicle) SetVehicleDoorsLocked(vehicle, 2) SetEntityInvincible(vehicle, true) FreezeEntityPosition(vehicle, true) missionVehicles[index] = vehicle SetModelAsNoLongerNeeded(model) end end end local function spawnMissionSetDressing() deleteProps(missionProps) for index, entry in ipairs(DevXCrimeConfig.PrepInterior.setDressing or {}) do local model = loadModel(entry.model) if model then local c = entry.coords local object = CreateObject(model, c.x, c.y, c.z, false, false, false) SetEntityHeading(object, c.w or 0.0) FreezeEntityPosition(object, true) SetEntityCollision(object, true, true) missionProps[index] = object SetModelAsNoLongerNeeded(model) end end end local function spawnPlanners() deletePeds(plannerPeds) local model = loadModel(DevXCrimeConfig.PlannerModel) if not model then return end for _, prep in ipairs(DevXCrimeConfig.PrepLocations) do local c = prep.planner local ped = CreatePed(4, model, c.x, c.y, c.z - 1.0, c.w, false, false) SetEntityInvincible(ped, true) SetBlockingOfNonTemporaryEvents(ped, true) FreezeEntityPosition(ped, true) TaskStartScenarioInPlace(ped, 'WORLD_HUMAN_CLIPBOARD', 0, true) plannerPeds[prep.key] = ped end SetModelAsNoLongerNeeded(model) end local function spawnWarehousePeds() deletePeds(warehousePeds) for index, worker in ipairs(DevXCrimeConfig.WarehouseWorkers or {}) do local model = loadModel(worker.model) if model then local c = worker.coords local ped = CreatePed(4, model, c.x, c.y, c.z, c.w, false, false) SetEntityInvincible(ped, true) SetBlockingOfNonTemporaryEvents(ped, true) FreezeEntityPosition(ped, true) TaskStartScenarioInPlace(ped, worker.scenario or 'WORLD_HUMAN_CLIPBOARD', 0, true) warehousePeds[index] = ped SetModelAsNoLongerNeeded(model) end end end local function removeObjectiveProp() if objectiveProp and DoesEntityExist(objectiveProp) then DeleteEntity(objectiveProp) end objectiveProp = nil end local function spawnObjectiveProp(prep) removeObjectiveProp() local model = loadModel(prep.prop) if not model then return end objectiveProp = CreateObject(model, prep.objective.x, prep.objective.y, prep.objective.z + 0.05, false, false, false) SetEntityHeading(objectiveProp, prep.objective.w or 0.0) FreezeEntityPosition(objectiveProp, true) SetModelAsNoLongerNeeded(model) end local function timedAction(label, seconds) local endTime = GetGameTimer() + seconds * 1000 FreezeEntityPosition(PlayerPedId(), true) while GetGameTimer() < endTime do local left = math.ceil((endTime - GetGameTimer()) / 1000) help(('%s ~b~%ss'):format(label, left)) DisableAllControlActions(0) EnableControlAction(0, 200, true) Wait(0) end FreezeEntityPosition(PlayerPedId(), false) end local function refreshStatus() DevXTriggerCallback('crime:status', {}, function(result) if result.ok then progress = result.progress or progress end end) end local function leavePrep() if not activePrep then return end DevXTriggerCallback('crime:leavePrep', {}, function(result) if result.ok then teleport(result.exterior) TriggerEvent('devx_ipl:client:restoreWorld', 'crime-leave') end end) activePrep = nil removeObjectiveProp() deletePeds(warehousePeds) deleteProps(missionProps) deleteVehicles(missionVehicles) end local function runPlanningScene(prep, callback) if cutsceneRunning then return end cutsceneRunning = true local planner = plannerPeds[prep.key] if not planner or not DoesEntityExist(planner) then cutsceneRunning = false callback() return end local player = PlayerPedId() FreezeEntityPosition(player, true) ClearPedTasksImmediately(planner) TaskTurnPedToFaceEntity(player, planner, 1500) TaskTurnPedToFaceEntity(planner, player, 1500) Wait(1000) local playerCoords = GetEntityCoords(player) local plannerCoords = GetEntityCoords(planner) local midpoint = (playerCoords + plannerCoords) / 2.0 local cam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true) SetCamCoord(cam, midpoint.x + 2.4, midpoint.y + 2.4, midpoint.z + 1.45) PointCamAtCoord(cam, midpoint.x, midpoint.y, midpoint.z + 0.85) SetCamFov(cam, 43.0) SetCamActive(cam, true) RenderScriptCams(true, true, 600, true, true) if loadAnim('gestures@m@standing@casual') then for _, line in ipairs(prep.dialogue or {}) do TaskPlayAnim(planner, 'gestures@m@standing@casual', 'gesture_hand_down', 2.0, -2.0, 2800, 49, 0.0, false, false, false) subtitle(('~b~%s:~s~ %s'):format(DevXCrimeConfig.PlannerName, line), 3300) Wait(3400) end else for _, line in ipairs(prep.dialogue or {}) do subtitle(('~b~%s:~s~ %s'):format(DevXCrimeConfig.PlannerName, line), 3300) Wait(3400) end end RenderScriptCams(false, true, 550, true, true) DestroyCam(cam, false) FreezeEntityPosition(player, false) TaskStartScenarioInPlace(planner, 'WORLD_HUMAN_CLIPBOARD', 0, true) cutsceneRunning = false callback() end local function beginPrep(prep) runPlanningScene(prep, function() DevXTriggerCallback('crime:enterPrep', { key = prep.key }, function(result) if not result.ok then notify(result.error); return end if not ensurePrepInterior() then notify('Das Missionslager konnte nicht geladen werden. Die Mission wurde sicher abgebrochen.') DevXTriggerCallback('crime:leavePrep', {}, function() end) return end activePrep = result.prep teleport(result.interior.spawn) spawnMissionSetDressing() spawnMissionVehicles() spawnObjectiveProp(activePrep) spawnWarehousePeds() subtitle(('~b~MISSION:~s~ %s'):format(activePrep.objectiveLabel), 4500) end) end) end CreateThread(function() ensurePrepInterior() while not exports.devx_core:IsPlayerLoaded() do Wait(500) end refreshStatus() spawnPlanners() for _, entry in ipairs(DevXCrimeConfig.PrepLocations) do local blip = AddBlipForCoord(entry.exterior.x, entry.exterior.y, entry.exterior.z) SetBlipSprite(blip,478);SetBlipScale(blip,.65);SetBlipColour(blip,47);SetBlipAsShortRange(blip,true) BeginTextCommandSetBlipName('STRING');AddTextComponentString('Bankraub-Vorbereitung');EndTextCommandSetBlipName(blip) end end) CreateThread(function() while true do local sleep = 700 if exports.devx_core:IsPlayerLoaded() and not robberyRunning and not cutsceneRunning then local coords = GetEntityCoords(PlayerPedId()) if activePrep then local objective = activePrep.objective local objectiveDistance = #(coords - vector3(objective.x, objective.y, objective.z)) local exit = DevXCrimeConfig.PrepInterior.exit local exitDistance = #(coords - exit) if objectiveDistance < 15.0 then sleep = 0;drawMarkerAt(objective,70,150,245) if objectiveDistance < 1.6 then help(('~INPUT_CONTEXT~ %s'):format(activePrep.objectiveLabel)) if IsControlJustReleased(0,38) then timedAction(activePrep.objectiveLabel,7) DevXTriggerCallback('crime:completePrep',{key=activePrep.key},function(result) if result.ok then progress=result.progress;removeObjectiveProp();deletePeds(warehousePeds);deleteProps(missionProps);deleteVehicles(missionVehicles);activePrep=nil;teleport(result.exterior);TriggerEvent('devx_ipl:client:restoreWorld', 'crime-complete') notify('Vorbereitungsmission abgeschlossen.') else notify(result.error or 'Mission fehlgeschlagen.') end end) end end end if exitDistance < 8.0 then sleep=0;drawMarkerAt(exit,230,230,230) if exitDistance < 1.6 then help('~INPUT_CONTEXT~ Lager verlassen') if IsControlJustReleased(0,38) then leavePrep() end end end else for _, entry in ipairs(DevXCrimeConfig.PrepLocations) do local distance = #(coords - vector3(entry.exterior.x,entry.exterior.y,entry.exterior.z)) if distance < 18.0 then sleep=0;drawMarkerAt(entry.exterior,progress[entry.key] and 70 or 230,progress[entry.key] and 190 or 150,90) if distance < 1.8 then if progress[entry.key] then help(('Vorbereitung abgeschlossen: %s'):format(entry.label)) else help(('~INPUT_CONTEXT~ mit %s planen: %s'):format(DevXCrimeConfig.PlannerName, entry.label)) if IsControlJustReleased(0,38) then beginPrep(entry) end end end end end local bankDistance=#(coords-DevXCrimeConfig.Bank) if bankDistance<18.0 then sleep=0;drawMarkerAt(DevXCrimeConfig.Bank,210,55,75) if bankDistance<1.8 then help('~INPUT_CONTEXT~ vorbereiteten Bankraub starten') if IsControlJustReleased(0,38) then DevXTriggerCallback('crime:startRobbery',{},function(result) if not result.ok then notify(result.error or 'Bankraub konnte nicht gestartet werden.');return end robberyRunning=true CreateThread(function() timedAction('Tresor wird geöffnet …',result.duration) DevXTriggerCallback('crime:finishRobbery',{},function(finish) robberyRunning=false if finish.ok then progress={drill=false,card=false,vehicle=false};notify(('Bankraub erfolgreich: %s $ und %s RP-XP.'):format(finish.reward,finish.xp)) else notify(finish.error or 'Bankraub fehlgeschlagen.') end end) end) end) end end end end end Wait(sleep) end end) AddEventHandler('onResourceStop',function(resourceName) if resourceName~=GetCurrentResourceName() then return end removeObjectiveProp();deletePeds(plannerPeds);deletePeds(warehousePeds);deleteProps(missionProps);deleteVehicles(missionVehicles);TriggerServerEvent('devx_core:server:returnToWorld');TriggerEvent('devx_ipl:client:restoreWorld', 'crime-stop') end)