first commit
This commit is contained in:
@@ -0,0 +1,678 @@
|
||||
local selectorOpen = false
|
||||
local selectorInitialized = false
|
||||
local previewPed = nil
|
||||
local previewCamera = nil
|
||||
local previewModel = nil
|
||||
local introRunning = false
|
||||
local introCamera = nil
|
||||
local introDriver = nil
|
||||
local introVehicle = nil
|
||||
|
||||
local function notify(message)
|
||||
BeginTextCommandThefeedPost('STRING')
|
||||
AddTextComponentSubstringPlayerName(message)
|
||||
EndTextCommandThefeedPostTicker(false, false)
|
||||
end
|
||||
|
||||
local function runNuiRpc(name, data, nuiCallback, afterResponse)
|
||||
local replied = false
|
||||
|
||||
local function reply(result)
|
||||
if replied then return end
|
||||
replied = true
|
||||
result = result or { ok = false, error = 'Leere Antwort vom Clientscript.' }
|
||||
if afterResponse then afterResponse(result) end
|
||||
nuiCallback(result)
|
||||
end
|
||||
|
||||
local ok, errorMessage = pcall(function()
|
||||
DevXTriggerCallback(name, data or {}, reply)
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
print(('[devx_characters] Client-RPC %s konnte nicht gestartet werden: %s'):format(
|
||||
name, tostring(errorMessage)
|
||||
))
|
||||
reply({ ok = false, error = ('Clientscriptfehler bei %s. Öffne F8.'):format(name) })
|
||||
return
|
||||
end
|
||||
|
||||
SetTimeout(24000, function()
|
||||
reply({ ok = false, error = ('Keine Antwort auf %s. Prüfe F8 und txAdmin.'):format(name) })
|
||||
end)
|
||||
end
|
||||
|
||||
local function loadModel(model)
|
||||
local hash = type(model) == 'number' and model or GetHashKey(model)
|
||||
if not IsModelInCdimage(hash) or not IsModelValid(hash) then return nil end
|
||||
|
||||
RequestModel(hash)
|
||||
local deadline = GetGameTimer() + 10000
|
||||
while not HasModelLoaded(hash) and GetGameTimer() < deadline do Wait(0) end
|
||||
return HasModelLoaded(hash) and hash or nil
|
||||
end
|
||||
|
||||
local function loadAnimDict(dict)
|
||||
RequestAnimDict(dict)
|
||||
local deadline = GetGameTimer() + 7000
|
||||
while not HasAnimDictLoaded(dict) and GetGameTimer() < deadline do Wait(0) end
|
||||
return HasAnimDictLoaded(dict)
|
||||
end
|
||||
|
||||
local function safeDeleteEntity(entity)
|
||||
if entity and DoesEntityExist(entity) then
|
||||
SetEntityAsMissionEntity(entity, true, true)
|
||||
DeleteEntity(entity)
|
||||
end
|
||||
end
|
||||
|
||||
local function applyBasicAppearance(ped, modelName)
|
||||
if not ped or not DoesEntityExist(ped) then return end
|
||||
|
||||
SetPedDefaultComponentVariation(ped)
|
||||
ClearAllPedProps(ped)
|
||||
|
||||
if modelName == 'mp_m_freemode_01' or modelName == 'mp_f_freemode_01' then
|
||||
SetPedHeadBlendData(ped, 0, 0, 0, 0, 0, 0, 0.5, 0.5, 0.0, false)
|
||||
SetPedComponentVariation(ped, 0, 0, 0, 2)
|
||||
SetPedHairColor(ped, 1, 1)
|
||||
SetPedEyeColor(ped, 2)
|
||||
end
|
||||
end
|
||||
|
||||
local function destroyPreviewScene()
|
||||
if previewCamera and DoesCamExist(previewCamera) then
|
||||
RenderScriptCams(false, true, 350, true, true)
|
||||
DestroyCam(previewCamera, false)
|
||||
end
|
||||
previewCamera = nil
|
||||
|
||||
safeDeleteEntity(previewPed)
|
||||
previewPed = nil
|
||||
previewModel = nil
|
||||
ClearFocus()
|
||||
end
|
||||
|
||||
local function createPreviewCharacter(modelName)
|
||||
if not selectorOpen then return end
|
||||
|
||||
modelName = modelName or 'mp_m_freemode_01'
|
||||
if previewPed and DoesEntityExist(previewPed) and previewModel == modelName then return end
|
||||
|
||||
local model = loadModel(modelName)
|
||||
if not model then
|
||||
modelName = 'mp_m_freemode_01'
|
||||
model = loadModel(modelName)
|
||||
end
|
||||
if not model then
|
||||
SendNUIMessage({ action = 'error', message = 'Vorschaumodell konnte nicht geladen werden.' })
|
||||
return
|
||||
end
|
||||
|
||||
safeDeleteEntity(previewPed)
|
||||
|
||||
local position = DevXCharacterConfig.Preview.pedPosition
|
||||
SetFocusPosAndVel(position.x, position.y, position.z, 0.0, 0.0, 0.0)
|
||||
|
||||
previewPed = CreatePed(4, model, position.x, position.y, position.z, position.heading, false, false)
|
||||
if not DoesEntityExist(previewPed) then
|
||||
SetModelAsNoLongerNeeded(model)
|
||||
SendNUIMessage({ action = 'error', message = 'Charaktervorschau konnte nicht erzeugt werden.' })
|
||||
return
|
||||
end
|
||||
|
||||
previewModel = modelName
|
||||
SetEntityInvincible(previewPed, true)
|
||||
SetEntityCollision(previewPed, false, false)
|
||||
FreezeEntityPosition(previewPed, true)
|
||||
SetBlockingOfNonTemporaryEvents(previewPed, true)
|
||||
SetPedCanRagdoll(previewPed, false)
|
||||
applyBasicAppearance(previewPed, modelName)
|
||||
|
||||
if loadAnimDict('anim@amb@business@bgen@bgen_no_work@') then
|
||||
TaskPlayAnim(previewPed, 'anim@amb@business@bgen@bgen_no_work@', 'stand_phone_phoneputdown_idle_nowork',
|
||||
3.0, -3.0, -1, 1, 0.0, false, false, false)
|
||||
end
|
||||
|
||||
local cameraPosition = DevXCharacterConfig.Preview.cameraPosition
|
||||
if not previewCamera or not DoesCamExist(previewCamera) then
|
||||
previewCamera = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
|
||||
SetCamCoord(previewCamera, cameraPosition.x, cameraPosition.y, cameraPosition.z)
|
||||
SetCamFov(previewCamera, DevXCharacterConfig.Preview.cameraFov or 32.0)
|
||||
SetCamActive(previewCamera, true)
|
||||
RenderScriptCams(true, true, 450, true, true)
|
||||
end
|
||||
|
||||
PointCamAtEntity(previewCamera, previewPed, 0.0, 0.0, 0.72, true)
|
||||
SetModelAsNoLongerNeeded(model)
|
||||
end
|
||||
|
||||
local function prepareSelectorScene()
|
||||
local stream = DevXCharacterConfig.Preview.streamPosition
|
||||
local ped = PlayerPedId()
|
||||
|
||||
SetEntityCoordsNoOffset(ped, stream.x, stream.y, stream.z, false, false, false)
|
||||
SetEntityHeading(ped, stream.heading)
|
||||
SetEntityVisible(ped, false, false)
|
||||
SetEntityCollision(ped, false, false)
|
||||
FreezeEntityPosition(ped, true)
|
||||
createPreviewCharacter('mp_m_freemode_01')
|
||||
end
|
||||
|
||||
local function setSelector(open)
|
||||
selectorOpen = open
|
||||
SetNuiFocus(open, open)
|
||||
SendNUIMessage({ action = open and 'open' or 'close' })
|
||||
|
||||
local ped = PlayerPedId()
|
||||
if open then
|
||||
DisplayRadar(false)
|
||||
prepareSelectorScene()
|
||||
else
|
||||
destroyPreviewScene()
|
||||
if DoesEntityExist(ped) then
|
||||
SetEntityVisible(ped, true, false)
|
||||
SetEntityCollision(ped, true, true)
|
||||
FreezeEntityPosition(ped, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function requestCharacters()
|
||||
DevXTriggerCallback('characters:list', {}, function(result)
|
||||
if not result.ok then
|
||||
SendNUIMessage({ action = 'error', message = result.error })
|
||||
return
|
||||
end
|
||||
|
||||
SendNUIMessage({
|
||||
action = 'characters',
|
||||
characters = result.characters,
|
||||
maxCharacters = result.maxCharacters
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
local function showDialogue(speaker, text)
|
||||
SendNUIMessage({ action = 'dialogue', speaker = speaker, text = text })
|
||||
end
|
||||
|
||||
local function hideDialogue()
|
||||
SendNUIMessage({ action = 'dialogueHide' })
|
||||
end
|
||||
|
||||
local function showSkipHelp()
|
||||
BeginTextCommandDisplayHelp('STRING')
|
||||
AddTextComponentSubstringPlayerName('Begrüßung überspringen: ~INPUT_JUMP~')
|
||||
EndTextCommandDisplayHelp(0, false, true, -1)
|
||||
end
|
||||
|
||||
local function introControls()
|
||||
showSkipHelp()
|
||||
DisableControlAction(0, 24, true)
|
||||
DisableControlAction(0, 25, true)
|
||||
DisableControlAction(0, 37, true)
|
||||
DisableControlAction(0, 75, true)
|
||||
return IsControlJustReleased(0, 22)
|
||||
end
|
||||
|
||||
local function waitIntro(milliseconds)
|
||||
local deadline = GetGameTimer() + milliseconds
|
||||
while introRunning and GetGameTimer() < deadline do
|
||||
if introControls() then return false end
|
||||
Wait(0)
|
||||
end
|
||||
return introRunning
|
||||
end
|
||||
|
||||
local function waitUntilIntro(predicate, timeoutMs)
|
||||
local deadline = GetGameTimer() + timeoutMs
|
||||
while introRunning and GetGameTimer() < deadline do
|
||||
if introControls() then return 'skip' end
|
||||
if predicate() then return 'ready' end
|
||||
Wait(0)
|
||||
end
|
||||
return 'timeout'
|
||||
end
|
||||
|
||||
local function setIntroCamera(position, target, fov, transition)
|
||||
if introCamera and DoesCamExist(introCamera) then
|
||||
DestroyCam(introCamera, false)
|
||||
end
|
||||
|
||||
introCamera = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
|
||||
SetCamCoord(introCamera, position.x, position.y, position.z)
|
||||
PointCamAtCoord(introCamera, target.x, target.y, target.z)
|
||||
SetCamFov(introCamera, fov or 48.0)
|
||||
SetCamActive(introCamera, true)
|
||||
RenderScriptCams(true, transition == true, transition and 700 or 0, true, true)
|
||||
end
|
||||
|
||||
local function resolveRoadPosition(point)
|
||||
local ok, found, node, heading = pcall(function()
|
||||
local success, position, roadHeading = GetClosestVehicleNodeWithHeading(
|
||||
point.x, point.y, point.z, 1, 3.0, 0
|
||||
)
|
||||
return success, position, roadHeading
|
||||
end)
|
||||
|
||||
if ok and found and node then
|
||||
return { x = node.x, y = node.y, z = node.z, heading = heading or point.heading }
|
||||
end
|
||||
return point
|
||||
end
|
||||
|
||||
local function playGesture(ped, animation)
|
||||
local dict = 'gestures@m@standing@casual'
|
||||
if not loadAnimDict(dict) then return end
|
||||
TaskPlayAnim(ped, dict, animation, 4.0, -4.0, 2600, 48, 0.0, false, false, false)
|
||||
end
|
||||
|
||||
local function walkTo(ped, target, timeoutMs)
|
||||
TaskFollowNavMeshToCoord(ped, target.x, target.y, target.z, 1.0, timeoutMs, 0.3, false, target.heading or 0.0)
|
||||
local result = waitUntilIntro(function()
|
||||
return #(GetEntityCoords(ped) - vector3(target.x, target.y, target.z)) < 1.4
|
||||
end, timeoutMs)
|
||||
|
||||
if result == 'timeout' and DoesEntityExist(ped) then
|
||||
SetEntityCoordsNoOffset(ped, target.x, target.y, target.z, false, false, false)
|
||||
SetEntityHeading(ped, target.heading or GetEntityHeading(ped))
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function finishWelcomeIntro(markCompleted)
|
||||
introRunning = false
|
||||
hideDialogue()
|
||||
|
||||
DoScreenFadeOut(350)
|
||||
local fadeDeadline = GetGameTimer() + 1400
|
||||
while not IsScreenFadedOut() and GetGameTimer() < fadeDeadline do Wait(0) end
|
||||
|
||||
if introCamera and DoesCamExist(introCamera) then
|
||||
RenderScriptCams(false, false, 0, true, true)
|
||||
DestroyCam(introCamera, false)
|
||||
end
|
||||
introCamera = nil
|
||||
|
||||
local playerPed = PlayerPedId()
|
||||
local finalSpawn = DevXCharacterConfig.Intro.finalSpawn
|
||||
SetEntityCoordsNoOffset(playerPed, finalSpawn.x, finalSpawn.y, finalSpawn.z, false, false, false)
|
||||
SetEntityHeading(playerPed, finalSpawn.heading)
|
||||
SetEntityVisible(playerPed, true, false)
|
||||
SetEntityCollision(playerPed, true, true)
|
||||
SetEntityInvincible(playerPed, false)
|
||||
FreezeEntityPosition(playerPed, false)
|
||||
ClearPedTasksImmediately(playerPed)
|
||||
|
||||
safeDeleteEntity(introDriver)
|
||||
safeDeleteEntity(introVehicle)
|
||||
introDriver = nil
|
||||
introVehicle = nil
|
||||
|
||||
DisplayRadar(true)
|
||||
ClearFocus()
|
||||
TriggerEvent('devx_hud:client:setVisible', true)
|
||||
TriggerServerEvent('devx_core:server:updatePosition', finalSpawn)
|
||||
|
||||
if markCompleted then
|
||||
DevXTriggerCallback('characters:completeIntro', {}, function(result)
|
||||
if not result.ok then
|
||||
print(('[devx_characters] Introstatus konnte nicht gespeichert werden: %s'):format(
|
||||
tostring(result.error)
|
||||
))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
DoScreenFadeIn(600)
|
||||
notify('Willkommen in DevX-City. Dein neues Leben beginnt jetzt.')
|
||||
end
|
||||
|
||||
local function startFollowCamera(ped)
|
||||
local config = DevXCharacterConfig.Intro.followCamera
|
||||
if introCamera and DoesCamExist(introCamera) then
|
||||
DestroyCam(introCamera, false)
|
||||
end
|
||||
|
||||
introCamera = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
|
||||
AttachCamToEntity(
|
||||
introCamera,
|
||||
ped,
|
||||
config.offset.x, config.offset.y, config.offset.z,
|
||||
true
|
||||
)
|
||||
PointCamAtEntity(
|
||||
introCamera,
|
||||
ped,
|
||||
config.lookAtOffset.x, config.lookAtOffset.y, config.lookAtOffset.z,
|
||||
true
|
||||
)
|
||||
SetCamFov(introCamera, config.fov or 48.0)
|
||||
SetCamActive(introCamera, true)
|
||||
RenderScriptCams(true, true, 600, true, true)
|
||||
end
|
||||
|
||||
local function parkIntroVehicle(vehicle, position)
|
||||
SetEntityCoordsNoOffset(vehicle, position.x, position.y, position.z, false, false, false)
|
||||
SetEntityHeading(vehicle, position.heading)
|
||||
SetVehicleOnGroundProperly(vehicle)
|
||||
SetVehicleEngineOn(vehicle, false, true, false)
|
||||
SetVehicleHandbrake(vehicle, true)
|
||||
FreezeEntityPosition(vehicle, true)
|
||||
end
|
||||
|
||||
local function startWelcomeIntro()
|
||||
if introRunning then return end
|
||||
introRunning = true
|
||||
|
||||
local config = DevXCharacterConfig.Intro
|
||||
local playerPed = PlayerPedId()
|
||||
local driverModel = loadModel(config.driverModel)
|
||||
local vehicleModel = loadModel(config.vehicleModel)
|
||||
|
||||
if not driverModel or not vehicleModel then
|
||||
finishWelcomeIntro(true)
|
||||
return
|
||||
end
|
||||
|
||||
TriggerEvent('devx_hud:client:setVisible', false)
|
||||
SetNuiFocus(false, false)
|
||||
DisplayRadar(false)
|
||||
SetEntityInvincible(playerPed, true)
|
||||
FreezeEntityPosition(playerPed, false)
|
||||
|
||||
local parked = config.vehicleParked
|
||||
introVehicle = CreateVehicle(
|
||||
vehicleModel, parked.x, parked.y, parked.z,
|
||||
parked.heading, false, false
|
||||
)
|
||||
introDriver = CreatePed(
|
||||
4, driverModel,
|
||||
config.driverWait.x, config.driverWait.y, config.driverWait.z,
|
||||
config.driverWait.heading, false, false
|
||||
)
|
||||
|
||||
SetModelAsNoLongerNeeded(driverModel)
|
||||
SetModelAsNoLongerNeeded(vehicleModel)
|
||||
|
||||
if not DoesEntityExist(introVehicle) or not DoesEntityExist(introDriver) then
|
||||
finishWelcomeIntro(true)
|
||||
return
|
||||
end
|
||||
|
||||
SetEntityAsMissionEntity(introVehicle, true, true)
|
||||
SetEntityAsMissionEntity(introDriver, true, true)
|
||||
SetEntityInvincible(introVehicle, true)
|
||||
SetEntityInvincible(introDriver, true)
|
||||
SetBlockingOfNonTemporaryEvents(introDriver, true)
|
||||
SetPedCanRagdoll(introDriver, false)
|
||||
SetVehicleDoorsLocked(introVehicle, 1)
|
||||
parkIntroVehicle(introVehicle, parked)
|
||||
|
||||
startFollowCamera(playerPed)
|
||||
|
||||
DoScreenFadeIn(700)
|
||||
if not waitIntro(900) then finishWelcomeIntro(true) return end
|
||||
|
||||
TaskFollowNavMeshToCoord(
|
||||
playerPed,
|
||||
config.terminalExit.x, config.terminalExit.y, config.terminalExit.z,
|
||||
1.0, 17000, 0.2, false, config.terminalExit.heading
|
||||
)
|
||||
|
||||
local walkResult = waitUntilIntro(function()
|
||||
return #(GetEntityCoords(playerPed) - vector3(
|
||||
config.terminalExit.x, config.terminalExit.y, config.terminalExit.z
|
||||
)) < 1.5
|
||||
end, 17000)
|
||||
if walkResult == 'skip' then finishWelcomeIntro(true) return end
|
||||
if walkResult == 'timeout' then
|
||||
SetEntityCoordsNoOffset(
|
||||
playerPed,
|
||||
config.terminalExit.x, config.terminalExit.y, config.terminalExit.z,
|
||||
false, false, false
|
||||
)
|
||||
SetEntityHeading(playerPed, config.terminalExit.heading)
|
||||
end
|
||||
|
||||
local playerWalk = walkTo(playerPed, config.meetingPoint, 9000)
|
||||
if playerWalk == 'skip' then finishWelcomeIntro(true) return end
|
||||
|
||||
TaskGoStraightToCoord(
|
||||
introDriver,
|
||||
config.meetingPoint.x + 1.30,
|
||||
config.meetingPoint.y - 0.10,
|
||||
config.meetingPoint.z,
|
||||
1.0, 7000, config.meetingPoint.heading + 180.0, 0.15
|
||||
)
|
||||
local driverResult = waitUntilIntro(function()
|
||||
return #(GetEntityCoords(introDriver) - vector3(
|
||||
config.meetingPoint.x + 1.30,
|
||||
config.meetingPoint.y - 0.10,
|
||||
config.meetingPoint.z
|
||||
)) < 1.4
|
||||
end, 7000)
|
||||
if driverResult == 'skip' then finishWelcomeIntro(true) return end
|
||||
if driverResult == 'timeout' then
|
||||
SetEntityCoordsNoOffset(
|
||||
introDriver,
|
||||
config.meetingPoint.x + 1.30,
|
||||
config.meetingPoint.y - 0.10,
|
||||
config.meetingPoint.z,
|
||||
false, false, false
|
||||
)
|
||||
end
|
||||
|
||||
local greetingCamera = config.greetingCamera
|
||||
setIntroCamera(greetingCamera, {
|
||||
x = config.meetingPoint.x,
|
||||
y = config.meetingPoint.y,
|
||||
z = config.meetingPoint.z + 0.90
|
||||
}, 43.0, true)
|
||||
|
||||
TaskTurnPedToFaceEntity(playerPed, introDriver, 1200)
|
||||
TaskTurnPedToFaceEntity(introDriver, playerPed, 1200)
|
||||
if not waitIntro(1200) then finishWelcomeIntro(true) return end
|
||||
|
||||
PlayAmbientSpeech1(introDriver, 'GENERIC_HI', 'SPEECH_PARAMS_FORCE')
|
||||
playGesture(introDriver, 'gesture_hello')
|
||||
showDialogue(config.driverName, 'Hey! Du musst neu hier sein. Willkommen in DevX-City.')
|
||||
if not waitIntro(4300) then finishWelcomeIntro(true) return end
|
||||
|
||||
playGesture(introDriver, 'gesture_hand_right')
|
||||
showDialogue(config.driverName, 'Ich bin Mason. Mein Wagen steht schon bereit – ich bringe dich direkt ins Zentrum.')
|
||||
if not waitIntro(5000) then finishWelcomeIntro(true) return end
|
||||
|
||||
playGesture(playerPed, 'gesture_nod_yes_hard')
|
||||
showDialogue('Du', 'Danke. Ich kenne mich hier noch überhaupt nicht aus.')
|
||||
if not waitIntro(3700) then finishWelcomeIntro(true) return end
|
||||
|
||||
playGesture(introDriver, 'gesture_point')
|
||||
showDialogue(config.driverName, 'Dann komm. Unterwegs erkläre ich dir die wichtigsten Dinge.')
|
||||
if not waitIntro(4100) then finishWelcomeIntro(true) return end
|
||||
hideDialogue()
|
||||
|
||||
ClearPedTasks(playerPed)
|
||||
ClearPedTasks(introDriver)
|
||||
FreezeEntityPosition(introVehicle, false)
|
||||
SetVehicleHandbrake(introVehicle, false)
|
||||
SetVehicleEngineOn(introVehicle, true, true, false)
|
||||
SetVehicleDoorsLocked(introVehicle, 1)
|
||||
|
||||
TaskEnterVehicle(introDriver, introVehicle, 12000, -1, 1.0, 1, 0)
|
||||
TaskEnterVehicle(playerPed, introVehicle, 12000, 0, 1.0, 1, 0)
|
||||
|
||||
local enterResult = waitUntilIntro(function()
|
||||
return IsPedInVehicle(introDriver, introVehicle, false)
|
||||
and IsPedInVehicle(playerPed, introVehicle, false)
|
||||
end, 12000)
|
||||
if enterResult == 'skip' then finishWelcomeIntro(true) return end
|
||||
if enterResult ~= 'ready' then
|
||||
SetPedIntoVehicle(introDriver, introVehicle, -1)
|
||||
SetPedIntoVehicle(playerPed, introVehicle, 0)
|
||||
end
|
||||
|
||||
if introCamera and DoesCamExist(introCamera) then
|
||||
RenderScriptCams(false, true, 900, true, true)
|
||||
DestroyCam(introCamera, false)
|
||||
introCamera = nil
|
||||
end
|
||||
|
||||
TaskVehicleDriveToCoordLongrange(
|
||||
introDriver, introVehicle,
|
||||
config.destinationRoad.x, config.destinationRoad.y, config.destinationRoad.z,
|
||||
config.driveSpeed, config.drivingStyle, 10.0
|
||||
)
|
||||
|
||||
showDialogue(config.driverName, 'Am Anfang startest du klein. Arbeit, Kontakte und ein eigenes Zuhause kommen Schritt für Schritt.')
|
||||
if not waitIntro(6200) then finishWelcomeIntro(true) return end
|
||||
|
||||
showDialogue(config.driverName, 'Mit M öffnest du dein Handy. Dort findest du deine Telefonnummer und dein Bankkonto.')
|
||||
if not waitIntro(5600) then finishWelcomeIntro(true) return end
|
||||
hideDialogue()
|
||||
|
||||
local driveDeadline = GetGameTimer() + config.maxDriveTimeMs
|
||||
while introRunning and GetGameTimer() < driveDeadline do
|
||||
if introControls() then finishWelcomeIntro(true) return end
|
||||
if not DoesEntityExist(introVehicle) or not DoesEntityExist(introDriver) then break end
|
||||
|
||||
local coords = GetEntityCoords(introVehicle)
|
||||
if #(coords - vector3(config.destinationRoad.x, config.destinationRoad.y, config.destinationRoad.z)) < 28.0 then
|
||||
break
|
||||
end
|
||||
Wait(0)
|
||||
end
|
||||
|
||||
if not introRunning then return end
|
||||
TaskVehicleTempAction(introDriver, introVehicle, 27, 2500)
|
||||
showDialogue(config.driverName, 'Da wären wir. Willkommen im Zentrum von DevX-City – und viel Erfolg!')
|
||||
if not waitIntro(4700) then finishWelcomeIntro(true) return end
|
||||
finishWelcomeIntro(true)
|
||||
end
|
||||
|
||||
local function spawnCharacter(player)
|
||||
local shouldPlayIntro = DevXCharacterConfig.Intro.enabled and player.introCompleted ~= true
|
||||
local position = shouldPlayIntro
|
||||
and DevXCharacterConfig.Intro.terminalInside
|
||||
or player.position
|
||||
or DevXConfig.StreetSpawn
|
||||
|
||||
local modelName = player.model or 'mp_m_freemode_01'
|
||||
local model = loadModel(modelName)
|
||||
if not model then
|
||||
SendNUIMessage({ action = 'error', message = 'Charaktermodell konnte nicht geladen werden.' })
|
||||
return
|
||||
end
|
||||
|
||||
DoScreenFadeOut(250)
|
||||
while not IsScreenFadedOut() do Wait(0) end
|
||||
|
||||
exports.spawnmanager:spawnPlayer({
|
||||
x = position.x,
|
||||
y = position.y,
|
||||
z = position.z,
|
||||
heading = position.heading or 0.0,
|
||||
model = model,
|
||||
skipFade = true
|
||||
}, function()
|
||||
SetModelAsNoLongerNeeded(model)
|
||||
setSelector(false)
|
||||
|
||||
local ped = PlayerPedId()
|
||||
applyBasicAppearance(ped, modelName)
|
||||
SetEntityCollision(ped, true, true)
|
||||
SetEntityVisible(ped, true, false)
|
||||
FreezeEntityPosition(ped, false)
|
||||
TriggerEvent('devx_characters:client:spawned', player)
|
||||
|
||||
if shouldPlayIntro then
|
||||
CreateThread(startWelcomeIntro)
|
||||
else
|
||||
DisplayRadar(true)
|
||||
DoScreenFadeIn(500)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function initializeSelector()
|
||||
if selectorInitialized then return end
|
||||
selectorInitialized = true
|
||||
|
||||
exports.spawnmanager:setAutoSpawn(false)
|
||||
while not NetworkIsSessionStarted() do Wait(100) end
|
||||
|
||||
local spawn = DevXCharacterConfig.Preview.streamPosition
|
||||
local model = loadModel('mp_m_freemode_01') or loadModel('a_m_y_business_01')
|
||||
if not model then return end
|
||||
|
||||
DoScreenFadeOut(0)
|
||||
local initialSpawnFinished = false
|
||||
|
||||
local function finishInitialization()
|
||||
if initialSpawnFinished then return end
|
||||
initialSpawnFinished = true
|
||||
SetModelAsNoLongerNeeded(model)
|
||||
setSelector(true)
|
||||
requestCharacters()
|
||||
ShutdownLoadingScreen()
|
||||
ShutdownLoadingScreenNui()
|
||||
DoScreenFadeIn(500)
|
||||
end
|
||||
|
||||
exports.spawnmanager:spawnPlayer({
|
||||
x = spawn.x,
|
||||
y = spawn.y,
|
||||
z = spawn.z,
|
||||
heading = spawn.heading,
|
||||
model = model,
|
||||
skipFade = true
|
||||
}, finishInitialization)
|
||||
|
||||
SetTimeout(12000, finishInitialization)
|
||||
end
|
||||
|
||||
RegisterNUICallback('ready', function(_, cb)
|
||||
requestCharacters()
|
||||
cb({ ok = true })
|
||||
end)
|
||||
|
||||
RegisterNUICallback('preview', function(data, cb)
|
||||
createPreviewCharacter(data.model or (data.sex == 'f' and 'mp_f_freemode_01' or 'mp_m_freemode_01'))
|
||||
cb({ ok = true })
|
||||
end)
|
||||
|
||||
RegisterNUICallback('create', function(data, cb)
|
||||
runNuiRpc('characters:create', data, cb, function(result)
|
||||
if result.ok then requestCharacters() end
|
||||
end)
|
||||
end)
|
||||
|
||||
RegisterNUICallback('select', function(data, cb)
|
||||
runNuiRpc('characters:select', data, cb, function(result)
|
||||
if result.ok then spawnCharacter(result.player) end
|
||||
end)
|
||||
end)
|
||||
|
||||
RegisterNUICallback('delete', function(data, cb)
|
||||
runNuiRpc('characters:delete', data, cb, function(result)
|
||||
if result.ok then requestCharacters() end
|
||||
end)
|
||||
end)
|
||||
|
||||
CreateThread(initializeSelector)
|
||||
|
||||
AddEventHandler('onClientResourceStart', function(resourceName)
|
||||
if resourceName ~= GetCurrentResourceName() then return end
|
||||
exports.spawnmanager:setAutoSpawn(false)
|
||||
CreateThread(initializeSelector)
|
||||
end)
|
||||
|
||||
AddEventHandler('onResourceStop', function(resourceName)
|
||||
if resourceName ~= GetCurrentResourceName() then return end
|
||||
introRunning = false
|
||||
destroyPreviewScene()
|
||||
safeDeleteEntity(introDriver)
|
||||
safeDeleteEntity(introVehicle)
|
||||
SetNuiFocus(false, false)
|
||||
end)
|
||||
Reference in New Issue
Block a user