forked from Simnation/Main
373 lines
13 KiB
Lua
373 lines
13 KiB
Lua
--[[ require ]]
|
|
|
|
local Utils = require 'modules.utils.client'
|
|
local Target = require 'modules.target.client'
|
|
|
|
--[[ state ]]
|
|
|
|
Rope = {}
|
|
|
|
local playerRopes = {}
|
|
|
|
local ownedHookObject = nil
|
|
local ownedFakeAtmObject = nil
|
|
local ownedAnchorThread = false
|
|
|
|
--[[ helper functions ]]
|
|
|
|
function deletePlayerRope(owner)
|
|
if not playerRopes[owner] then return end
|
|
if DoesPlayerRopeExist(owner) then
|
|
DeleteRope(playerRopes[owner].id)
|
|
end
|
|
cleanup_rope_textures()
|
|
playerRopes[owner] = nil
|
|
end
|
|
|
|
function deletePlayerRopes()
|
|
for _, rope in pairs(playerRopes) do
|
|
if DoesRopeExist(rope.id) then
|
|
DeleteRope(rope.id)
|
|
end
|
|
end
|
|
playerRopes = {}
|
|
end
|
|
|
|
function deleteHookObject()
|
|
if not ownedHookObject or not DoesEntityExist(ownedHookObject) then return end
|
|
SetEntityAsMissionEntity(ownedHookObject, true, true)
|
|
DeleteEntity(ownedHookObject)
|
|
ownedHookObject = nil
|
|
end
|
|
|
|
function deleteFakeAtmEntity()
|
|
if not ownedFakeAtmObject or not DoesEntityExist(ownedFakeAtmObject) then return end
|
|
SetEntityAsMissionEntity(ownedFakeAtmObject, true, true)
|
|
DeleteEntity(ownedFakeAtmObject)
|
|
ownedFakeAtmObject = nil
|
|
end
|
|
|
|
function giveHookToPedHand()
|
|
deleteHookObject()
|
|
local playerPedId = cache.ped
|
|
local hookModel = 'prop_rope_hook_01'
|
|
|
|
lib.requestModel(hookModel)
|
|
local hookCoords = GetEntityCoords(playerPedId)
|
|
ownedHookObject = Utils.CreateObject(hookModel, hookCoords, nil, true, true, false)
|
|
local boneIndex = GetPedBoneIndex(playerPedId, 57005)
|
|
|
|
AttachEntityToEntity(ownedHookObject, playerPedId, boneIndex,
|
|
0.15, 0.0, -0.05, 120.0, 0.0, 15.0, true, false, false, true, 2, true)
|
|
end
|
|
|
|
function cleanup_rope_textures()
|
|
if #GetAllRopes() == 0 then
|
|
RopeUnloadTextures()
|
|
end
|
|
end
|
|
|
|
function addRopeToPlayer(owner, attach)
|
|
while not RopeAreTexturesLoaded() do
|
|
RopeLoadTextures()
|
|
Citizen.Wait(0)
|
|
end
|
|
|
|
local coords = GetEntityCoords(cache.ped)
|
|
|
|
local rope = AddRope(
|
|
coords.x, coords.y, coords.z,
|
|
0.0, 0.0, 0.0,
|
|
1.0,
|
|
4, 7.0, 1.0, 0,
|
|
false, false, false, 0, false, 0
|
|
)
|
|
|
|
if not DoesRopeExist(rope) then
|
|
cleanup_rope_textures()
|
|
return false
|
|
end
|
|
|
|
playerRopes[owner] = { id = rope, attach = attach }
|
|
|
|
return rope
|
|
end
|
|
|
|
function attachRopeToObject(rope, ent1, ent2, offset1, offset2)
|
|
AttachEntitiesToRope(rope, ent1, ent2,
|
|
offset1.x, offset1.y, offset1.z,
|
|
offset2.x, offset2.y, offset2.z,
|
|
7.0, 0, 0, 'rope_attach_a', 'rope_attach_b')
|
|
end
|
|
|
|
function DoesPlayerRopeExist(owner, attach)
|
|
local playerRope = playerRopes[owner]
|
|
local state = playerRope and DoesRopeExist(playerRope.id)
|
|
if not state then return false end
|
|
if attach and playerRope.attach ~= attach then
|
|
state = false
|
|
end
|
|
return state
|
|
end
|
|
|
|
function onOwnedAnchorCreated()
|
|
if ownedAnchorThread then return end
|
|
|
|
Citizen.CreateThread(function()
|
|
local textUI = false
|
|
local attachedVehicle = nil
|
|
|
|
ownedAnchorThread = true
|
|
|
|
while ownedAnchorThread do
|
|
local wait = 500
|
|
local entityCoords = GetEntityCoords(cache.ped)
|
|
local closestVehicle = lib.getClosestVehicle(entityCoords, 5.0, false)
|
|
local distanceFromFakeAtm = #(entityCoords - GetEntityCoords(ownedFakeAtmObject))
|
|
|
|
if distanceFromFakeAtm < 2.0 then
|
|
wait = 5
|
|
if not textUI then
|
|
textUI = true
|
|
Utils.ShowTextUI('[E] ' .. locale('remove_rope'))
|
|
end
|
|
if IsControlJustPressed(0, 38) then
|
|
Utils.HideTextUI()
|
|
deleteHookObject()
|
|
deleteFakeAtmEntity()
|
|
TriggerServerEvent(_e('server:rope:DeletePlayerRope'))
|
|
client.setBusy(false, 'rope.removeatmrope')
|
|
ownedAnchorThread = false
|
|
return
|
|
end
|
|
elseif closestVehicle then
|
|
local offsetCoords = GetOffsetFromEntityInWorldCoords(closestVehicle, 0, -2.3, 0.0)
|
|
if #(entityCoords - offsetCoords) < 1.5 then
|
|
wait = 5
|
|
if not textUI then
|
|
textUI = true
|
|
Utils.ShowTextUI('[E] ' .. locale('attach_rope'))
|
|
end
|
|
if IsControlJustPressed(0, 38) then
|
|
Utils.HideTextUI()
|
|
attachedVehicle = closestVehicle
|
|
break
|
|
end
|
|
elseif textUI then
|
|
Utils.HideTextUI()
|
|
textUI = false
|
|
end
|
|
elseif textUI then
|
|
Utils.HideTextUI()
|
|
textUI = false
|
|
end
|
|
|
|
Citizen.Wait(wait)
|
|
end
|
|
|
|
ownedAnchorThread = false
|
|
|
|
if not attachedVehicle then return end
|
|
|
|
TriggerServerEvent(_e('server:rope:attachRopeWithVehicle'),
|
|
ObjToNet(ownedFakeAtmObject),
|
|
VehToNet(attachedVehicle))
|
|
|
|
deleteHookObject()
|
|
|
|
Citizen.SetTimeout(10000, function()
|
|
if not ownedFakeAtmObject or not DoesEntityExist(ownedFakeAtmObject) then return end
|
|
|
|
local coords = GetEntityCoords(ownedFakeAtmObject)
|
|
local model = GetEntityModel(ownedFakeAtmObject)
|
|
|
|
TriggerServerEvent(_e('server:rope:onAtmRipped'), coords, model)
|
|
|
|
SetEntityVisible(ownedFakeAtmObject, true)
|
|
FreezeEntityPosition(ownedFakeAtmObject, false)
|
|
SetObjectPhysicsParams(ownedFakeAtmObject, 170.0, -1.0, 30.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
|
|
|
local forceDirection = vector3(2.0, 2.0, 2.0)
|
|
ApplyForceToEntity(ownedAtm, 1,
|
|
forceDirection.x, forceDirection.y, forceDirection.z,
|
|
0, 0, 0, true, true, true, true, false, true)
|
|
end)
|
|
end)
|
|
end
|
|
|
|
function addRobTargetToAtm()
|
|
Target.AddLocalEntity(ownedFakeAtmObject, { {
|
|
label = locale('collect'),
|
|
icon = 'fa-solid fa-money-bill-1-wave',
|
|
distance = 2.0,
|
|
canInteract = function()
|
|
return Entity(ownedFakeAtmObject) and Entity(ownedFakeAtmObject)?.state?.robbable
|
|
end,
|
|
onSelect = function()
|
|
client.setBusy(false, 'rope.onAtmRobbed')
|
|
Target.RemoveLocalEntity(ownedFakeAtmObject)
|
|
|
|
Entity(ownedFakeAtmObject).state.robbable = false
|
|
Entity(ownedFakeAtmObject).state.robbed = true
|
|
|
|
TriggerServerEvent(_e('server:rope:DeletePlayerRope'))
|
|
|
|
SetEntityAsNoLongerNeeded(ownedFakeAtmObject)
|
|
|
|
ownedFakeAtmObject = nil
|
|
|
|
lib.playAnim(cache.ped, 'amb@medic@standing@tendtodead@idle_a', 'idle_a')
|
|
lib.progressBar({
|
|
duration = 3500,
|
|
position = 'bottom',
|
|
label = locale('collect'),
|
|
useWhileDead = false,
|
|
canCancel = false,
|
|
disable = { car = true, move = true, combat = true, sprint = true },
|
|
})
|
|
ClearPedTasks(ped)
|
|
TriggerServerEvent(_e('server:GiveItem'),
|
|
Config.Rope.reward.name,
|
|
math.random(Config.Rope.reward.min, Config.Rope.reward.max))
|
|
end
|
|
} })
|
|
end
|
|
|
|
--[[ functions ]]
|
|
|
|
function Rope.CreateAtmRope(_, model)
|
|
Utils.debug('Rope.CreateAtmRope | Selected ATM Model: ', model)
|
|
|
|
local pedCoords = GetEntityCoords(cache.ped)
|
|
|
|
local entity = GetClosestObjectOfType(pedCoords.x, pedCoords.y, pedCoords.z, 2.0,
|
|
model, false, false, false)
|
|
|
|
if not DoesEntityExist(entity) then return end
|
|
|
|
local atmCoords = GetEntityCoords(entity)
|
|
local atmRotation = GetEntityRotation(entity)
|
|
local atmModel = GetEntityModel(entity)
|
|
|
|
if Entity(entity).state.robbed then
|
|
return Utils.Notify(locale('atm_can_not_be_rob'), 'error')
|
|
end
|
|
|
|
if lib.callback.await(_e('server:IsAtmHacked'), false, atmModel, atmCoords) then
|
|
return Utils.Notify(locale('atm_can_not_be_hack'), 'error')
|
|
end
|
|
|
|
if not lib.callback.await(_e('server:HasItem'), false, Config.Rope.requiredItem.name) then
|
|
return Utils.Notify(locale('need_item', Config.Rope.requiredItem.label), 'error')
|
|
end
|
|
|
|
Utils.TriggerPoliceAlert(atmCoords)
|
|
client.setHeadingToObject(atmModel)
|
|
client.setBusy(true, 'rope.createatmrope')
|
|
|
|
lib.playAnim(cache.ped, 'missmechanic', 'work2_base', nil, nil, nil, 1)
|
|
Citizen.Wait(2500)
|
|
ClearPedTasks(cache.ped)
|
|
|
|
giveHookToPedHand()
|
|
|
|
ownedFakeAtmObject = Utils.CreateObject(atmModel, atmCoords, atmRotation, true, true, false)
|
|
Entity(ownedFakeAtmObject).state.isFakeAtm = true
|
|
SetEntityVisible(ownedFakeAtmObject, false)
|
|
|
|
local netPedId = PedToNet(cache.ped)
|
|
local netFakeAtmEntity = ObjToNet(ownedFakeAtmObject)
|
|
|
|
TriggerServerEvent(_e('server:rope:AttachAtmRopeToPed'), netPedId, netFakeAtmEntity)
|
|
TriggerServerEvent(_e('server:RemoveItem'), Config.Rope.requiredItem.name)
|
|
onOwnedAnchorCreated()
|
|
addRobTargetToAtm()
|
|
end
|
|
|
|
function Rope.Clear()
|
|
if ownedFakeAtmObject then
|
|
TriggerServerEvent(_e('server:rope:DeletePlayerRope'))
|
|
end
|
|
deleteHookObject()
|
|
deleteFakeAtmEntity()
|
|
deletePlayerRopes()
|
|
|
|
ownedAnchorThread = nil
|
|
end
|
|
|
|
--[[ events ]]
|
|
|
|
RegisterNetEvent(_e('client:rope:AttachAtmRopeToPed'), function(netPedId, netFakeAtmEntity, owner)
|
|
if not Utils.WaitForEntityWithNetworkId(netPedId) then return end
|
|
if not Utils.WaitForEntityWithNetworkId(netFakeAtmEntity) then return end
|
|
|
|
local targetPedId = NetToPed(netPedId)
|
|
local targetAtmEntity = NetToObj(netFakeAtmEntity)
|
|
|
|
if not DoesEntityExist(targetPedId) then return end
|
|
if not DoesEntityExist(targetAtmEntity) then return end
|
|
|
|
ClearPedTasksImmediately(cache.ped)
|
|
Citizen.Wait(100)
|
|
|
|
local rope = addRopeToPlayer(owner, 'ped')
|
|
local targetAtmOffset = GetOffsetFromEntityInWorldCoords(targetAtmEntity, 0.0, 0.0, 1.0)
|
|
local pedBoneCoords = GetPedBoneCoords(targetPedId, 6286, 0.0, 0.0, 0.0)
|
|
attachRopeToObject(rope, targetAtmEntity, targetPedId, targetAtmOffset, pedBoneCoords)
|
|
|
|
Citizen.CreateThread(function()
|
|
while DoesPlayerRopeExist(owner, 'ped') and DoesEntityExist(targetAtmEntity) and DoesEntityExist(targetPedId) do
|
|
targetAtmOffset = GetOffsetFromEntityInWorldCoords(targetAtmEntity, 0.0, 0.0, 1.0)
|
|
pedBoneCoords = GetPedBoneCoords(targetPedId, 6286, 0.0, 0.0, 0.0)
|
|
DetachEntity(targetAtmEntity)
|
|
DetachEntity(targetPedId)
|
|
attachRopeToObject(rope, targetAtmEntity, targetPedId, targetAtmOffset, pedBoneCoords)
|
|
Citizen.Wait(64)
|
|
end
|
|
end)
|
|
end)
|
|
|
|
RegisterNetEvent(_e('client:rope:attachRopeWithVehicle'), function(netFakeAtmEntity, netAttachedVehicle, owner)
|
|
deletePlayerRope(owner)
|
|
|
|
if not Utils.WaitForEntityWithNetworkId(netFakeAtmEntity) then return end
|
|
if not Utils.WaitForEntityWithNetworkId(netAttachedVehicle) then return end
|
|
|
|
local targetAtmEntity = NetToObj(netFakeAtmEntity)
|
|
local targetVehicleEntity = NetToVeh(netAttachedVehicle)
|
|
|
|
if not DoesEntityExist(targetAtmEntity) then return end
|
|
if not DoesEntityExist(targetVehicleEntity) then return end
|
|
|
|
local targetAtmOffset = GetOffsetFromEntityInWorldCoords(targetAtmEntity, 0, 0.0, 1.0)
|
|
local targetVehicleOffset = GetOffsetFromEntityInWorldCoords(targetVehicleEntity, 0, -2.1, -.2)
|
|
|
|
local rope = addRopeToPlayer(owner, 'vehicle')
|
|
|
|
attachRopeToObject(rope,
|
|
targetAtmEntity, targetVehicleEntity,
|
|
targetAtmOffset, targetVehicleOffset)
|
|
end)
|
|
|
|
RegisterNetEvent(_e('client:rope:onAtmRipped'), function(coords, model, owner)
|
|
local nearByObjects = lib.getNearbyObjects(coords, 0.3)
|
|
for _, v in ipairs(nearByObjects) do
|
|
if GetEntityModel(v.object) == model and not Entity(v.object).state.isFakeAtm then
|
|
SetEntityAsMissionEntity(v.object, true, true)
|
|
DeleteEntity(v.object)
|
|
break
|
|
end
|
|
end
|
|
|
|
if owner == cache.serverId then
|
|
Utils.Notify(locale('sec_active', Config.Rope.breakTime), 'inform', 5000)
|
|
Citizen.SetTimeout(Config.Rope.breakTime * 1000, function()
|
|
Utils.Notify(locale('sec_deactive'), 'success', 5000)
|
|
Entity(ownedFakeAtmObject).state.robbable = true
|
|
end)
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent(_e('client:rope:DeletePlayerRope'), function(owner)
|
|
deletePlayerRope(owner)
|
|
end)
|