first commit

This commit is contained in:
Colin-Joel Scupin
2026-08-05 01:29:39 +02:00
commit 86d4bcdc37
85 changed files with 7706 additions and 0 deletions
+385
View File
@@ -0,0 +1,385 @@
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)
@@ -0,0 +1,10 @@
fx_version 'cerulean'
game 'gta5'
author 'Colin-Joel / DevX Studios'
description 'DevX RP bank robbery with preparation missions'
version '0.4.3'
lua54 'yes'
shared_scripts { '@devx_core/shared/config.lua', 'shared/config.lua' }
client_scripts { '@devx_core/client/callbacks.lua', 'client/main.lua' }
server_scripts { '@oxmysql/lib/MySQL.lua', '@devx_core/server/callbacks.lua', 'server/main.lua' }
dependencies { 'devx_core', 'devx_banking', 'devx_ipl' }
+165
View File
@@ -0,0 +1,165 @@
local activeRobberies = {}
local prepSessions = {}
local globalCooldownUntil = 0
local function nearLocation(source, coords, maximum)
local ped = GetPlayerPed(source)
if not ped or ped <= 0 then return false end
local playerCoords = GetEntityCoords(ped)
return #(playerCoords - vector3(coords.x, coords.y, coords.z)) <= maximum
end
local function findPrep(key)
for _, entry in ipairs(DevXCrimeConfig.PrepLocations) do
if entry.key == key then return entry end
end
return nil
end
local function getProgress(characterId)
local row = MySQL.single.await([[
SELECT drill_done AS drillDone, card_done AS cardDone, vehicle_done AS vehicleDone
FROM devx_heist_progress WHERE character_id = ? LIMIT 1
]], { characterId })
if not row then
MySQL.insert.await('INSERT IGNORE INTO devx_heist_progress (character_id) VALUES (?)', { characterId })
row = { drillDone = 0, cardDone = 0, vehicleDone = 0 }
end
return {
drill = tonumber(row.drillDone) == 1,
card = tonumber(row.cardDone) == 1,
vehicle = tonumber(row.vehicleDone) == 1
}
end
local function leavePrep(source)
prepSessions[source] = nil
SetPlayerRoutingBucket(source, 0)
end
MySQL.ready(function()
MySQL.query.await([[
CREATE TABLE IF NOT EXISTS devx_heist_progress (
character_id BIGINT UNSIGNED NOT NULL,
drill_done TINYINT(1) NOT NULL DEFAULT 0,
card_done TINYINT(1) NOT NULL DEFAULT 0,
vehicle_done TINYINT(1) NOT NULL DEFAULT 0,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (character_id),
CONSTRAINT fk_devx_heist_character FOREIGN KEY (character_id)
REFERENCES devx_characters (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
]])
end)
DevXRegisterCallback('crime:status', function(source)
local player = exports.devx_core:GetPlayer(source)
if not player then return { ok = false, error = 'Kein Charakter aktiv.' } end
return {
ok = true,
progress = getProgress(player.characterId),
cooldownSeconds = math.max(0, globalCooldownUntil - os.time()),
active = activeRobberies[source] ~= nil
}
end)
DevXRegisterCallback('crime:enterPrep', function(source, payload)
local player = exports.devx_core:GetPlayer(source)
local prep = findPrep(tostring(payload.key or ''))
if not player or not prep then return { ok = false, error = 'Ungültige Vorbereitungsmission.' } end
if not nearLocation(source, prep.exterior, 6.0) then return { ok = false, error = 'Du bist nicht am Lagereingang.' } end
local bucket = DevXCrimeConfig.MissionBucketBase + tonumber(source)
prepSessions[source] = { key = prep.key, exterior = prep.exterior, bucket = bucket }
SetRoutingBucketPopulationEnabled(bucket, false)
SetRoutingBucketEntityLockdownMode(bucket, 'relaxed')
SetPlayerRoutingBucket(source, bucket)
return { ok = true, interior = DevXCrimeConfig.PrepInterior, prep = prep }
end)
DevXRegisterCallback('crime:leavePrep', function(source)
local session = prepSessions[source]
if not session then return { ok = false, error = 'Keine Vorbereitungsmission aktiv.' } end
local exterior = session.exterior
leavePrep(source)
return { ok = true, exterior = exterior }
end)
DevXRegisterCallback('crime:completePrep', function(source, payload)
local player = exports.devx_core:GetPlayer(source)
local key = tostring(payload.key or '')
local prep = findPrep(key)
local session = prepSessions[source]
if not player or not prep or not session or session.key ~= key then
return { ok = false, error = 'Keine passende Vorbereitungsmission aktiv.' }
end
if not nearLocation(source, prep.objective, 5.0) then return { ok = false, error = 'Du bist nicht am Missionsziel.' } end
local column = ({ drill = 'drill_done', card = 'card_done', vehicle = 'vehicle_done' })[key]
MySQL.query.await('INSERT IGNORE INTO devx_heist_progress (character_id) VALUES (?)', { player.characterId })
MySQL.update.await(('UPDATE devx_heist_progress SET %s = 1 WHERE character_id = ?'):format(column), { player.characterId })
local exterior = session.exterior
leavePrep(source)
return { ok = true, progress = getProgress(player.characterId), exterior = exterior }
end)
DevXRegisterCallback('crime:startRobbery', function(source)
local player = exports.devx_core:GetPlayer(source)
if not player then return { ok = false, error = 'Kein Charakter aktiv.' } end
if not nearLocation(source, DevXCrimeConfig.Bank, 6.0) then return { ok = false, error = 'Du bist nicht im Tresorbereich.' } end
if activeRobberies[source] then return { ok = false, error = 'Der Raub läuft bereits.' } end
if os.time() < globalCooldownUntil then
return { ok = false, error = ('Die Bank ist noch %s Minuten gesperrt.'):format(math.ceil((globalCooldownUntil - os.time()) / 60)) }
end
local progress = getProgress(player.characterId)
if not progress.drill or not progress.card or not progress.vehicle then
return { ok = false, error = 'Schließe zuerst alle drei Vorbereitungsmissionen ab.' }
end
local duration = DevXCrimeConfig.RobberyDurationSeconds
activeRobberies[source] = { characterId = player.characterId, completeAt = os.time() + duration }
globalCooldownUntil = os.time() + DevXCrimeConfig.GlobalCooldownSeconds
return { ok = true, duration = duration }
end)
DevXRegisterCallback('crime:finishRobbery', function(source)
local robbery = activeRobberies[source]
local player = exports.devx_core:GetPlayer(source)
if not robbery or not player or robbery.characterId ~= player.characterId then
return { ok = false, error = 'Kein aktiver Bankraub.' }
end
if os.time() < robbery.completeAt then return { ok = false, error = 'Der Tresor ist noch nicht geöffnet.' } end
if not nearLocation(source, DevXCrimeConfig.Bank, 12.0) then
activeRobberies[source] = nil
return { ok = false, error = 'Du hast den Bankraubbereich verlassen.' }
end
local reward = math.random(DevXCrimeConfig.MinimumReward, DevXCrimeConfig.MaximumReward)
exports.devx_core:AddCash(source, reward, 'Fleeca-Bankraub')
exports.devx_core:AddMetadataNumber(source, 'rpXp', 75)
MySQL.update.await([[
UPDATE devx_heist_progress
SET drill_done = 0, card_done = 0, vehicle_done = 0
WHERE character_id = ?
]], { player.characterId })
activeRobberies[source] = nil
return { ok = true, reward = reward, xp = 75 }
end)
AddEventHandler('playerDropped', function()
activeRobberies[source] = nil
leavePrep(source)
end)
AddEventHandler('onResourceStop', function(resourceName)
if resourceName ~= GetCurrentResourceName() then return end
for _, playerId in ipairs(GetPlayers()) do SetPlayerRoutingBucket(tonumber(playerId), 0) end
end)
RegisterCommand('resetheist', function(source)
if source ~= 0 and not IsPlayerAceAllowed(source, 'devx.admin') then return end
globalCooldownUntil = 0
activeRobberies = {}
end, false)
@@ -0,0 +1,90 @@
DevXCrimeConfig = {
MissionBucketBase = 20000,
-- v0.4.3 deliberately uses the always-available 10-car garage interior.
-- It needs no IPL and therefore cannot appear as an empty warehouse when a
-- DLC placement fails to stream. Mission props/NPCs create the warehouse set.
PrepInterior = {
ipls = {},
spawn = vector4(239.86, -1004.71, -99.00, 88.5),
exit = vector3(239.55, -1004.55, -99.00),
loadAt = vector3(229.96, -981.79, -99.66),
setDressing = {
{ model = 'prop_boxpile_07d', coords = vector4(233.60, -1003.00, -99.65, 90.0) },
{ model = 'prop_boxpile_06b', coords = vector4(233.55, -999.55, -99.65, 90.0) },
{ model = 'prop_tool_bench02', coords = vector4(224.25, -1001.50, -99.65, 270.0) },
{ model = 'prop_crate_11e', coords = vector4(225.90, -998.80, -99.65, 0.0) },
{ model = 'prop_generator_03b', coords = vector4(228.20, -997.30, -99.65, 180.0) },
{ model = 'prop_worklight_03b', coords = vector4(229.70, -1003.20, -99.65, 90.0) },
{ model = 'prop_shelves_02', coords = vector4(222.40, -998.10, -99.65, 270.0) },
{ model = 'prop_shelves_02', coords = vector4(222.40, -1001.20, -99.65, 270.0) },
{ model = 'prop_box_wood05a', coords = vector4(236.40, -998.20, -99.65, 0.0) },
{ model = 'prop_cratepile_07a', coords = vector4(236.25, -1001.60, -99.65, 180.0) },
{ model = 'prop_toolchest_05', coords = vector4(225.10, -996.65, -99.65, 90.0) },
{ model = 'prop_barrel_02a', coords = vector4(238.00, -995.80, -99.65, 0.0) },
{ model = 'prop_barrel_02a', coords = vector4(238.00, -994.90, -99.65, 0.0) },
{ model = 'prop_worklight_04b', coords = vector4(222.95, -1003.55, -99.65, 270.0) },
{ model = 'prop_folding_chair_01', coords = vector4(228.40, -1003.80, -99.65, 180.0) },
{ model = 'prop_table_03', coords = vector4(230.00, -1003.85, -99.65, 0.0) }
},
vehicles = {
{ model = 'burrito3', coords = vector4(235.25, -988.10, -99.45, 180.0) },
{ model = 'sultan', coords = vector4(229.80, -988.10, -99.45, 180.0) }
}
},
PlannerModel = 's_m_m_highsec_01',
PlannerName = 'Vincent',
PrepLocations = {
{
key = 'drill',
label = 'Bohrgerät am Hafen beschaffen',
exterior = vector4(1204.15, -3116.62, 5.54, 90.0),
planner = vector4(1202.40, -3115.15, 5.54, 178.0),
objective = vector4(224.45, -1001.20, -99.65, 270.0),
objectiveLabel = 'Industriebohrer von der Werkbank nehmen',
prop = 'prop_tool_drill',
dialogue = {
'Der Tresor braucht mehr als rohe Gewalt.',
'Unser Werkzeug liegt in einer abgeschirmten Garage.',
'Hol den Industriebohrer und verschwinde unauffällig.'
}
},
{
key = 'card',
label = 'Zugangskarte aus dem Lager holen',
exterior = vector4(707.31, -966.86, 30.41, 180.0),
planner = vector4(705.55, -966.10, 30.41, 260.0),
objective = vector4(226.10, -998.75, -99.65, 0.0),
objectiveLabel = 'Sicherheitskarte aus der Transportkiste nehmen',
prop = 'prop_cs_swipe_card',
dialogue = {
'Die Banktür prüft eine Mitarbeiterkarte.',
'Unser Kontakt hat eine Karte in einer Kiste deponiert.',
'Nimm sie und hinterlasse keine Spuren.'
}
},
{
key = 'vehicle',
label = 'Fluchtfahrzeug vorbereiten',
exterior = vector4(970.25, -1832.05, 31.11, 175.0),
planner = vector4(968.65, -1831.40, 31.11, 260.0),
objective = vector4(228.35, -997.15, -99.65, 180.0),
objectiveLabel = 'Fahrzeugsteuergerät am Laptop programmieren',
prop = 'prop_laptop_01a',
dialogue = {
'Ohne sauberes Fluchtfahrzeug endet der Plan an der ersten Kreuzung.',
'Programmiere das Steuergerät auf unsere Kennung.',
'Danach ist das Fahrzeug für das Finale vorbereitet.'
}
}
},
WarehouseWorkers = {
{ model = 's_m_m_dockwork_01', coords = vector4(232.20, -997.80, -99.65, 180.0), scenario = 'WORLD_HUMAN_CLIPBOARD' },
{ model = 's_m_y_dockwork_01', coords = vector4(225.10, -1003.35, -99.65, 270.0), scenario = 'WORLD_HUMAN_WELDING' }
},
Bank = vector3(146.74, -1044.82, 29.37),
RobberyDurationSeconds = 75,
GlobalCooldownSeconds = 3600,
MinimumReward = 4500,
MaximumReward = 8500
}