1
0
Fork 0
forked from Simnation/Main
Main/resources/[Developer]/[Nordi]/0r-atmrobbery/escrow/hacking/client.lua
2025-06-07 08:51:21 +02:00

503 lines
16 KiB
Lua

--[[ require ]]
local Utils = require 'modules.utils.client'
local Target = require 'modules.target.client'
--[[ state ]]
Hack = {
camId = nil,
}
local onAtmHack = false
local currentAtmCameraKey = 1
local isAimBusy = false
local aimOnHackable = false
local foundEntityByShapeTest = nil
local atmParticles = {}
local ownedCashPiles = {}
local selectedAtmEntity = nil
--[[ functions ]]
local function isAtmModel(model)
for _, atmModel in pairs(Config.AtmModels) do
if model == atmModel then
return true
end
end
return false
end
local function deleteOwnedCashPile(entity)
for key, value in pairs(ownedCashPiles) do
if value == entity then
if DoesEntityExist(value) then
SetEntityAsMissionEntity(value, true, true)
DeleteEntity(value)
end
table.remove(ownedCashPiles, key)
return
end
end
end
local function createMoneySprayEffect(entity)
local coords = GetOffsetFromEntityInWorldCoords(entity, 0.0, 0.0, 1.0)
local ptFxName = 'scr_xs_celebration'
local rotX, rotY, rotZ = table.unpack(GetEntityRotation(entity, 2))
local atmParticles = {}
for i = 1, 10 do
lib.requestNamedPtfxAsset(ptFxName)
UseParticleFxAssetNextCall(ptFxName)
local randomRotX = 60.0
local randomRotY = rotY
local randomRotZ = rotZ
local particle = StartParticleFxLoopedAtCoord(
'scr_xs_money_rain',
coords.x, coords.y, coords.z,
randomRotX, randomRotY, randomRotZ,
1.0, false, false, false, false
)
table.insert(atmParticles, particle)
RemoveNamedPtfxAsset(ptFxName)
end
local particleKey = #atmParticles + 1
atmParticles[particleKey] = atmParticles
Citizen.SetTimeout(10000, function()
for _, particle in ipairs(atmParticles[particleKey]) do
StopParticleFxLooped(particle, 0)
end
atmParticles[particleKey] = nil
end)
end
local function createMoneyObject(entity)
local coords = GetOffsetFromEntityInWorldCoords(entity, 0.0, -.5, 1.0)
local rotation = GetEntityRotation(entity)
local model = 'bkr_prop_bkr_cashpile_05'
local objectCashPile = Utils.CreateObject(model, coords, rotation, true, true, false)
table.insert(ownedCashPiles, objectCashPile)
lib.waitFor(function()
return PlaceObjectOnGroundProperly(objectCashPile)
end, nil, 1000)
local zoneKey = string.format('atmrobbery_hack_pickup_money_%d', #ownedCashPiles)
Target.addBoxZone(zoneKey, {
coords = GetEntityCoords(objectCashPile),
size = vector3(1.75, 1.75, 1.75),
debug = false,
options = {
{
icon = 'fa-solid fa-hand-fist',
label = locale('collect'),
distance = 1.5,
pile = objectCashPile,
onSelect = function(data)
local playerPedId = cache.ped
Target.removeZone(zoneKey)
lib.playAnim(playerPedId, 'pickup_object', 'pickup_low', nil, nil, 1000)
Citizen.Wait(1000)
ClearPedTasks(playerPedId)
deleteOwnedCashPile(objectCashPile)
lib.callback.await(_e('server:hacking:collectAtmCashPile'), false)
end
}
}
})
end
local function RotationToDirection(rotation)
local radZ = math.rad(rotation.z)
local radX = math.rad(rotation.x)
local num = math.abs(math.cos(radX))
return vector3(
-math.sin(radZ) * num,
math.cos(radZ) * num,
math.sin(radX)
)
end
local function IsPointInFrontOfCamera(camPos, forwardVec, point)
local pointDirection = (point - camPos)
local dotProduct = forwardVec.x * pointDirection.x + forwardVec.y * pointDirection.y +
forwardVec.z * pointDirection.z
return dotProduct > 0
end
local function FindHackableObjectFromCamera()
local hit, entityHit = lib.raycast.fromCamera(16, 4, 15.0)
if not hit or not DoesEntityExist(entityHit) then
return nil
end
local entityModel = GetEntityModel(entityHit)
local entityPos = GetEntityCoords(entityHit)
if not isAtmModel(entityModel) then
return nil
end
return entityHit
end
local function DisableDisplayControlActions()
DisableControlAction(0, 263, true) -- disable melee
DisableControlAction(0, 264, true) -- disable melee
DisableControlAction(0, 257, true) -- disable melee
DisableControlAction(0, 140, true) -- disable melee
DisableControlAction(0, 141, true) -- disable melee
DisableControlAction(0, 142, true) -- disable melee
DisableControlAction(0, 143, true) -- disable melee
DisableControlAction(0, 177, true) -- disable escape
DisableControlAction(0, 200, true) -- disable escape
DisableControlAction(0, 202, true) -- disable escape
DisableControlAction(0, 322, true) -- disable escape
DisableControlAction(0, 245, true) -- disable chat
if isAimBusy then
DisableAllControlActions(0)
end
end
function deleteHackCam()
if DoesCamExist(Hack.camId) then
DestroyCam(Hack.camId)
ClearFocus()
RenderScriptCams(false, false, 0, false, false)
ClearTimecycleModifier()
ClearExtraTimecycleModifier()
end
client.setBusy(false, 'hack.clear')
Hack.camId = nil
end
function CleanUp()
deleteHackCam()
client.SendReactMessage('ui:setVisible', false)
if aimOnHackable then
client.SendReactMessage('ui:setAimOnHackable', false)
SetEntityDrawOutline(aimOnHackable, false)
end
Utils.ToggleHud(true)
client.removeTabletFromPlayer()
aimOnHackable = false
onAtmHack = false
selectedAtmEntity = nil
end
function CreateAtmCamera(cameraData)
local cam = CreateCamWithParams('DEFAULT_SCRIPTED_CAMERA',
cameraData.coords.x, cameraData.coords.y, cameraData.coords.z,
cameraData.rot.x, cameraData.rot.y, cameraData.rot.z,
70.0)
return cam
end
function ChangeCamera(cam, id)
ClearFocus()
local camCoords = Config.AtmCameras[id].coords
local camRot = Config.AtmCameras[id].rot
SetCamCoord(cam, camCoords.x, camCoords.y, camCoords.z)
SetCamRot(cam, camRot.x, camRot.y, camRot.z, 2)
SetFocusPosAndVel(camCoords.x, camCoords.y, camCoords.z, 0.0, 0.0, 0.0)
end
function CheckCamRotationInput(cam)
local rightAxisX = GetDisabledControlNormal(0, 220)
local rightAxisY = GetDisabledControlNormal(0, 221)
if rightAxisX ~= 0.0 or rightAxisY ~= 0.0 then
local rotation = GetCamRot(cam, 2)
local new_z = rotation.z + rightAxisX * -1.0 * 2.0 * (4.0 + 0.1)
local new_x = math.max(-60.0, math.min(60.0, rotation.x + rightAxisY * -1.0 * 2.0 * (4.0 + 0.1)))
SetCamRot(cam, new_x, 0.0, new_z, 2)
end
end
function HandleControls(cam)
DisableAllControlActions(0)
HideHudAndRadarThisFrame()
CheckCamRotationInput(cam)
if IsDisabledControlJustPressed(0, 34) then
currentAtmCameraKey = (currentAtmCameraKey - 2) % #Config.AtmCameras + 1
ChangeCamera(cam, currentAtmCameraKey)
elseif IsDisabledControlJustPressed(0, 35) then
currentAtmCameraKey = currentAtmCameraKey % #Config.AtmCameras + 1
ChangeCamera(cam, currentAtmCameraKey)
end
if IsDisabledControlPressed(0, 97) and GetCamFov(cam) < 100 then
SetCamFov(cam, GetCamFov(cam) + 2.0)
elseif IsDisabledControlPressed(0, 96) and GetCamFov(cam) > 10 then
SetCamFov(cam, GetCamFov(cam) - 2.0)
end
if IsDisabledControlJustPressed(0, 194) then
CleanUp()
end
end
function UpdateHackableEntityData()
local entity = FindHackableObjectFromCamera()
foundEntityByShapeTest = entity
end
function GetLocationLabel(entity)
local entityCoords = GetEntityCoords(entity)
local streetHash, crossingRoadHash = GetStreetNameAtCoord(entityCoords.x, entityCoords.y, entityCoords.z)
local st1 = GetStreetNameFromHashKey(streetHash)
local st2 = GetStreetNameFromHashKey(crossingRoadHash)
return st1 .. ' ' .. st2
end
function HandleHackableObject()
if foundEntityByShapeTest then
if not aimOnHackable then
aimOnHackable = foundEntityByShapeTest
local targetCoords = GetOffsetFromEntityInWorldCoords(aimOnHackable, 0.2, -0.2, 1.5)
local _, screenX, screenY = GetScreenCoordFromWorldCoord(targetCoords.x, targetCoords.y, targetCoords.z)
client.SendReactMessage('ui:setAimOnHackable', true)
client.SendReactMessage('ui:setAtmInfo', {
entity = aimOnHackable,
screenX = screenX,
screenY = screenY,
minMoney = Entity(aimOnHackable).state.robbed and 0 or Config.Hack.reward.min,
maxMoney = Entity(aimOnHackable).state.robbed and 0 or Config.Hack.reward.max,
location = GetLocationLabel(aimOnHackable)
})
SetEntityDrawOutlineColor(255, 0, 0)
SetEntityDrawOutline(foundEntityByShapeTest, true)
elseif aimOnHackable ~= foundEntityByShapeTest then
SetEntityDrawOutline(aimOnHackable, false)
aimOnHackable = false
end
if not isAimBusy and IsDisabledControlJustPressed(0, 229) then
local model = GetEntityModel(aimOnHackable)
local coords = GetEntityCoords(aimOnHackable)
if lib.callback.await(_e('server:IsAtmHacked'), false, model, coords) then
Utils.Notify(locale('atm_can_not_be_hack'), 'error')
Citizen.Wait(500)
else
Utils.TriggerPoliceAlert(coords)
isAimBusy = true
selectedAtmEntity = aimOnHackable
client.SendReactMessage('ui:setMiniGameOpen', true)
SetNuiFocus(true, false)
end
end
elseif aimOnHackable then
client.SendReactMessage('ui:setAimOnHackable', false)
SetEntityDrawOutline(aimOnHackable, false)
aimOnHackable = false
end
end
function Hack.Clear()
deleteHackCam()
for _, v in ipairs(atmParticles) do
for _, particle in pairs(v) do
StopParticleFxLooped(particle, 0)
end
end
for key, value in pairs(ownedCashPiles) do
if DoesEntityExist(value) then
SetEntityAsMissionEntity(value, true, true)
DeleteEntity(value)
end
local zoneKey = string.format('atmrobbery_explode_pickup_money_%d', key)
Target.removeZone(zoneKey)
end
if onAtmHack then
CleanUp()
end
atmParticles = {}
ownedCashPiles = {}
end
function Hack.OpenTablet()
if onAtmHack then return end
onAtmHack = true
client.SendReactMessage('ui:setPage', 'tablet')
client.SendReactMessage('ui:setVisible', true)
client.setBusy(true, 'hack.OpenTablet')
client.giveTabletToPlayer()
local cameras = Config.AtmCameras
Hack.camId = CreateAtmCamera(cameras[currentAtmCameraKey])
SetCamActive(Hack.camId, true)
RenderScriptCams(true, false, 1, false, true)
ChangeCamera(Hack.camId, currentAtmCameraKey)
SetTimecycleModifier('scanline_cam_cheap')
SetTimecycleModifierStrength(2.0)
Utils.ToggleHud(false)
Citizen.CreateThread(function()
while onAtmHack do
local wait = isAimBusy and 1000 or 1
HandleControls(Hack.camId)
HandleHackableObject()
Citizen.Wait(wait)
end
CleanUp()
end)
Citizen.CreateThread(function()
while onAtmHack do
local wait = isAimBusy and 1000 or 500
UpdateHackableEntityData()
Citizen.Wait(wait)
end
end)
end
function Hack.DeletePhone()
if onAtmHack then CleanUp() end
end
function Hack.OpenHackPhone()
if onAtmHack then return end
onAtmHack = true
client.SendReactMessage('ui:setPage', 'phone')
client.SendReactMessage('ui:setVisible', true)
client.setBusy(true, 'hack.OpenHackPhone')
client.givePhoneToPlayer()
Utils.ToggleHud(false)
Citizen.CreateThread(function()
while onAtmHack do
local wait = 0
HandleHackableObject()
DisableDisplayControlActions()
if IsDisabledControlJustPressed(0, 194) then
CleanUp()
end
if aimOnHackable then
if DoesEntityExist(client.tabletInHand) then
local phoneCoords = GetEntityCoords(client.tabletInHand)
local camCoords = GetGameplayCamCoord()
local camRot = GetGameplayCamRot(2)
local camDirection = RotationToDirection(camRot)
local distance = 1.0
local targetCoords = vector3(
camCoords.x + camDirection.x * distance,
camCoords.y + camDirection.y * distance,
camCoords.z + camDirection.z * distance
)
DrawLine(
phoneCoords.x, phoneCoords.y, phoneCoords.z,
targetCoords.x, targetCoords.y, targetCoords.z,
255, 255, 255, 125
)
end
end
Citizen.Wait(wait)
end
CleanUp()
end)
Citizen.CreateThread(function()
local holdPhoneAnim = {
dict = 'cellphone@',
name = 'cellphone_text_in'
}
while onAtmHack do
local wait = 500
UpdateHackableEntityData()
local ped = cache.ped
if not IsEntityPlayingAnim(ped, holdPhoneAnim.dict, holdPhoneAnim.name, 3) then
lib.playAnim(playerPedId, holdPhoneAnim.dict, holdPhoneAnim.name, 3.0, 3.0, -1, 50)
end
Citizen.Wait(wait)
end
end)
end
--[[ events ]]
RegisterNUICallback('nui:hacking:hackCurrentAtm', function(_, resultCallback)
if not selectedAtmEntity or
not DoesEntityExist(selectedAtmEntity)
then
return Utils.Notify(locale('atm_can_not_be_hack'), 'error')
end
local model = GetEntityModel(selectedAtmEntity)
local coords = GetEntityCoords(selectedAtmEntity)
TriggerServerEvent(_e('server:hacking:onAtmHackCompleted'), model, coords)
Utils.Notify(locale('atm_hacked'), 'success')
Entity(selectedAtmEntity).state.robbed = true
resultCallback(true)
end)
RegisterNetEvent(_e('client:hacking:onAtmTabletUsed'), function()
Hack.OpenTablet()
end)
RegisterNetEvent(_e('client:hacking:onHackPhoneUsed'), function()
Hack.OpenHackPhone()
end)
RegisterNetEvent(_e('client:hacking:onAtmHacked'), function(model, coords)
local atmEntity = GetClosestObjectOfType(coords.x, coords.y, coords.z, 0.3, model, true)
if not DoesEntityExist(atmEntity) then return end
createMoneySprayEffect(atmEntity)
end)
RegisterNetEvent(_e('client:hacking:onHackCompleted'), function(model, coords)
isAimBusy = false
SetNuiFocus(false, false)
Citizen.SetTimeout(7000, function()
local atmEntity = GetClosestObjectOfType(coords.x, coords.y, coords.z, 0.3, model, true)
if not DoesEntityExist(atmEntity) then return end
createMoneyObject(atmEntity)
end)
end)