1053 lines
32 KiB
Lua
1053 lines
32 KiB
Lua
----------------------
|
|
-- SYNCING
|
|
----------------------
|
|
|
|
function SetNetworkShared(netId)
|
|
Citizen.CreateThread(function()
|
|
local timeout = 0
|
|
while not NetworkDoesEntityExistWithNetworkId(netId) and timeout < 10 do
|
|
Citizen.Wait(10)
|
|
timeout = timeout + 1
|
|
end
|
|
|
|
local entity = NetworkGetEntityFromNetworkId(netId)
|
|
SetEntityAsMissionEntity(entity, 1, 1)
|
|
NetworkAllowLocalEntityAttachment(entity, true)
|
|
|
|
SetEntityLoadCollisionFlag(entity, not Config.experimentalSync)
|
|
|
|
SetNetworkIdExistsOnAllMachines(netId, true)
|
|
|
|
if Config.experimentalSync then
|
|
SetNetworkIdAlwaysExistsForPlayer(netId, PlayerId(), true)
|
|
|
|
if IsEntityAVehicle(entity) then
|
|
SetNetworkVehiclePositionUpdateMultiplier(entity, 2.0)
|
|
SetNetworkEnableVehiclePositionCorrection(entity, true)
|
|
end
|
|
|
|
if tonumber(Config.experimentalSync) == 111 then
|
|
NetworkUseHighPrecisionBlending(netId, false)
|
|
SetNetworkIdCanMigrate(netId, false)
|
|
end
|
|
end
|
|
|
|
--NetworkUseHighPrecisionBlending(netId, true)
|
|
--SetNetworkIdCanMigrate(netId, false)
|
|
end)
|
|
end
|
|
|
|
function SyncToServer()
|
|
TriggerServerEvent('kq_carheist:heistCreated', heist)
|
|
end
|
|
|
|
RegisterNetEvent('kq_carheist:syncHeist')
|
|
AddEventHandler('kq_carheist:syncHeist', function(newHeist)
|
|
heist = newHeist
|
|
|
|
RemoveRelationshipGroup('kq_carheist_ped')
|
|
RemoveRelationshipGroup(robberRelationship)
|
|
|
|
SetPedRelationshipGroupDefaultHash(PlayerPedId(), GetHashKey('PLAYER'))
|
|
|
|
AddRelationshipGroup('kq_carheist_ped')
|
|
_, robberRelationship = AddRelationshipGroup('kq_carheist_robber' .. heist.hash)
|
|
|
|
|
|
RemovePoliceTrailerBlip()
|
|
SetPedsRelationships()
|
|
|
|
Debug(heist.truck)
|
|
Debug(GetTruck())
|
|
Debug(GetEntityCoords(GetTruck()))
|
|
end)
|
|
|
|
|
|
RegisterNetEvent('kq_carheist:syncSellableVehicles')
|
|
AddEventHandler('kq_carheist:syncSellableVehicles', function(newSellable)
|
|
sellableVehicles = newSellable
|
|
end)
|
|
|
|
|
|
----------------------
|
|
-- ALARM
|
|
----------------------
|
|
|
|
function CheckAlertness()
|
|
Debug('Checking alertness')
|
|
|
|
for k, ped in pairs(GetAllHeistPeds()) do
|
|
if GetPedAlertness(ped) >= 2 then
|
|
Debug('Peds were alerted. Setting off the alarm')
|
|
SetOffAlarm()
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
function SetOffAlarm()
|
|
if not heist.alarm then
|
|
heist.alarm = true
|
|
|
|
if Config.useVehicleAlarm then
|
|
SetVehicleAlarm(GetTrailer(), true)
|
|
SetVehicleAlarmTimeLeft(GetTruck(), 120000)
|
|
end
|
|
|
|
TriggerServerEvent('kq_carheist:startAlarm')
|
|
end
|
|
|
|
for k, ped in pairs(GetAllHeistPeds()) do
|
|
if ped ~= GetNPC() then
|
|
local veh = GetSupportVehicle()
|
|
if GetPedInVehicleSeat(veh, -1) ~= ped then
|
|
TaskCombatPed(ped, PlayerPedId(), 0, 16)
|
|
end
|
|
end
|
|
end
|
|
|
|
SetRelationshipBetweenGroups(5, 'kq_carheist_ped', robberRelationship)
|
|
end
|
|
|
|
RegisterNetEvent('kq_carheist:setAlarm')
|
|
AddEventHandler('kq_carheist:setAlarm', function(trailerCoords)
|
|
heist.alarm = true
|
|
if IsPolice() then
|
|
RefreshPoliceTrailerAlarm(trailerCoords)
|
|
SendDispatchMessage(L('Our car transport is under attack! Coordinates have been shared!'), L('Robbery in progress'))
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent('kq_carheist:truckArrived')
|
|
AddEventHandler('kq_carheist:truckArrived', function()
|
|
if heist.alarm then
|
|
EndPoliceAlarm()
|
|
end
|
|
end)
|
|
|
|
|
|
|
|
function DoSparkParticle()
|
|
local dict = 'core'
|
|
|
|
if not HasNamedPtfxAssetLoaded(dict) then
|
|
RequestNamedPtfxAsset(dict)
|
|
while not HasNamedPtfxAssetLoaded(dict) do
|
|
Citizen.Wait(1)
|
|
end
|
|
end
|
|
SetPtfxAssetNextCall(dict)
|
|
local sparks = StartParticleFxLoopedOnEntityBone("ent_brk_sparking_wires", grinder, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0, 1.3, 1, 1, 1)
|
|
end
|
|
|
|
|
|
----------------------
|
|
-- RAMP OPENING
|
|
----------------------
|
|
RegisterNetEvent('kq_carheist:startOpeningRamp')
|
|
AddEventHandler('kq_carheist:startOpeningRamp', function()
|
|
StartOpeningRamp()
|
|
end)
|
|
|
|
function StartOpeningRamp()
|
|
openingRamp = true
|
|
progress = 0
|
|
|
|
SetOffAlarm()
|
|
OnStartOpeningTrailer()
|
|
|
|
local playerPed = PlayerPedId()
|
|
local playerCoords = GetEntityCoords(playerPed)
|
|
|
|
SetPedRelationshipGroupHash(playerPed, robberRelationship)
|
|
|
|
local bootCoords = GetWorldPositionOfEntityBone(GetTrailer(), GetEntityBoneIndexByName(GetTrailer(), 'boot'))
|
|
|
|
local grinderModel = 'imp_prop_grinder_01a'
|
|
DoRequestModel(grinderModel)
|
|
|
|
grinder = CreateObject(grinderModel, playerCoords.x, playerCoords.y, playerCoords.z, true, true, 1)
|
|
|
|
local handBone = 6286
|
|
local grinderOffset = vector3(0.2, 0.065, -0.015)
|
|
local grinderRot = vector3(200.0, 265.0, -40.0)
|
|
local handBoneIndex = GetPedBoneIndex(playerPed, handBone)
|
|
AttachEntityToEntity(grinder, playerPed, handBoneIndex, grinderOffset.x, grinderOffset.y, grinderOffset.z, grinderRot.x, grinderRot.y, grinderRot.z, true, false, false, false, 2, true)
|
|
|
|
local heading = GetHeadingFromVector_2d(playerCoords.x - bootCoords.x, playerCoords.y - bootCoords.y) + 200
|
|
SetEntityHeading(playerPed, heading)
|
|
TaskStartScenarioInPlace(PlayerPedId(), 'WORLD_HUMAN_WELDING', 10, true)
|
|
end
|
|
|
|
function FinishOpeningRamp()
|
|
StopOpeningRamp()
|
|
heist.rampDown = true
|
|
Citizen.Wait(1000)
|
|
OpenTrailer()
|
|
end
|
|
|
|
function StopOpeningRamp()
|
|
cutting = false
|
|
openingRamp = false
|
|
|
|
local playerPed = PlayerPedId()
|
|
local playerCoords = GetEntityCoords(playerPed)
|
|
|
|
ClearPedTasks(playerPed)
|
|
|
|
Citizen.Wait(50)
|
|
local welder = GetClosestObjectOfType(playerCoords, 5.0, GetHashKey('prop_weld_torch'), false, false, false)
|
|
SetEntityAsMissionEntity(welder, true)
|
|
DeleteEntity(welder)
|
|
|
|
DeleteObject(grinder)
|
|
end
|
|
|
|
function OpenTrailer()
|
|
TriggerServerEvent('kq_carheist:openTrailer', heist.trailer)
|
|
|
|
if openingRamp then
|
|
StopOpeningRamp()
|
|
end
|
|
end
|
|
|
|
RegisterNetEvent('kq_carheist:openTrailer')
|
|
AddEventHandler('kq_carheist:openTrailer', function(networkVehicle)
|
|
heist.rampDown = true
|
|
if NetworkDoesEntityExistWithNetworkId(networkVehicle) then
|
|
SetVehicleDoorOpen(NetworkGetEntityFromNetworkId(networkVehicle), 5, false, false)
|
|
end
|
|
|
|
if heist.vehicles[3] then
|
|
DetachVehicle(3, true)
|
|
end
|
|
|
|
for k, vehicle in pairs(heist.vehicles) do
|
|
if NetworkDoesEntityExistWithNetworkId(vehicle.vehicle) then
|
|
local veh = NetworkGetEntityFromNetworkId(vehicle.vehicle)
|
|
SetVehicleDoorsLocked(veh, 0)
|
|
SetVehicleDoorsLockedForAllPlayers(veh, false)
|
|
end
|
|
end
|
|
|
|
OnTrailerOpened()
|
|
end)
|
|
|
|
function DetachVehicle(vehicleKey, falling)
|
|
Citizen.CreateThread(function()
|
|
if falling then
|
|
Citizen.Wait(1000)
|
|
end
|
|
if NetworkDoesEntityExistWithNetworkId(heist.vehicles[vehicleKey].vehicle) and NetworkHasControlOfEntity(NetworkGetEntityFromNetworkId(heist.vehicles[vehicleKey].vehicle)) then
|
|
local vehicle = NetworkGetEntityFromNetworkId(heist.vehicles[vehicleKey].vehicle)
|
|
|
|
if Config.useVehicleAlarm then
|
|
SetVehicleAlarm(vehicle, true)
|
|
SetVehicleAlarmTimeLeft(vehicle, math.random(25000, 60000))
|
|
end
|
|
|
|
DetachEntity(vehicle, 1, 1)
|
|
SetVehicleSuspensionHeight(vehicle, 0.0)
|
|
|
|
Entity(vehicle).state.ignoreLocks = true
|
|
OnVehicleDetach(vehicle)
|
|
|
|
if falling then
|
|
Citizen.Wait(1000)
|
|
end
|
|
|
|
TriggerServerEvent('kq_carheist:enableCollisions', heist.vehicles[vehicleKey].vehicle)
|
|
|
|
if falling then
|
|
local falls = 0
|
|
|
|
while falls < 45 do
|
|
local vel = GetEntityVelocity(vehicle)
|
|
SetEntityVelocity(vehicle, vel.x * 0.98, vel.y * 0.98, vel.z + 0.24)
|
|
falls = falls + 1
|
|
Citizen.Wait(100)
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
|
|
RegisterNetEvent('kq_carheist:enableCollisions')
|
|
AddEventHandler('kq_carheist:enableCollisions', function(netVehicle)
|
|
local vehicle = NetworkGetEntityFromNetworkId(netVehicle)
|
|
for k, veh in pairs(heist.vehicles) do
|
|
if NetworkDoesEntityExistWithNetworkId(veh.vehicle) then
|
|
local otherVeh = NetworkGetEntityFromNetworkId(veh.vehicle)
|
|
if otherVeh ~= vehicle then
|
|
SetEntityNoCollisionEntity(vehicle, otherVeh, true)
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
----------------------
|
|
-- HEIST CREATION
|
|
----------------------
|
|
|
|
RegisterNetEvent('kq_carheist:startDriver')
|
|
AddEventHandler('kq_carheist:startDriver', function()
|
|
StartDriver()
|
|
end)
|
|
|
|
function StartDriver()
|
|
if not heist.endLocation then
|
|
return
|
|
end
|
|
|
|
local endLoc = heist.endLocation
|
|
--TaskVehicleDriveWander(GetNPC(), GetTruck(), Config.truckDriveSpeed + 0.0, Config.truckDriveStyle)
|
|
local coords = GetEntityCoords(GetTruck())
|
|
|
|
RequestAdditionalCollisionAtCoord(coords)
|
|
RequestCollisionAtCoord(coords)
|
|
|
|
AddNavmeshRequiredRegion(coords.x, coords.y, 100.0)
|
|
|
|
if GetDistanceBetweenCoords(GetEntityCoords(GetTruck()), endLoc.x, endLoc.y, endLoc.z, true) > 15.0 then
|
|
TaskVehicleDriveToCoordLongrange(GetNPC(), GetTruck(), endLoc.x, endLoc.y, endLoc.z, Config.truckDriveSpeed + 0.0, Config.truckDriveStyle, 10.0)
|
|
end
|
|
end
|
|
|
|
RegisterNetEvent('kq_carheist:createHeist')
|
|
AddEventHandler('kq_carheist:createHeist', function(newHeist)
|
|
heist = newHeist
|
|
CreateHeist()
|
|
end)
|
|
|
|
function CreateHeist()
|
|
Citizen.CreateThread(function()
|
|
heist.truck = NetworkGetNetworkIdFromEntity(CreateTruck())
|
|
heist.trailer = NetworkGetNetworkIdFromEntity(CreateTrailer())
|
|
heist.npc = NetworkGetNetworkIdFromEntity(CreateDriver())
|
|
if heist.passengerEnabled then
|
|
heist.passenger = NetworkGetNetworkIdFromEntity(CreatePassenger())
|
|
end
|
|
|
|
if heist.supportEnabled then
|
|
Debug('create support')
|
|
heist.support = CreateSupport()
|
|
end
|
|
|
|
Citizen.Wait(10)
|
|
Debug('attach trailer')
|
|
AttachVehicleToTrailer(GetTruck(), GetTrailer(), 10.0)
|
|
|
|
Citizen.Wait(1000)
|
|
SyncToServer()
|
|
end)
|
|
end
|
|
|
|
function CreateTruck()
|
|
local availableTrucks = Config.events[heist.event].trucks
|
|
local model = availableTrucks[math.random(1, #availableTrucks)]
|
|
DoRequestModel(model)
|
|
|
|
local veh = CreateVehicle(model, heist.startLocation.truck.x, heist.startLocation.truck.y, heist.startLocation.truck.z, heist.startLocation.truck.h, 1, 0)
|
|
SetEntityAsMissionEntity(veh, true, true)
|
|
SetNetworkShared(NetworkGetNetworkIdFromEntity(veh), true)
|
|
|
|
|
|
if heist.bulletproofTiresEnabled then
|
|
SetVehicleTyresCanBurst(veh, false)
|
|
end
|
|
|
|
SetVehicleDoorsLocked(veh, 2)
|
|
SetVehicleDoorsLockedForAllPlayers(veh, true)
|
|
SetVehicleCustomPrimaryColour(veh, Config.truckColor.r, Config.truckColor.g, Config.truckColor.b)
|
|
SetVehicleCustomSecondaryColour(veh, Config.truckColor.r, Config.truckColor.g, Config.truckColor.b)
|
|
|
|
AfterTruckCreated(veh)
|
|
|
|
return veh
|
|
end
|
|
|
|
function CreateTrailer()
|
|
local model = Settings.trailer
|
|
DoRequestModel(model)
|
|
|
|
local veh = CreateVehicle(model, heist.startLocation.truck.x, heist.startLocation.truck.y, heist.startLocation.truck.z + 4.0, 0.0, 1, 0)
|
|
SetEntityAsMissionEntity(veh, true, true)
|
|
SetNetworkShared(NetworkGetNetworkIdFromEntity(veh), true)
|
|
FreezeEntityPosition(veh, true)
|
|
SetEntityProofs(veh, false, true, true, false, false, true, 1, false)
|
|
SetVehicleDoorCanBreak(veh, 5, false)
|
|
SetVehicleDoorCanBreak(veh, 4, false)
|
|
|
|
|
|
CreateTrailerVehicles(veh)
|
|
|
|
FreezeEntityPosition(veh, false)
|
|
|
|
if heist.bulletproofTiresEnabled then
|
|
SetVehicleTyresCanBurst(veh, false)
|
|
end
|
|
|
|
AfterTrailerCreated(veh)
|
|
|
|
return veh
|
|
end
|
|
|
|
function CreateTrailerVehicles(trailer)
|
|
local lastVeh = nil
|
|
for k, slot in pairs(Settings.trailerSlots) do
|
|
if heist.vehicles[k] then
|
|
local vehicle = heist.vehicles[k].data
|
|
|
|
if IsModelValid(vehicle.model) then
|
|
Debug('created trailer vehicle ' .. vehicle.model)
|
|
DoRequestModel(vehicle.model)
|
|
|
|
local coords = slot.coords
|
|
local rot = slot.rot
|
|
|
|
local veh = CreateVehicle(vehicle.model, heist.startLocation.truck.x + coords.x, heist.startLocation.truck.y + coords.y, heist.startLocation.truck.z + 4.0 + coords.z + 0.6, 0.0, 1, 0)
|
|
SetEntityAsMissionEntity(veh, true, true)
|
|
SetNetworkShared(NetworkGetNetworkIdFromEntity(veh), true)
|
|
|
|
SetVehicleModKit(veh, 0)
|
|
SetVehicleLivery(veh, math.random(0, 8))
|
|
SetVehicleMod(veh, 48, math.random(0, 8), 1)
|
|
|
|
FreezeEntityPosition(veh, true)
|
|
|
|
heist.vehicles[k] = { data = vehicle, vehicle = NetworkGetNetworkIdFromEntity(veh), tracker = true, trackerLocation = math.random(1,6), slot = slot, hash = heist.hash }
|
|
|
|
SetEntityCoords(veh, heist.startLocation.truck.x + coords.x, heist.startLocation.truck.y + coords.y, heist.startLocation.truck.z + 4.0 + coords.z + 0.5, 0, 0, 0, 0)
|
|
|
|
if DoesEntityExist(veh) then
|
|
SetVehicleSuspensionHeight(veh, 0.075)
|
|
end
|
|
|
|
local offset = GetOffsetFromEntityGivenWorldCoords(trailer, GetEntityCoords(veh))
|
|
AttachEntityToEntity(veh, trailer, 0, offset.x, offset.y, offset.z, rot.x, rot.y, rot.z, 1, 1, 1, 0, 0, 1)
|
|
|
|
SetVehicleDoorsLocked(veh, 2)
|
|
SetVehicleDoorsLockedForAllPlayers(veh, true)
|
|
|
|
AfterTrailerVehicleCreated(veh)
|
|
|
|
FreezeEntityPosition(veh, false)
|
|
|
|
if lastVeh then
|
|
SetEntityNoCollisionEntity(veh, lastVeh, false)
|
|
end
|
|
lastVeh = veh
|
|
end
|
|
end
|
|
end
|
|
Debug('created trailer vehicles')
|
|
return true
|
|
end
|
|
|
|
function CreateDriver()
|
|
local availableNPCs = Config.events[heist.event].npcs
|
|
|
|
local model = availableNPCs[math.random(1, #availableNPCs)]
|
|
DoRequestModel(model)
|
|
|
|
local driver = CreatePedInsideVehicle(GetTruck(), 0, model, -1, 1, 1)
|
|
SetNetworkShared(NetworkGetNetworkIdFromEntity(driver), true)
|
|
|
|
SetPedBaseConfig(driver)
|
|
SetBlockingOfNonTemporaryEvents(driver, true)
|
|
|
|
SetPedMaxHealth(driver, Config.npcHealth)
|
|
SetEntityHealth(driver, Config.npcHealth)
|
|
SetPedSuffersCriticalHits(driver, Config.npcSuffersCriticalHits)
|
|
|
|
AfterDriverCreated(driver)
|
|
|
|
return driver
|
|
end
|
|
|
|
function CreatePassenger()
|
|
local availableNPCs = Config.events[heist.event].npcs
|
|
|
|
local model = availableNPCs[math.random(1, #availableNPCs)]
|
|
DoRequestModel(model)
|
|
|
|
local passenger = CreatePedInsideVehicle(GetTruck(), 0, model, 0, 1, 0)
|
|
SetNetworkShared(NetworkGetNetworkIdFromEntity(passenger), true)
|
|
|
|
SetPedBaseConfig(passenger)
|
|
|
|
SetPedMaxHealth(passenger, 150)
|
|
SetEntityHealth(passenger, 150)
|
|
SetPedSuffersCriticalHits(passenger, false)
|
|
|
|
if heist.weaponsEnabled then
|
|
GiveWeaponToPed(passenger, Config.weapons[math.random(1, #Config.weapons)], 120, false, true)
|
|
end
|
|
|
|
AfterPassengerCreated(passenger)
|
|
|
|
return passenger
|
|
end
|
|
|
|
function CreateSupport()
|
|
local model = Config.supportVehicles[math.random(1, #Config.supportVehicles)]
|
|
DoRequestModel(model)
|
|
|
|
local veh = CreateVehicle(model, heist.startLocation.support.x, heist.startLocation.support.y, heist.startLocation.support.z, heist.startLocation.support.h, 1, 0)
|
|
SetEntityAsMissionEntity(veh, true, true)
|
|
SetNetworkShared(NetworkGetNetworkIdFromEntity(veh), true)
|
|
|
|
if heist.bulletproofTiresEnabled then
|
|
SetVehicleTyresCanBurst(veh, false)
|
|
end
|
|
|
|
SetVehicleDoorsLocked(veh, 2)
|
|
SetVehicleDoorsLockedForAllPlayers(veh, true)
|
|
|
|
SetVehicleModKit(veh, 0)
|
|
SetVehicleWindowTint(veh, 2)
|
|
|
|
SetVehicleModColor_1(veh, 3, 0, 0)
|
|
SetVehicleModColor_2(veh, 3, 0, 0)
|
|
SetVehicleCustomPrimaryColour(veh, Config.supportColor.r, Config.supportColor.g, Config.supportColor.b)
|
|
SetVehicleCustomSecondaryColour(veh, Config.supportColor.r, Config.supportColor.g, Config.supportColor.b)
|
|
|
|
local supportAmount = math.random(2, GetVehicleModelNumberOfSeats(model))
|
|
|
|
AfterSupportVehicleCreated(veh)
|
|
|
|
local peds = {}
|
|
|
|
for i = 1, supportAmount, 1 do
|
|
local pedModel = Config.supportPeds[math.random(1, #Config.supportPeds)]
|
|
|
|
DoRequestModel(pedModel)
|
|
|
|
local ped = CreatePedInsideVehicle(veh, 0, pedModel, i - 2, 1, 1)
|
|
SetNetworkShared(NetworkGetNetworkIdFromEntity(ped), true)
|
|
|
|
if i == 1 then
|
|
TaskVehicleEscort(ped, veh, GetTrailer(), -1, Config.truckDriveSpeed * 1.2, Config.truckDriveStyle, 25.0, 25.0)
|
|
SetBlockingOfNonTemporaryEvents(ped, true)
|
|
end
|
|
|
|
GiveWeaponToPed(ped, Config.weapons[math.random(1, #Config.weapons)], 120, false, true)
|
|
|
|
SetPedBaseConfig(ped)
|
|
|
|
SetPedMaxHealth(ped, 200)
|
|
SetEntityHealth(ped, 200)
|
|
SetPedSuffersCriticalHits(ped, false)
|
|
|
|
table.insert(peds, NetworkGetNetworkIdFromEntity(ped))
|
|
|
|
AfterSupportPedCreated(ped)
|
|
end
|
|
|
|
return { vehicle = NetworkGetNetworkIdFromEntity(veh), peds = peds }
|
|
end
|
|
|
|
function SetPedBaseConfig(ped)
|
|
SetEntityAlwaysPrerender(ped, true)
|
|
SetDriverAbility(ped, 100.0)
|
|
SetDriverAggressiveness(ped, 0.1)
|
|
SetPedAllowedToDuck(ped, 1)
|
|
SetPedArmour(ped, 100)
|
|
SetPedCombatAttributes(ped, 2, true)
|
|
SetPedCombatAttributes(ped, 3, true)
|
|
SetTaskVehicleChaseBehaviorFlag(ped, 32, true)
|
|
SetPedCanBeDraggedOut(ped, false)
|
|
SetPedKeepTask(ped, true)
|
|
SetPedCombatRange(ped, 0)
|
|
SetPedCombatAbility(ped, 0)
|
|
if Config.npcShootingAccuracy > 30 then
|
|
SetPedCombatAbility(ped, 1)
|
|
end
|
|
|
|
if Config.npcShootingAccuracy > 80 then
|
|
SetPedCombatAbility(ped, 2)
|
|
end
|
|
|
|
SetPedAccuracy(ped, Config.npcShootingAccuracy)
|
|
SetPedDropsWeaponsWhenDead(ped, false)
|
|
end
|
|
|
|
function SetPedsRelationships()
|
|
SetRelationshipBetweenGroups(0, 'kq_carheist_ped', 'kq_carheist_ped')
|
|
SetRelationshipBetweenGroups(5, 'kq_carheist_ped', robberRelationship)
|
|
|
|
local friendlyTo = { 'COP', 'SECURITY_GUARD', 'PRIVATE_SECURITY', 'FIREMAN', 'ARMY', 'CIVMALE', 'CIVFEMALE' }
|
|
for _, group in pairs(friendlyTo) do
|
|
SetRelationshipBetweenGroups(2, 'kq_carheist_ped', GetHashKey(group))
|
|
SetRelationshipBetweenGroups(2, GetHashKey(group), 'kq_carheist_ped')
|
|
end
|
|
|
|
for k, ped in pairs(GetAllHeistPeds()) do
|
|
SetPedRelationshipGroupHash(ped, 'kq_carheist_ped')
|
|
SetPedDropsWeaponsWhenDead(ped, false)
|
|
end
|
|
|
|
SetRelationshipBetweenGroups(3, 'kq_carheist_ped', GetHashKey('PLAYER'))
|
|
SetRelationshipBetweenGroups(3, GetHashKey('PLAYER'), 'kq_carheist_ped')
|
|
|
|
SetRelationshipBetweenGroups(1, GetHashKey('PLAYER'), robberRelationship)
|
|
SetRelationshipBetweenGroups(1, robberRelationship, GetHashKey('PLAYER'))
|
|
end
|
|
|
|
|
|
|
|
RegisterNetEvent('kq_carheist:announceHeist')
|
|
AddEventHandler('kq_carheist:announceHeist', function(coords)
|
|
if not IsPolice() then
|
|
SendAnnouncementMessage(L('Car transport truck is currently departing.\nI\'ll pay top dollar if you can get them'), L('Car collector'), coords)
|
|
end
|
|
end)
|
|
|
|
function CreateTemporaryBlip(coords, sprite, color, alpha, scale, message, shortRange, noLegend)
|
|
Citizen.CreateThread(function()
|
|
local blip = AddBlipForCoord(coords)
|
|
|
|
SetBlipSprite(blip, sprite)
|
|
SetBlipHighDetail(blip, true)
|
|
SetBlipColour(blip, color)
|
|
SetBlipAlpha(blip, alpha)
|
|
SetBlipScale(blip, scale)
|
|
BeginTextCommandSetBlipName('STRING')
|
|
AddTextComponentString(message)
|
|
EndTextCommandSetBlipName(blip)
|
|
SetBlipAsShortRange(blip, shortRange)
|
|
if noLegend then
|
|
SetBlipDisplay(blip, 8)
|
|
end
|
|
|
|
Citizen.Wait(Config.announcementBlip.duration or 15000)
|
|
RemoveBlip(blip)
|
|
end)
|
|
end
|
|
----------------------
|
|
-- TRACKERS
|
|
----------------------
|
|
|
|
function CheckForTracker(vehKey, trackerKey, textCoords, veh)
|
|
local playerPed = PlayerPedId()
|
|
local vehCoords = GetEntityCoords(veh)
|
|
Debug(vehCoords)
|
|
local playerCoords = GetEntityCoords(playerPed)
|
|
Debug(playerCoords)
|
|
|
|
|
|
local heading = GetHeadingFromVector_2d(playerCoords.x - vehCoords.x, playerCoords.y - vehCoords.y) + 180.0
|
|
SetEntityHeading(playerPed, heading)
|
|
PlayAnim('anim@amb@clubhouse@tutorial@bkr_tut_ig3@', 'machinic_loop_mechandplayer')
|
|
|
|
local searching = true
|
|
Citizen.CreateThread(function()
|
|
while searching do
|
|
Draw3DText(textCoords.x, textCoords.y, textCoords.z + 0.2, L('~w~Searching for a tracking device'), 4, 0.04, 0.04)
|
|
Draw3DText(textCoords.x, textCoords.y, textCoords.z + 0.1, L('~w~Press [~r~E~w~] to cancel'), 4, 0.035, 0.035)
|
|
if IsControlJustReleased(0, Keys['E']) or IsPedRagdoll(playerPed) or GetDistanceBetweenCoords(textCoords, playerCoords, true) > 3.0 then
|
|
searching = false
|
|
end
|
|
Citizen.Wait(1)
|
|
end
|
|
end)
|
|
|
|
local waits = Config.tracker.searchDuration / 1000
|
|
while waits > 0 and searching do
|
|
Citizen.Wait(1000)
|
|
waits = waits - 1
|
|
end
|
|
|
|
if not searching then
|
|
ClearPedTasks(playerPed)
|
|
end
|
|
|
|
if searching then
|
|
searching = false
|
|
|
|
if sellableVehicles[vehKey].trackerLocation == trackerKey then
|
|
local removing = true
|
|
|
|
local removalKeybinds = Config.tracker.removalKeybinds
|
|
local keyKey = #removalKeybinds
|
|
local progress = 0
|
|
|
|
while progress < Config.tracker.removalLength and sellableVehicles[vehKey].tracker and removing do
|
|
local currentKey = Config.tracker.removalKeybinds[keyKey]
|
|
|
|
local text = '~w~'
|
|
for k, key in pairs(removalKeybinds) do
|
|
if key ~= currentKey then
|
|
text = text .. '~w~' .. key
|
|
if IsControlJustReleased(0, Keys[key]) then
|
|
progress = progress - 2
|
|
currentKey = nil
|
|
local display = true
|
|
Citizen.CreateThread(function()
|
|
while display and removing do
|
|
Draw3DText(textCoords.x, textCoords.y, textCoords.z, L('~r~You dropped the screwdriver'), 4, 0.04, 0.04)
|
|
Draw3DText(textCoords.x, textCoords.y, textCoords.z - 0.1, L('~w~Press [~r~E~w~] to cancel'), 4, 0.035, 0.035)
|
|
if IsControlJustReleased(0, Keys['E']) or IsPedRagdoll(playerPed) or GetDistanceBetweenCoords(textCoords, playerCoords, true) > 3.0 then
|
|
removing = false
|
|
ClearPedTasks(playerPed)
|
|
end
|
|
Citizen.Wait(1)
|
|
end
|
|
end)
|
|
Citizen.Wait(Config.tracker.removalMessedUpDuration)
|
|
display = false
|
|
end
|
|
else
|
|
text = text .. '~r~' .. currentKey
|
|
end
|
|
end
|
|
|
|
if IsControlJustReleased(0, Keys[currentKey]) then
|
|
progress = progress + 1
|
|
|
|
keyKey = keyKey - 1
|
|
if keyKey < 1 then
|
|
keyKey = #removalKeybinds
|
|
end
|
|
end
|
|
|
|
Draw3DText(textCoords.x, textCoords.y, textCoords.z + 0.2, L('~g~Tracking device found!'), 4, 0.04, 0.04)
|
|
Draw3DText(textCoords.x, textCoords.y, textCoords.z, '~w~' .. (L('Press ~w~[{keybinds}~w~] to unscrew')):gsub('{keybinds}', text), 4, 0.04, 0.04)
|
|
Draw3DText(textCoords.x, textCoords.y, textCoords.z - 0.1, L('~w~Press [~r~E~w~] to cancel'), 4, 0.035, 0.035)
|
|
if IsControlJustReleased(0, Keys['E']) or IsPedRagdoll(playerPed) or GetDistanceBetweenCoords(textCoords, playerCoords, true) > 3.0 then
|
|
removing = false
|
|
ClearPedTasks(playerPed)
|
|
end
|
|
Citizen.Wait(1)
|
|
end
|
|
|
|
if removing then
|
|
removing = false
|
|
|
|
if sellableVehicles[vehKey].tracker then
|
|
TriggerServerEvent('kq_carheist:removeTracker', vehKey, textCoords)
|
|
CreateTrackerObject()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
ClearPedTasks(playerPed)
|
|
end
|
|
|
|
function CreateTrackerObject()
|
|
local trackerModel = 'prop_cs_mini_tv'
|
|
DoRequestModel(trackerModel)
|
|
|
|
local playerPed = PlayerPedId()
|
|
local playerCoords = GetEntityCoords(playerPed)
|
|
|
|
local trackerObj = CreateObject(trackerModel, playerCoords.x, playerCoords.y, playerCoords.z, true, true, 1)
|
|
SetEntityDynamic(trackerObj, true)
|
|
end
|
|
|
|
----------------------
|
|
-- DROP OFF
|
|
----------------------
|
|
function RemoveDropOffs()
|
|
RemoveDropOffBlips()
|
|
currentDropOffs = {}
|
|
end
|
|
|
|
function CreateNewDropOffs(hash)
|
|
Debug('create dropoff')
|
|
math.randomseed(hash)
|
|
|
|
local amount = math.random(Config.dropOff.minLocations, Config.dropOff.maxLocations)
|
|
|
|
currentDropOffs = {}
|
|
while #currentDropOffs < amount do
|
|
local newLoc = Config.dropOff.locations[math.random(1, #Config.dropOff.locations)]
|
|
if not Contains(currentDropOffs, newLoc) then
|
|
table.insert(currentDropOffs, newLoc)
|
|
end
|
|
Citizen.Wait(1)
|
|
end
|
|
Debug('Got ' .. #currentDropOffs)
|
|
|
|
RemoveDropOffBlips()
|
|
for k, dropOff in pairs(currentDropOffs) do
|
|
|
|
Debug(json.encode(dropOff))
|
|
local blipConf = Config.dropOff.blips.primary
|
|
local primBlip = CreateDropOffBlip(vector3(dropOff.x, dropOff.y, dropOff.z), blipConf.sprite, blipConf.color, blipConf.alpha, blipConf.scale, L('Stolen vehicle drop off point'), blipConf.shortRange)
|
|
SetBlipDisplay(primBlip, 8)
|
|
table.insert(dropOffBlips, primBlip)
|
|
|
|
blipConf = Config.dropOff.blips.secondary
|
|
local secBlip = CreateDropOffBlip(vector3(dropOff.x, dropOff.y, dropOff.z), blipConf.sprite, blipConf.color, blipConf.alpha, blipConf.scale, L('Stolen vehicle drop off point'), blipConf.shortRange)
|
|
table.insert(dropOffBlips, secBlip)
|
|
end
|
|
|
|
|
|
math.randomseed(GetGameTimer())
|
|
end
|
|
|
|
function RemoveDropOffBlips()
|
|
for b, blip in pairs(dropOffBlips) do
|
|
RemoveBlip(blip)
|
|
end
|
|
dropOffBlips = {}
|
|
end
|
|
|
|
function CreateDropOffBlip(coords, sprite, color, alpha, scale, message, shortRange)
|
|
local blip = AddBlipForCoord(coords)
|
|
|
|
SetBlipSprite(blip, sprite)
|
|
SetBlipHighDetail(blip, true)
|
|
SetBlipColour(blip, color)
|
|
SetBlipAlpha(blip, alpha)
|
|
SetBlipScale(blip, scale)
|
|
BeginTextCommandSetBlipName('STRING')
|
|
AddTextComponentString(message)
|
|
EndTextCommandSetBlipName(blip)
|
|
SetBlipAsShortRange(blip, shortRange)
|
|
return blip
|
|
end
|
|
|
|
|
|
function DropOffVehicle(vehicle, dropCoords)
|
|
if DoesEntityExist(vehicle) then
|
|
TriggerServerEvent('kq_carheist:dropOffVehicle', NetworkGetNetworkIdFromEntity(vehicle), dropCoords)
|
|
|
|
if DoesEntityExist(vehicle) then
|
|
SetVehicleDoorsLocked(vehicle, 2)
|
|
SetVehicleUndriveable(vehicle, true)
|
|
SetVehicleAsNoLongerNeeded(vehicle)
|
|
SetEntityCleanupByEngine(vehicle, true)
|
|
end
|
|
end
|
|
end
|
|
|
|
----------------------
|
|
-- GETTERS
|
|
----------------------
|
|
function GetTruck()
|
|
return GetSomething('truck')
|
|
end
|
|
|
|
function GetTrailer()
|
|
return GetSomething('trailer')
|
|
end
|
|
|
|
function GetNPC()
|
|
return GetSomething('npc')
|
|
end
|
|
|
|
function GetPassenger()
|
|
return GetSomething('passenger')
|
|
end
|
|
|
|
function GetSomething(key)
|
|
if not heist[key] then
|
|
return nil
|
|
end
|
|
|
|
if not NetworkDoesEntityExistWithNetworkId(heist[key]) then
|
|
return nil
|
|
end
|
|
|
|
local entity = NetworkGetEntityFromNetworkId(heist[key])
|
|
|
|
if DoesEntityExist(entity) then
|
|
return entity
|
|
end
|
|
return false
|
|
end
|
|
|
|
function GetSupportVehicle()
|
|
if not heist.supportEnabled or not heist.support then
|
|
return nil
|
|
end
|
|
|
|
if not NetworkDoesEntityExistWithNetworkId(heist.support.vehicle) then
|
|
return nil
|
|
end
|
|
|
|
local entity = NetworkGetEntityFromNetworkId(heist.support.vehicle)
|
|
|
|
if DoesEntityExist(entity) then
|
|
return entity
|
|
end
|
|
return nil
|
|
end
|
|
|
|
|
|
function GetAllHeistPeds()
|
|
local peds = { GetNPC() }
|
|
if GetPassenger() then
|
|
table.insert(peds, GetPassenger())
|
|
end
|
|
|
|
if heist.supportEnabled and heist.support and #heist.support.peds then
|
|
for k, netId in pairs(heist.support.peds) do
|
|
if NetworkDoesEntityExistWithNetworkId(netId) then
|
|
local ped = NetworkGetEntityFromNetworkId(netId)
|
|
table.insert(peds, ped)
|
|
end
|
|
end
|
|
end
|
|
|
|
return peds
|
|
end
|
|
|
|
function IsHeistVehicle(veh)
|
|
for k, vehicle in pairs(heist.vehicles) do
|
|
if NetworkDoesEntityExistWithNetworkId(vehicle.vehicle) then
|
|
local hVeh = NetworkGetEntityFromNetworkId(vehicle.vehicle)
|
|
if veh == hVeh then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function IsOnBike()
|
|
if IsPedInAnyVehicle(PlayerPedId()) then
|
|
local veh = GetVehiclePedIsIn(PlayerPedId())
|
|
if GetVehicleClass(veh) == 8 or GetVehicleClass(veh) == 13 then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
function IsInsideATunnel(coords)
|
|
local tunnels = {9, 12, 6, 15, 7, 3, 21, 4, 18, 5}
|
|
return Contains(tunnels, GetInteriorGroupId(GetInteriorAtCoords(coords)))
|
|
end
|
|
|
|
----------------------
|
|
-- HELPERS
|
|
----------------------
|
|
function DoRequestModel(model)
|
|
local hash = GetHashKey(model)
|
|
RequestModel(hash)
|
|
local timeout = 0
|
|
while not HasModelLoaded(hash) and timeout < 100 do
|
|
Citizen.Wait(100)
|
|
timeout = timeout + 1
|
|
end
|
|
end
|
|
|
|
function Contains(tab, val)
|
|
for index, value in ipairs(tab) do
|
|
if value == val then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function DoRequestAnimDict(dict)
|
|
RequestAnimDict(dict)
|
|
local timeout = 0
|
|
while not HasAnimDictLoaded(dict) do
|
|
Citizen.Wait(50)
|
|
timeout = timeout + 1
|
|
if timeout > 100 then
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
function PlayAnim(dict, anim, flag)
|
|
Citizen.CreateThread(function()
|
|
RequestAnimDict(dict)
|
|
local timeout = 0
|
|
while not HasAnimDictLoaded(dict) do
|
|
Citizen.Wait(50)
|
|
timeout = timeout + 1
|
|
if timeout > 100 then
|
|
return
|
|
end
|
|
end
|
|
TaskPlayAnim(PlayerPedId(), dict, anim, 4.0, 8.0, 5.0, flag or 1, 0, false, false, false)
|
|
RemoveAnimDict(dict)
|
|
end)
|
|
end
|
|
|
|
function Shuffle(tbl)
|
|
-- suffles numeric indices
|
|
local len, random = #tbl, math.random;
|
|
for i = len, 2, -1 do
|
|
local j = random(1, i);
|
|
tbl[i], tbl[j] = tbl[j], tbl[i];
|
|
end
|
|
return tbl;
|
|
end
|
|
|
|
function string.insert(str1, str2, pos)
|
|
return str1:sub(1, pos) .. str2 .. str1:sub(pos + 1)
|
|
end
|
|
|
|
function myDot(a, b)
|
|
return (a[1] * b[1]) + (a[2] * b[2]) + (a[3] * b[3])
|
|
end
|
|
|
|
function myMag(a)
|
|
return math.sqrt((a[1] * a[1]) + (a[2] * a[2]) + (a[3] * a[3]))
|
|
end
|
|
|
|
RegisterNetEvent('kq_carheist:notify')
|
|
AddEventHandler('kq_carheist:notify', function(message)
|
|
ShowTooltip(message)
|
|
end)
|
|
|
|
function Debug(message)
|
|
if Config.debug then
|
|
print(message)
|
|
end
|
|
end
|
|
|
|
|
|
local alertTitle = nil
|
|
local alertMessage = nil
|
|
local alertActive = false
|
|
function Alert(title, message, duration)
|
|
Citizen.CreateThread(function()
|
|
alertTitle = title
|
|
alertMessage = message
|
|
alertActive = true
|
|
Citizen.Wait(duration)
|
|
alertActive = false
|
|
end)
|
|
end
|
|
|
|
Citizen.CreateThread(function()
|
|
while true do
|
|
local sleep = 500
|
|
if alertActive then
|
|
sleep = 1
|
|
local scaleform = RequestScaleformMovie('MP_BIG_MESSAGE_FREEMODE')
|
|
while not HasScaleformMovieLoaded(scaleform) do
|
|
Citizen.Wait(1)
|
|
end
|
|
PushScaleformMovieFunction(scaleform, 'SHOW_SHARD_WASTED_MP_MESSAGE')
|
|
PushScaleformMovieFunctionParameterString(alertTitle)
|
|
PushScaleformMovieFunctionParameterString(alertMessage)
|
|
PopScaleformMovieFunctionVoid()
|
|
DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0)
|
|
end
|
|
|
|
Citizen.Wait(sleep)
|
|
end
|
|
end)
|
|
|
|
function L(text)
|
|
if Locale and Locale[text] then
|
|
return Locale[text]
|
|
end
|
|
|
|
return text
|
|
end
|