312 lines
14 KiB
Lua
312 lines
14 KiB
Lua
|
|
local config = require 'config.client'
|
||
|
|
local sharedConfig = require 'config.shared'
|
||
|
|
local isHacking
|
||
|
|
local isSmashing
|
||
|
|
local closestVitrine = 1
|
||
|
|
local animName
|
||
|
|
local insideJewelry = false
|
||
|
|
local electricalBoxEntity
|
||
|
|
|
||
|
|
local function dropFingerprint()
|
||
|
|
if qbx.isWearingGloves() then return end
|
||
|
|
if config.fingerprintChance > math.random(0, 100) then
|
||
|
|
local coords = GetEntityCoords(cache.ped)
|
||
|
|
TriggerServerEvent('evidence:server:CreateFingerDrop', coords)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createElectricalBox()
|
||
|
|
lib.requestModel(`tr_prop_tr_elecbox_01a`)
|
||
|
|
electricalBoxEntity = CreateObjectNoOffset(joaat("tr_prop_tr_elecbox_01a"), sharedConfig.electrical.x, sharedConfig.electrical.y, sharedConfig.electrical.z, true, true, false)
|
||
|
|
SetModelAsNoLongerNeeded(`tr_prop_tr_elecbox_01a`)
|
||
|
|
while not DoesEntityExist(electricalBoxEntity) do
|
||
|
|
Wait(0)
|
||
|
|
end
|
||
|
|
SetEntityHeading(electricalBoxEntity, sharedConfig.electrical.w)
|
||
|
|
if config.useTarget then
|
||
|
|
exports.ox_target:addLocalEntity(electricalBoxEntity, {
|
||
|
|
{
|
||
|
|
name = 'qbx_jewelery:electricalBox',
|
||
|
|
icon = 'fab fa-usb',
|
||
|
|
label = locale('text.electricalTarget'),
|
||
|
|
distance = 1.6,
|
||
|
|
items = sharedConfig.doorlock.requiredItem,
|
||
|
|
onSelect = function()
|
||
|
|
lib.callback('qbx_jewelery:callback:electricalbox', false, function(CanHack)
|
||
|
|
if not CanHack then return end
|
||
|
|
TriggerEvent('qbx_jewelery:client:electricalHandler')
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
}
|
||
|
|
})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function removeElectricalBox()
|
||
|
|
if config.useTarget then
|
||
|
|
exports.ox_target:removeLocalEntity(electricalBoxEntity, 'qbx_jewelery:electricalBox')
|
||
|
|
end
|
||
|
|
if electricalBoxEntity ~= nil and DoesEntityExist(electricalBoxEntity) then
|
||
|
|
DeleteObject(electricalBoxEntity)
|
||
|
|
end
|
||
|
|
electricalBoxEntity = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if not config.useTarget then
|
||
|
|
CreateThread(function()
|
||
|
|
local hasShownText
|
||
|
|
while true do
|
||
|
|
local playerCoords = GetEntityCoords(cache.ped)
|
||
|
|
local electricalCoords = vector3(sharedConfig.electrical.x, sharedConfig.electrical.y, sharedConfig.electrical.z + 1.1)
|
||
|
|
local waitTime = 800
|
||
|
|
local nearby = false
|
||
|
|
if #(playerCoords - electricalCoords) <= 1.5 and not isHacking then
|
||
|
|
waitTime = 0
|
||
|
|
nearby = true
|
||
|
|
if config.useDrawText then
|
||
|
|
qbx.drawText3d({text = locale('text.electrical'), coords = electricalCoords})
|
||
|
|
elseif not config.useDrawText and not hasShownText then
|
||
|
|
hasShownText = true
|
||
|
|
lib.showTextUI(locale('text.electrical'), {position = 'left-center'})
|
||
|
|
end
|
||
|
|
if IsControlJustPressed(0, 38) then
|
||
|
|
lib.callback('qbx_jewelery:callback:electricalbox', false, function(CanHack)
|
||
|
|
if not CanHack then return end
|
||
|
|
isHacking = true
|
||
|
|
TriggerEvent('qbx_jewelery:client:electricalHandler')
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if not nearby and hasShownText then
|
||
|
|
hasShownText = false
|
||
|
|
lib.hideTextUI()
|
||
|
|
end
|
||
|
|
Wait(waitTime)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
|
||
|
|
AddEventHandler('qbx_jewelery:client:electricalHandler', function()
|
||
|
|
local animDictBox = 'anim@scripted@player@mission@tun_control_tower@male@'
|
||
|
|
lib.requestAnimDict(animDictBox)
|
||
|
|
local playerCoords = GetEntityCoords(cache.ped)
|
||
|
|
local box = GetClosestObjectOfType(playerCoords.x, playerCoords.y, playerCoords.z, 1.5, `tr_prop_tr_elecbox_01a`, false, false, false)
|
||
|
|
local enterScene = NetworkCreateSynchronisedScene(sharedConfig.electrical.x, sharedConfig.electrical.y, sharedConfig.electrical.z, 0, 0, sharedConfig.electrical.w, 2, true, false, 1065353216, -1, 1.0)
|
||
|
|
NetworkAddPedToSynchronisedScene(cache.ped, enterScene, animDictBox, 'enter', 1.5, -4.0, 1, 16, 1148846080, 0)
|
||
|
|
NetworkAddEntityToSynchronisedScene(box, enterScene, animDictBox, 'enter_electric_box', 4.0, -8.0, 1)
|
||
|
|
local loopingScene = NetworkCreateSynchronisedScene(sharedConfig.electrical.x, sharedConfig.electrical.y, sharedConfig.electrical.z, 0, 0, sharedConfig.electrical.w, 2, false, true, 1065353216, -1, 1.0)
|
||
|
|
NetworkAddPedToSynchronisedScene(cache.ped, loopingScene, animDictBox, 'loop', 1.5, -4.0, 1, 16, 1148846080, 0)
|
||
|
|
NetworkAddEntityToSynchronisedScene(box, loopingScene, animDictBox, 'loop_electric_box', 4.0, -8.0, 1)
|
||
|
|
local leavingScene = NetworkCreateSynchronisedScene(sharedConfig.electrical.x, sharedConfig.electrical.y, sharedConfig.electrical.z, 0, 0, sharedConfig.electrical.w, 2, true, false, 1065353216, -1, 1.0)
|
||
|
|
NetworkAddPedToSynchronisedScene(cache.ped, leavingScene, animDictBox, 'exit', 1.5, -4.0, 1, 16, 1148846080, 0)
|
||
|
|
NetworkAddEntityToSynchronisedScene(box, leavingScene, animDictBox, 'exit_electric_box', 4.0, -8.0, 1)
|
||
|
|
|
||
|
|
local duration = GetAnimDuration(animDictBox, 'enter')
|
||
|
|
NetworkStartSynchronisedScene(enterScene)
|
||
|
|
Wait(duration * 1000)
|
||
|
|
NetworkStartSynchronisedScene(loopingScene)
|
||
|
|
|
||
|
|
TriggerEvent('ultra-voltlab', math.random(sharedConfig.doorlock.hackTime.min, sharedConfig.doorlock.hackTime.max), function(result, reason)
|
||
|
|
Wait(2500)
|
||
|
|
NetworkStartSynchronisedScene(leavingScene)
|
||
|
|
isHacking = false
|
||
|
|
if result == 0 then -- Failed
|
||
|
|
TriggerServerEvent('qbx_jewelery:server:failedhackdoor')
|
||
|
|
exports.qbx_core:Notify(reason, 'error')
|
||
|
|
elseif result == 1 then -- Succeeded
|
||
|
|
TriggerServerEvent('qbx_jewelery:server:succeshackdoor')
|
||
|
|
elseif result == 2 then -- Timed out
|
||
|
|
TriggerServerEvent('qbx_jewelery:server:failedhackdoor')
|
||
|
|
exports.qbx_core:Notify(reason, 'error')
|
||
|
|
elseif result == -1 then -- Some error
|
||
|
|
TriggerServerEvent('qbx_jewelery:server:failedhackdoor')
|
||
|
|
exports.qbx_core:Notify('Failed hack', 'error')
|
||
|
|
print('Error occured', reason)
|
||
|
|
end
|
||
|
|
Wait(GetAnimDuration(animDictBox, 'exit') * 1000)
|
||
|
|
NetworkStopSynchronisedScene(leavingScene)
|
||
|
|
RemoveAnimDict(animDictBox)
|
||
|
|
end)
|
||
|
|
end)
|
||
|
|
|
||
|
|
local function startRayFire(coords, rayFire)
|
||
|
|
local RayFireObject = GetRayfireMapObject(coords.x, coords.y, coords.z, 1.4, rayFire)
|
||
|
|
SetStateOfRayfireMapObject(RayFireObject, 4)
|
||
|
|
Wait(100)
|
||
|
|
SetStateOfRayfireMapObject(RayFireObject, 6)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function loadParticle()
|
||
|
|
lib.requestNamedPtfxAsset('scr_jewelheist')
|
||
|
|
UseParticleFxAsset('scr_jewelheist')
|
||
|
|
end
|
||
|
|
|
||
|
|
local function playSmashAudio(coords)
|
||
|
|
local soundId = GetSoundId()
|
||
|
|
PlaySoundFromCoord(soundId, 'Glass_Smash', coords.x, coords.y, coords.z, '', false, 6.0, false)
|
||
|
|
ReleaseSoundId(soundId)
|
||
|
|
end
|
||
|
|
|
||
|
|
if config.useTarget then
|
||
|
|
for i = 1, #sharedConfig.vitrines do
|
||
|
|
exports.ox_target:addBoxZone({
|
||
|
|
coords = sharedConfig.vitrines[i].coords,
|
||
|
|
size = vec3(1.2, 1.6, 1),
|
||
|
|
rotation = sharedConfig.vitrines[i].heading,
|
||
|
|
--debug = true,
|
||
|
|
options = {
|
||
|
|
{
|
||
|
|
icon = 'fas fa-gem',
|
||
|
|
label = locale('text.cabinetTarget'),
|
||
|
|
distance = 0.6,
|
||
|
|
onSelect = function()
|
||
|
|
closestVitrine = i
|
||
|
|
lib.callback('qbx_jewelery:callback:cabinet', false, function(CanSmash)
|
||
|
|
if not CanSmash then return end
|
||
|
|
TriggerEvent('qbx_jewelery:client:cabinetHandler')
|
||
|
|
end, closestVitrine)
|
||
|
|
end
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
end
|
||
|
|
else
|
||
|
|
CreateThread(function()
|
||
|
|
local hasShownText
|
||
|
|
while true do
|
||
|
|
local playerCoords = GetEntityCoords(cache.ped)
|
||
|
|
local nearby = false
|
||
|
|
local waitTime = 800
|
||
|
|
for i = 1, #sharedConfig.vitrines do
|
||
|
|
if #(playerCoords - sharedConfig.vitrines[i].coords) < 0.5 then
|
||
|
|
if not closestVitrine then closestVitrine = i
|
||
|
|
elseif #(playerCoords - sharedConfig.vitrines[i].coords) < #(playerCoords - sharedConfig.vitrines[closestVitrine].coords) then closestVitrine = i end
|
||
|
|
waitTime = 0
|
||
|
|
nearby = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if nearby and not (isSmashing or sharedConfig.vitrines[closestVitrine].isOpened) then
|
||
|
|
if config.useDrawText then
|
||
|
|
qbx.drawText3d({text = locale('text.cabinet'), coords = sharedConfig.vitrines[closestVitrine].coords})
|
||
|
|
elseif not config.useDrawText and not hasShownText then
|
||
|
|
hasShownText = true
|
||
|
|
lib.showTextUI(locale('text.cabinet'), {position = 'left-center'})
|
||
|
|
end
|
||
|
|
if IsControlJustPressed(0, 38) then
|
||
|
|
lib.callback('qbx_jewelery:callback:cabinet', false, function(CanSmash)
|
||
|
|
if not CanSmash then return end
|
||
|
|
|
||
|
|
isSmashing = true
|
||
|
|
if hasShownText then hasShownText = false lib.hideTextUI() end
|
||
|
|
TriggerEvent('qbx_jewelery:client:cabinetHandler')
|
||
|
|
end, closestVitrine)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if not nearby and hasShownText then hasShownText = false lib.hideTextUI() end
|
||
|
|
Wait(waitTime)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
|
||
|
|
AddEventHandler('qbx_jewelery:client:cabinetHandler', function()
|
||
|
|
local animDictCabinet = 'missheist_jewel'
|
||
|
|
local animNameSmashFront = {
|
||
|
|
'smash_case_tray_b',
|
||
|
|
'smash_case_necklace_skull'
|
||
|
|
}
|
||
|
|
local animNameSmashTop = {
|
||
|
|
'smash_case_tray_a',
|
||
|
|
'smash_case_d',
|
||
|
|
'smash_case_e'
|
||
|
|
}
|
||
|
|
local playerCoords = GetEntityCoords(cache.ped)
|
||
|
|
|
||
|
|
dropFingerprint()
|
||
|
|
|
||
|
|
TaskAchieveHeading(cache.ped, sharedConfig.vitrines[closestVitrine].heading, 1500)
|
||
|
|
Wait(1500)
|
||
|
|
lib.requestAnimDict(animDictCabinet)
|
||
|
|
if sharedConfig.vitrines[closestVitrine].rayFire == 'DES_Jewel_Cab4' then
|
||
|
|
animName = animNameSmashFront[math.random(1, #animNameSmashFront)]
|
||
|
|
TaskPlayAnim(cache.ped, animDictCabinet, animName, 3.0, 3.0, -1, 2, 0, false, false, false)
|
||
|
|
Wait(150)
|
||
|
|
startRayFire(playerCoords, sharedConfig.vitrines[closestVitrine].rayFire)
|
||
|
|
elseif sharedConfig.vitrines[closestVitrine].rayFire then
|
||
|
|
animName = animNameSmashTop[math.random(1, #animNameSmashTop)]
|
||
|
|
TaskPlayAnim(cache.ped, animDictCabinet, animName, 3.0, 3.0, -1, 2, 0, false, false, false)
|
||
|
|
Wait(300)
|
||
|
|
startRayFire(playerCoords, sharedConfig.vitrines[closestVitrine].rayFire)
|
||
|
|
else
|
||
|
|
animName = animNameSmashTop[math.random(1, #animNameSmashTop)]
|
||
|
|
TaskPlayAnim(cache.ped, animDictCabinet, animName, 3.0, 3.0, -1, 2, 0, false, false, false)
|
||
|
|
Wait(300)
|
||
|
|
end
|
||
|
|
RemoveAnimDict(animDictCabinet)
|
||
|
|
loadParticle()
|
||
|
|
StartNetworkedParticleFxNonLoopedOnEntity('scr_jewel_cab_smash', GetCurrentPedWeaponEntityIndex(cache.ped), 0, 0, 0, 0, 0, 0, 1.6, false, false, false)
|
||
|
|
playSmashAudio(playerCoords)
|
||
|
|
Wait(GetAnimDuration(animDictCabinet, animName) * 850)
|
||
|
|
ClearPedTasks(cache.ped)
|
||
|
|
RemoveNamedPtfxAsset('scr_jewelheist')
|
||
|
|
isSmashing = false
|
||
|
|
TriggerServerEvent('qbx_jewelery:server:endcabinet')
|
||
|
|
end)
|
||
|
|
|
||
|
|
RegisterNetEvent('qbx_jewelery:client:synceffects', function(closestVitrines, originalPlayer)
|
||
|
|
closestVitrine = closestVitrines
|
||
|
|
Wait(1500)
|
||
|
|
if sharedConfig.vitrines[closestVitrine].rayFire == 'DES_Jewel_Cab4' then
|
||
|
|
Wait(150)
|
||
|
|
startRayFire(sharedConfig.vitrines[closestVitrine].coords, sharedConfig.vitrines[closestVitrine].rayFire)
|
||
|
|
elseif sharedConfig.vitrines[closestVitrine].rayFire then
|
||
|
|
Wait(300)
|
||
|
|
startRayFire(sharedConfig.vitrines[closestVitrine].coords, sharedConfig.vitrines[closestVitrine].rayFire)
|
||
|
|
end
|
||
|
|
loadParticle()
|
||
|
|
StartNetworkedParticleFxNonLoopedOnEntity('scr_jewel_cab_smash', GetCurrentPedWeaponEntityIndex(GetPlayerPed(GetPlayerFromServerId(originalPlayer))), 0, 0, 0, 0, 0, 0, 1.6, false, false, false)
|
||
|
|
playSmashAudio(sharedConfig.vitrines[closestVitrine].coords)
|
||
|
|
RemoveNamedPtfxAsset('scr_jewelheist')
|
||
|
|
end)
|
||
|
|
|
||
|
|
RegisterNetEvent('qbx_jewelery:client:syncconfig', function(vitrines)
|
||
|
|
sharedConfig.vitrines = vitrines
|
||
|
|
end)
|
||
|
|
|
||
|
|
RegisterNetEvent('qbx_jewelery:client:alarm', function()
|
||
|
|
PrepareAlarm('JEWEL_STORE_HEIST_ALARMS')
|
||
|
|
Wait(100)
|
||
|
|
StartAlarm('JEWEL_STORE_HEIST_ALARMS', false)
|
||
|
|
Wait(config.alarmDuration)
|
||
|
|
StopAlarm('JEWEL_STORE_HEIST_ALARMS', true)
|
||
|
|
end)
|
||
|
|
|
||
|
|
lib.zones.sphere({
|
||
|
|
coords = vec3(sharedConfig.vitrines[1].coords.x, sharedConfig.vitrines[1].coords.y, sharedConfig.vitrines[1].coords.z),
|
||
|
|
radius = 80,
|
||
|
|
--debug = true,
|
||
|
|
onEnter = function()
|
||
|
|
insideJewelry = true
|
||
|
|
createElectricalBox()
|
||
|
|
CreateThread(function()
|
||
|
|
while insideJewelry do
|
||
|
|
for i = 1, #sharedConfig.vitrines do
|
||
|
|
if sharedConfig.vitrines[i].isOpened then
|
||
|
|
local rayFire = GetRayfireMapObject(sharedConfig.vitrines[i].coords.x, sharedConfig.vitrines[i].coords.y, sharedConfig.vitrines[i].coords.z, 1.4, sharedConfig.vitrines[i].rayFire)
|
||
|
|
SetStateOfRayfireMapObject(rayFire, 9)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
Wait(6000)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
end,
|
||
|
|
onExit = function()
|
||
|
|
removeElectricalBox()
|
||
|
|
insideJewelry = false
|
||
|
|
end,
|
||
|
|
})
|
||
|
|
|
||
|
|
AddEventHandler('onResourceStop', function(resource)
|
||
|
|
if resource ~= cache.resource then return end
|
||
|
|
removeElectricalBox()
|
||
|
|
end)
|