669 lines
30 KiB
Lua
669 lines
30 KiB
Lua
|
local QBCore = exports['qb-core']:GetCoreObject()
|
||
|
local pedInSameVehicleLast=false
|
||
|
local vehicle
|
||
|
local lastVehicle
|
||
|
local vehicleClass
|
||
|
local fCollisionDamageMult = 0.0
|
||
|
local fDeformationDamageMult = 0.0
|
||
|
local fEngineDamageMult = 0.0
|
||
|
local fBrakeForce = 1.0
|
||
|
local isBrakingForward = false
|
||
|
local isBrakingReverse = false
|
||
|
local healthEngineLast = 1000.0
|
||
|
local healthEngineCurrent = 1000.0
|
||
|
local healthEngineNew = 1000.0
|
||
|
local healthEngineDelta = 0.0
|
||
|
local healthEngineDeltaScaled = 0.0
|
||
|
local healthBodyLast = 1000.0
|
||
|
local healthBodyCurrent = 1000.0
|
||
|
local healthBodyNew = 1000.0
|
||
|
local healthBodyDelta = 0.0
|
||
|
local healthBodyDeltaScaled = 0.0
|
||
|
local healthPetrolTankLast = 1000.0
|
||
|
local healthPetrolTankCurrent = 1000.0
|
||
|
local healthPetrolTankNew = 1000.0
|
||
|
local healthPetrolTankDelta = 0.0
|
||
|
local healthPetrolTankDeltaScaled = 0.0
|
||
|
local tireBurstLuckyNumber
|
||
|
local fixMessagePos = math.random(repairCfg.fixMessageCount)
|
||
|
local noFixMessagePos = math.random(repairCfg.noFixMessageCount)
|
||
|
local tireBurstMaxNumber = cfg.randomTireBurstInterval * 1200;
|
||
|
if cfg.randomTireBurstInterval ~= 0 then tireBurstLuckyNumber = math.random(tireBurstMaxNumber) end
|
||
|
|
||
|
local DamageComponents = {
|
||
|
"radiator",
|
||
|
"axle",
|
||
|
"clutch",
|
||
|
"fuel",
|
||
|
"brakes",
|
||
|
}
|
||
|
|
||
|
-- Functions
|
||
|
|
||
|
-- local function DamageRandomComponent()
|
||
|
-- local dmgFctr = math.random() + math.random(0, 2)
|
||
|
-- local randomComponent = DamageComponents[math.random(1, #DamageComponents)]
|
||
|
-- local randomDamage = (math.random() + math.random(0, 1)) * dmgFctr
|
||
|
-- exports['qb-mechanicjob']:SetVehicleStatus(QBCore.Functions.GetPlate(vehicle), randomComponent, exports['qb-mechanicjob']:GetVehicleStatus(QBCore.Functions.GetPlate(vehicle), randomComponent) - randomDamage)
|
||
|
-- end
|
||
|
|
||
|
local function CleanVehicle(veh)
|
||
|
local ped = PlayerPedId()
|
||
|
TaskStartScenarioInPlace(ped, "WORLD_HUMAN_MAID_CLEAN", 0, true)
|
||
|
QBCore.Functions.Progressbar("cleaning_vehicle", Lang:t("progress.clean_veh"), math.random(10000, 20000), false, true, {
|
||
|
disableMovement = true,
|
||
|
disableCarMovement = true,
|
||
|
disableMouse = false,
|
||
|
disableCombat = true,
|
||
|
}, {}, {}, {}, function() -- Done
|
||
|
QBCore.Functions.Notify(Lang:t("success.cleaned_veh"))
|
||
|
SetVehicleDirtLevel(veh, 0.1)
|
||
|
SetVehicleUndriveable(veh, false)
|
||
|
WashDecalsFromVehicle(veh, 1.0)
|
||
|
TriggerServerEvent('qb-vehiclefailure:server:removewashingkit', veh)
|
||
|
TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["cleaningkit"], "remove")
|
||
|
ClearAllPedProps(ped)
|
||
|
ClearPedTasks(ped)
|
||
|
end, function() -- Cancel
|
||
|
QBCore.Functions.Notify(Lang:t("error.failed_notification"), "error")
|
||
|
ClearAllPedProps(ped)
|
||
|
ClearPedTasks(ped)
|
||
|
end)
|
||
|
end
|
||
|
|
||
|
local function IsBackEngine(vehModel)
|
||
|
if BackEngineVehicles[vehModel] then return true else return false end
|
||
|
end
|
||
|
|
||
|
local function RepairVehicleFull(veh)
|
||
|
if (IsBackEngine(GetEntityModel(veh))) then
|
||
|
SetVehicleDoorOpen(veh, 5, false, false)
|
||
|
else
|
||
|
SetVehicleDoorOpen(veh, 4, false, false)
|
||
|
end
|
||
|
|
||
|
QBCore.Functions.Progressbar("repair_vehicle", Lang:t("progress.repair_veh"), math.random(20000, 30000), false, true, {
|
||
|
disableMovement = true,
|
||
|
disableCarMovement = true,
|
||
|
disableMouse = false,
|
||
|
disableCombat = true,
|
||
|
}, {
|
||
|
animDict = "mini@repair",
|
||
|
anim = "fixing_a_player",
|
||
|
flags = 1,
|
||
|
}, {}, {}, function() -- Done
|
||
|
StopAnimTask(PlayerPedId(), "mini@repair", "fixing_a_player", 1.0)
|
||
|
QBCore.Functions.Notify(Lang:t("success.repaired_veh"))
|
||
|
SetVehicleEngineHealth(veh, 1000.0)
|
||
|
SetVehicleEngineOn(veh, true, false)
|
||
|
for i = 0, 5 do
|
||
|
SetVehicleTyreFixed(veh, i)
|
||
|
TriggerEvent('qb-vehiclefailure:client:TyreSync', veh, i)
|
||
|
end
|
||
|
if (IsBackEngine(GetEntityModel(veh))) then
|
||
|
SetVehicleDoorShut(veh, 5, false)
|
||
|
else
|
||
|
SetVehicleDoorShut(veh, 4, false)
|
||
|
end
|
||
|
TriggerServerEvent('qb-vehiclefailure:removeItem', "advancedrepairkit")
|
||
|
end, function() -- Cancel
|
||
|
StopAnimTask(PlayerPedId(), "mini@repair", "fixing_a_player", 1.0)
|
||
|
QBCore.Functions.Notify(Lang:t("error.failed_notification"), "error")
|
||
|
if (IsBackEngine(GetEntityModel(veh))) then
|
||
|
SetVehicleDoorShut(veh, 5, false)
|
||
|
else
|
||
|
SetVehicleDoorShut(veh, 4, false)
|
||
|
end
|
||
|
end)
|
||
|
end
|
||
|
|
||
|
local function RepairVehicle(veh)
|
||
|
if (IsBackEngine(GetEntityModel(veh))) then
|
||
|
SetVehicleDoorOpen(veh, 5, false, false)
|
||
|
else
|
||
|
SetVehicleDoorOpen(veh, 4, false, false)
|
||
|
end
|
||
|
QBCore.Functions.Progressbar("repair_vehicle", Lang:t("progress.repair_veh"), math.random(10000, 20000), false, true, {
|
||
|
disableMovement = true,
|
||
|
disableCarMovement = true,
|
||
|
disableMouse = false,
|
||
|
disableCombat = true,
|
||
|
}, {
|
||
|
animDict = "mini@repair",
|
||
|
anim = "fixing_a_player",
|
||
|
flags = 1,
|
||
|
}, {}, {}, function() -- Done
|
||
|
StopAnimTask(PlayerPedId(), "mini@repair", "fixing_a_player", 1.0)
|
||
|
QBCore.Functions.Notify(Lang:t("success.repaired_veh"))
|
||
|
SetVehicleEngineHealth(veh, 500.0)
|
||
|
SetVehicleEngineOn(veh, true, false)
|
||
|
for i = 0, 5 do
|
||
|
SetVehicleTyreFixed(veh, i)
|
||
|
TriggerEvent('qb-vehiclefailure:client:TyreSync', veh, i)
|
||
|
end
|
||
|
if (IsBackEngine(GetEntityModel(veh))) then
|
||
|
SetVehicleDoorShut(veh, 5, false)
|
||
|
else
|
||
|
SetVehicleDoorShut(veh, 4, false)
|
||
|
end
|
||
|
TriggerServerEvent('qb-vehiclefailure:removeItem', "repairkit")
|
||
|
end, function() -- Cancel
|
||
|
StopAnimTask(PlayerPedId(), "mini@repair", "fixing_a_player", 1.0)
|
||
|
QBCore.Functions.Notify(Lang:t("error.failed_notification"), "error")
|
||
|
if (IsBackEngine(GetEntityModel(veh))) then
|
||
|
SetVehicleDoorShut(veh, 5, false)
|
||
|
else
|
||
|
SetVehicleDoorShut(veh, 4, false)
|
||
|
end
|
||
|
end)
|
||
|
end
|
||
|
|
||
|
local function isPedDrivingAVehicle()
|
||
|
local ped = PlayerPedId()
|
||
|
vehicle = GetVehiclePedIsIn(ped, false)
|
||
|
if IsPedInAnyVehicle(ped, false) then
|
||
|
-- Check if ped is in driver seat
|
||
|
if GetPedInVehicleSeat(vehicle, -1) == ped then
|
||
|
local class = GetVehicleClass(vehicle)
|
||
|
-- We don't want planes, helicopters, bicycles and trains
|
||
|
if class ~= 15 and class ~= 16 and class ~=21 and class ~=13 then
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
local function IsNearMechanic()
|
||
|
local ped = PlayerPedId()
|
||
|
local pedLocation = GetEntityCoords(ped, 0)
|
||
|
for _, item in pairs(repairCfg.mechanics) do
|
||
|
local distance = #(vector3(item.x, item.y, item.z) - pedLocation)
|
||
|
if distance <= item.r then
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function fscale(inputValue, originalMin, originalMax, newBegin, newEnd, curve)
|
||
|
local OriginalRange
|
||
|
local NewRange
|
||
|
local zeroRefCurVal
|
||
|
local normalizedCurVal
|
||
|
local rangedValue
|
||
|
local invFlag = 0
|
||
|
|
||
|
if (curve > 10.0) then curve = 10.0 end
|
||
|
if (curve < -10.0) then curve = -10.0 end
|
||
|
|
||
|
curve = (curve * -.1)
|
||
|
curve = 10.0 ^ curve
|
||
|
|
||
|
if (inputValue < originalMin) then
|
||
|
inputValue = originalMin
|
||
|
end
|
||
|
if inputValue > originalMax then
|
||
|
inputValue = originalMax
|
||
|
end
|
||
|
|
||
|
OriginalRange = originalMax - originalMin
|
||
|
|
||
|
if (newEnd > newBegin) then
|
||
|
NewRange = newEnd - newBegin
|
||
|
else
|
||
|
NewRange = newBegin - newEnd
|
||
|
invFlag = 1
|
||
|
end
|
||
|
|
||
|
zeroRefCurVal = inputValue - originalMin
|
||
|
normalizedCurVal = zeroRefCurVal / OriginalRange
|
||
|
|
||
|
if (originalMin > originalMax ) then
|
||
|
return 0
|
||
|
end
|
||
|
|
||
|
if (invFlag == 0) then
|
||
|
rangedValue = ((normalizedCurVal ^ curve) * NewRange) + newBegin
|
||
|
else
|
||
|
rangedValue = newBegin - ((normalizedCurVal ^ curve) * NewRange)
|
||
|
end
|
||
|
|
||
|
return rangedValue
|
||
|
end
|
||
|
|
||
|
local function tireBurstLottery()
|
||
|
local tireBurstNumber = math.random(tireBurstMaxNumber)
|
||
|
if tireBurstNumber == tireBurstLuckyNumber then
|
||
|
-- We won the lottery, lets burst a tire.
|
||
|
if GetVehicleTyresCanBurst(vehicle) == false then return end
|
||
|
local numWheels = GetVehicleNumberOfWheels(vehicle)
|
||
|
local affectedTire
|
||
|
if numWheels == 2 then
|
||
|
affectedTire = (math.random(2) - 1) * 4 -- wheel 0 or 4
|
||
|
elseif numWheels == 4 then
|
||
|
affectedTire = (math.random(4) - 1)
|
||
|
if affectedTire > 1 then affectedTire = affectedTire + 2 end -- 0, 1, 4, 5
|
||
|
elseif numWheels == 6 then
|
||
|
affectedTire = (math.random(6) - 1)
|
||
|
else
|
||
|
affectedTire = 0
|
||
|
end
|
||
|
SetVehicleTyreBurst(vehicle, affectedTire, false, 1000.0)
|
||
|
tireBurstLuckyNumber = math.random(tireBurstMaxNumber) -- Select a new number to hit, just in case some numbers occur more often than others
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Events
|
||
|
|
||
|
RegisterNetEvent('qb-vehiclefailure:client:RepairVehicle', function()
|
||
|
local veh = QBCore.Functions.GetClosestVehicle()
|
||
|
local engineHealth = GetVehicleEngineHealth(veh) --This is to prevent people from "repairing" a vehicle and setting engine health lower than what the vehicles engine health was before repairing.
|
||
|
if veh ~= nil and veh ~= 0 and engineHealth < 500 then
|
||
|
local ped = PlayerPedId()
|
||
|
local pos = GetEntityCoords(ped)
|
||
|
local vehpos = GetEntityCoords(veh)
|
||
|
if #(pos - vehpos) < 5.0 and not IsPedInAnyVehicle(ped) then
|
||
|
local drawpos = GetOffsetFromEntityInWorldCoords(veh, 0, 2.5, 0)
|
||
|
if (IsBackEngine(GetEntityModel(veh))) then
|
||
|
drawpos = GetOffsetFromEntityInWorldCoords(veh, 0, -2.5, 0)
|
||
|
end
|
||
|
if #(pos - drawpos) < 2.0 and not IsPedInAnyVehicle(ped) then
|
||
|
RepairVehicle(veh)
|
||
|
end
|
||
|
else
|
||
|
if #(pos - vehpos) > 4.9 then
|
||
|
QBCore.Functions.Notify(Lang:t("error.out_range_veh"), "error")
|
||
|
else
|
||
|
QBCore.Functions.Notify(Lang:t("error.inside_veh"), "error")
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
if veh == nil or veh == 0 then
|
||
|
QBCore.Functions.Notify(Lang:t("error.not_near_veh"), "error")
|
||
|
else
|
||
|
QBCore.Functions.Notify(Lang:t("error.healthy_veh"), "error")
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('qb-vehiclefailure:client:SyncWash', function(veh)
|
||
|
SetVehicleDirtLevel(veh, 0.1)
|
||
|
SetVehicleUndriveable(veh, false)
|
||
|
WashDecalsFromVehicle(veh, 1.0)
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('qb-vehiclefailure:client:CleanVehicle', function()
|
||
|
local veh = QBCore.Functions.GetClosestVehicle()
|
||
|
if veh ~= nil and veh ~= 0 then
|
||
|
local ped = PlayerPedId()
|
||
|
local pos = GetEntityCoords(ped)
|
||
|
local vehpos = GetEntityCoords(veh)
|
||
|
if #(pos - vehpos) < 3.0 and not IsPedInAnyVehicle(ped) then
|
||
|
CleanVehicle(veh)
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('qb-vehiclefailure:client:TyreSync', function(veh, tyre)
|
||
|
SetVehicleTyreFixed(veh, tyre)
|
||
|
SetVehicleWheelHealth(veh, tyre, 100)
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('qb-vehiclefailure:client:RepairVehicleFull', function()
|
||
|
local veh = QBCore.Functions.GetClosestVehicle()
|
||
|
if veh ~= nil and veh ~= 0 then
|
||
|
local ped = PlayerPedId()
|
||
|
local pos = GetEntityCoords(ped)
|
||
|
local vehpos = GetEntityCoords(veh)
|
||
|
if #(pos - vehpos) < 5.0 and not IsPedInAnyVehicle(ped) then
|
||
|
local drawpos = GetOffsetFromEntityInWorldCoords(veh, 0, 2.5, 0)
|
||
|
if (IsBackEngine(GetEntityModel(veh))) then
|
||
|
drawpos = GetOffsetFromEntityInWorldCoords(veh, 0, -2.5, 0)
|
||
|
end
|
||
|
if #(pos - drawpos) < 2.0 and not IsPedInAnyVehicle(ped) then
|
||
|
RepairVehicleFull(veh)
|
||
|
end
|
||
|
else
|
||
|
if #(pos - vehpos) > 4.9 then
|
||
|
QBCore.Functions.Notify(Lang:t("error.out_range_veh"), "error")
|
||
|
else
|
||
|
QBCore.Functions.Notify(Lang:t("error.inside_veh"), "error")
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
QBCore.Functions.Notify(Lang:t("error.not_near_veh"), "error")
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('iens:repaira', function()
|
||
|
local ped = PlayerPedId()
|
||
|
if IsPedInAnyVehicle(ped, false) or IsPedInAnyPlane(ped) then
|
||
|
vehicle = GetVehiclePedIsIn(ped, false)
|
||
|
SetVehicleDirtLevel(vehicle)
|
||
|
SetVehicleUndriveable(vehicle, false)
|
||
|
WashDecalsFromVehicle(vehicle, 1.0)
|
||
|
QBCore.Functions.Notify(Lang:t("success.repaired_veh"))
|
||
|
SetVehicleFixed(vehicle)
|
||
|
healthBodyLast = 1000.0
|
||
|
healthEngineLast = 1000.0
|
||
|
healthPetrolTankLast = 1000.0
|
||
|
SetVehicleEngineOn(vehicle, true, false )
|
||
|
return true
|
||
|
end
|
||
|
QBCore.Functions.Notify(Lang:t("error.inside_veh_req"))
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('iens:besked', function()
|
||
|
QBCore.Functions.Notify(Lang:t("error.roadside_avail"))
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('iens:notAllowed', function()
|
||
|
QBCore.Functions.Notify(Lang:t("error.no_permission"))
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('iens:repair', function()
|
||
|
if isPedDrivingAVehicle() then
|
||
|
local ped = PlayerPedId()
|
||
|
vehicle = GetVehiclePedIsIn(ped, false)
|
||
|
if IsNearMechanic() then
|
||
|
return
|
||
|
end
|
||
|
if GetVehicleEngineHealth(vehicle) < cfg.cascadingFailureThreshold + 5 then
|
||
|
if GetVehicleOilLevel(vehicle) > 0 then
|
||
|
SetVehicleUndriveable(vehicle, false)
|
||
|
SetVehicleEngineHealth(vehicle, cfg.cascadingFailureThreshold + 5)
|
||
|
SetVehiclePetrolTankHealth(vehicle, 750.0)
|
||
|
healthEngineLast=cfg.cascadingFailureThreshold +5
|
||
|
healthPetrolTankLast=750.0
|
||
|
SetVehicleEngineOn(vehicle, true, false )
|
||
|
SetVehicleOilLevel(vehicle,(GetVehicleOilLevel(vehicle)/3)-0.5)
|
||
|
QBCore.Functions.Notify(Lang:t(('fix_message_%s'):format(fixMessagePos)))
|
||
|
fixMessagePos = fixMessagePos + 1
|
||
|
if fixMessagePos > repairCfg.fixMessageCount then fixMessagePos = 1 end
|
||
|
else
|
||
|
QBCore.Functions.Notify(Lang:t("error.veh_damaged"))
|
||
|
end
|
||
|
else
|
||
|
QBCore.Functions.Notify(Lang:t(('nofix_message_%s'):format(noFixMessagePos)))
|
||
|
noFixMessagePos = noFixMessagePos + 1
|
||
|
if noFixMessagePos > repairCfg.noFixMessageCount then noFixMessagePos = 1 end
|
||
|
end
|
||
|
else
|
||
|
QBCore.Functions.Notify(Lang:t("error.inside_veh_req"))
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
-- Threads
|
||
|
|
||
|
CreateThread(function()
|
||
|
if (cfg.displayBlips == true) then
|
||
|
for _, item in pairs(repairCfg.mechanics) do
|
||
|
item.blip = AddBlipForCoord(item.x, item.y, item.z)
|
||
|
SetBlipSprite(item.blip, item.id)
|
||
|
SetBlipScale(item.blip, 0.8)
|
||
|
SetBlipAsShortRange(item.blip, true)
|
||
|
BeginTextCommandSetBlipName("STRING")
|
||
|
AddTextComponentString(item.name)
|
||
|
EndTextCommandSetBlipName(item.blip)
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
if cfg.torqueMultiplierEnabled or cfg.preventVehicleFlip or cfg.limpMode then
|
||
|
CreateThread(function()
|
||
|
while true do
|
||
|
Wait(0)
|
||
|
if cfg.torqueMultiplierEnabled or cfg.sundayDriver or cfg.limpMode then
|
||
|
if pedInSameVehicleLast then
|
||
|
local factor = 1.0
|
||
|
if cfg.torqueMultiplierEnabled and healthEngineNew < 900 then
|
||
|
factor = (healthEngineNew+200.0) / 1100
|
||
|
end
|
||
|
if cfg.sundayDriver and GetVehicleClass(vehicle) ~= 14 then -- Not for boats
|
||
|
local accelerator = GetControlValue(2,71)
|
||
|
local brake = GetControlValue(2,72)
|
||
|
local speed = GetEntitySpeedVector(vehicle, true)['y']
|
||
|
-- Change Braking force
|
||
|
local brk = fBrakeForce
|
||
|
if speed >= 1.0 then
|
||
|
-- Going forward
|
||
|
if accelerator > 127 then
|
||
|
-- Forward and accelerating
|
||
|
local acc = fscale(accelerator, 127.0, 254.0, 0.1, 1.0, 10.0-(cfg.sundayDriverAcceleratorCurve*2.0))
|
||
|
factor = factor * acc
|
||
|
end
|
||
|
if brake > 127 then
|
||
|
-- Forward and braking
|
||
|
isBrakingForward = true
|
||
|
brk = fscale(brake, 127.0, 254.0, 0.01, fBrakeForce, 10.0-(cfg.sundayDriverBrakeCurve*2.0))
|
||
|
--exports['qb-vehicletuning']:SetVehicleStatus(QBCore.Functions.GetPlate(vehicle), "brakes", exports['qb-vehicletuning']:GetVehicleStatus(QBCore.Functions.GetPlate(vehicle), "brakes") - 0.01)
|
||
|
end
|
||
|
elseif speed <= -1.0 then
|
||
|
-- Going reverse
|
||
|
if brake > 127 then
|
||
|
-- Reversing and accelerating (using the brake)
|
||
|
local rev = fscale(brake, 127.0, 254.0, 0.1, 1.0, 10.0-(cfg.sundayDriverAcceleratorCurve*2.0))
|
||
|
factor = factor * rev
|
||
|
--exports['qb-vehicletuning']:SetVehicleStatus(QBCore.Functions.GetPlate(vehicle), "brakes", exports['qb-vehicletuning']:GetVehicleStatus(QBCore.Functions.GetPlate(vehicle), "brakes") - 0.01)
|
||
|
end
|
||
|
if accelerator > 127 then
|
||
|
-- Reversing and braking (Using the accelerator)
|
||
|
isBrakingReverse = true
|
||
|
brk = fscale(accelerator, 127.0, 254.0, 0.01, fBrakeForce, 10.0 - (cfg.sundayDriverBrakeCurve * 2.0))
|
||
|
end
|
||
|
else
|
||
|
-- Stopped or almost stopped or sliding sideways
|
||
|
local entitySpeed = GetEntitySpeed(vehicle)
|
||
|
if entitySpeed < 1 then
|
||
|
-- Not sliding sideways
|
||
|
if isBrakingForward == true then
|
||
|
--Stopped or going slightly forward while braking
|
||
|
DisableControlAction(2, 72, true) -- Disable Brake until user lets go of brake
|
||
|
SetVehicleForwardSpeed(vehicle, speed * 0.98)
|
||
|
SetVehicleBrakeLights(vehicle, true)
|
||
|
end
|
||
|
if isBrakingReverse == true then
|
||
|
--Stopped or going slightly in reverse while braking
|
||
|
DisableControlAction(2, 71, true) -- Disable reverse Brake until user lets go of reverse brake (Accelerator)
|
||
|
SetVehicleForwardSpeed(vehicle,speed*0.98)
|
||
|
SetVehicleBrakeLights(vehicle,true)
|
||
|
end
|
||
|
if isBrakingForward == true and GetDisabledControlNormal(2, 72) == 0 then
|
||
|
-- We let go of the brake
|
||
|
isBrakingForward = false
|
||
|
end
|
||
|
if isBrakingReverse == true and GetDisabledControlNormal(2, 71) == 0 then
|
||
|
-- We let go of the reverse brake (Accelerator)
|
||
|
isBrakingReverse = false
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if brk > fBrakeForce - 0.02 then brk = fBrakeForce end -- Make sure we can brake max.
|
||
|
SetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fBrakeForce', brk) -- Set new Brake Force multiplier
|
||
|
end
|
||
|
if cfg.limpMode == true and healthEngineNew < cfg.engineSafeGuard + 5 then
|
||
|
factor = cfg.limpModeMultiplier
|
||
|
end
|
||
|
SetVehicleEngineTorqueMultiplier(vehicle, factor)
|
||
|
end
|
||
|
end
|
||
|
if cfg.preventVehicleFlip then
|
||
|
local roll = GetEntityRoll(vehicle)
|
||
|
if (roll > 75.0 or roll < -75.0) and GetEntitySpeed(vehicle) < 2 then
|
||
|
DisableControlAction(2, 59, true) -- Disable left/right
|
||
|
DisableControlAction(2, 60, true) -- Disable up/down
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
end
|
||
|
|
||
|
CreateThread(function()
|
||
|
while true do
|
||
|
Wait(50)
|
||
|
local ped = PlayerPedId()
|
||
|
if isPedDrivingAVehicle() then
|
||
|
vehicle = GetVehiclePedIsIn(ped, false)
|
||
|
vehicleClass = GetVehicleClass(vehicle)
|
||
|
healthEngineCurrent = GetVehicleEngineHealth(vehicle)
|
||
|
if healthEngineCurrent == 1000 then healthEngineLast = 1000.0 end
|
||
|
healthEngineNew = healthEngineCurrent
|
||
|
healthEngineDelta = healthEngineLast - healthEngineCurrent
|
||
|
healthEngineDeltaScaled = healthEngineDelta * cfg.damageFactorEngine * cfg.classDamageMultiplier[vehicleClass]
|
||
|
|
||
|
healthBodyCurrent = GetVehicleBodyHealth(vehicle)
|
||
|
if healthBodyCurrent == 1000 then healthBodyLast = 1000.0 end
|
||
|
healthBodyNew = healthBodyCurrent
|
||
|
healthBodyDelta = healthBodyLast - healthBodyCurrent
|
||
|
healthBodyDeltaScaled = healthBodyDelta * cfg.damageFactorBody * cfg.classDamageMultiplier[vehicleClass]
|
||
|
|
||
|
healthPetrolTankCurrent = GetVehiclePetrolTankHealth(vehicle)
|
||
|
if cfg.compatibilityMode and healthPetrolTankCurrent < 1 then
|
||
|
-- SetVehiclePetrolTankHealth(vehicle, healthPetrolTankLast)
|
||
|
-- healthPetrolTankCurrent = healthPetrolTankLast
|
||
|
healthPetrolTankLast = healthPetrolTankCurrent
|
||
|
end
|
||
|
if healthPetrolTankCurrent == 1000 then healthPetrolTankLast = 1000.0 end
|
||
|
healthPetrolTankNew = healthPetrolTankCurrent
|
||
|
healthPetrolTankDelta = healthPetrolTankLast-healthPetrolTankCurrent
|
||
|
healthPetrolTankDeltaScaled = healthPetrolTankDelta * cfg.damageFactorPetrolTank * cfg.classDamageMultiplier[vehicleClass]
|
||
|
|
||
|
if healthEngineCurrent > cfg.engineSafeGuard+1 then
|
||
|
SetVehicleUndriveable(vehicle,false)
|
||
|
end
|
||
|
|
||
|
if healthEngineCurrent <= cfg.engineSafeGuard+1 and cfg.limpMode == false then
|
||
|
local vehpos = GetEntityCoords(vehicle)
|
||
|
StartParticleFxLoopedAtCoord("ent_ray_heli_aprtmnt_l_fire", vehpos.x, vehpos.y, vehpos.z-0.7, 0.0, 0.0, 0.0, 1.0, false, false, false, false)
|
||
|
SetVehicleUndriveable(vehicle,true)
|
||
|
end
|
||
|
|
||
|
-- If ped spawned a new vehicle while in a vehicle or teleported from one vehicle to another, handle as if we just entered the car
|
||
|
if vehicle ~= lastVehicle then
|
||
|
pedInSameVehicleLast = false
|
||
|
end
|
||
|
|
||
|
|
||
|
if pedInSameVehicleLast == true then
|
||
|
-- Damage happened while in the car = can be multiplied
|
||
|
|
||
|
-- Only do calculations if any damage is present on the car. Prevents weird behavior when fixing using trainer or other script
|
||
|
if healthEngineCurrent ~= 1000.0 or healthBodyCurrent ~= 1000.0 or healthPetrolTankCurrent ~= 1000.0 then
|
||
|
|
||
|
-- Combine the delta values (Get the largest of the three)
|
||
|
local healthEngineCombinedDelta = math.max(healthEngineDeltaScaled, healthBodyDeltaScaled, healthPetrolTankDeltaScaled)
|
||
|
|
||
|
-- If huge damage, scale back a bit
|
||
|
if healthEngineCombinedDelta > (healthEngineCurrent - cfg.engineSafeGuard) then
|
||
|
healthEngineCombinedDelta = healthEngineCombinedDelta * 0.7
|
||
|
end
|
||
|
|
||
|
-- If complete damage, but not catastrophic (ie. explosion territory) pull back a bit, to give a couple of seconds og engine runtime before dying
|
||
|
if healthEngineCombinedDelta > healthEngineCurrent then
|
||
|
healthEngineCombinedDelta = healthEngineCurrent - (cfg.cascadingFailureThreshold / 5)
|
||
|
end
|
||
|
|
||
|
|
||
|
------- Calculate new value
|
||
|
|
||
|
healthEngineNew = healthEngineLast - healthEngineCombinedDelta
|
||
|
|
||
|
|
||
|
------- Sanity Check on new values and further manipulations
|
||
|
|
||
|
-- If somewhat damaged, slowly degrade until slightly before cascading failure sets in, then stop
|
||
|
|
||
|
if healthEngineNew > (cfg.cascadingFailureThreshold + 5) and healthEngineNew < cfg.degradingFailureThreshold then
|
||
|
healthEngineNew = healthEngineNew-(0.038 * cfg.degradingHealthSpeedFactor)
|
||
|
end
|
||
|
|
||
|
-- If Damage is near catastrophic, cascade the failure
|
||
|
if healthEngineNew < cfg.cascadingFailureThreshold then
|
||
|
healthEngineNew = healthEngineNew-(0.1 * cfg.cascadingFailureSpeedFactor)
|
||
|
end
|
||
|
|
||
|
-- Prevent Engine going to or below zero. Ensures you can reenter a damaged car.
|
||
|
if healthEngineNew < cfg.engineSafeGuard then
|
||
|
healthEngineNew = cfg.engineSafeGuard
|
||
|
end
|
||
|
|
||
|
-- Prevent Explosions
|
||
|
if cfg.compatibilityMode == false and healthPetrolTankCurrent < 750 then
|
||
|
healthPetrolTankNew = 750.0
|
||
|
end
|
||
|
|
||
|
-- Prevent negative body damage.
|
||
|
if healthBodyNew < 0 then
|
||
|
healthBodyNew = 0.0
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
-- Just got in the vehicle. Damage can not be multiplied this round
|
||
|
-- Set vehicle handling data
|
||
|
fDeformationDamageMult = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fDeformationDamageMult')
|
||
|
fBrakeForce = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fBrakeForce')
|
||
|
local newFDeformationDamageMult = fDeformationDamageMult ^ cfg.deformationExponent -- Pull the handling file value closer to 1
|
||
|
if cfg.deformationMultiplier ~= -1 then SetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fDeformationDamageMult', newFDeformationDamageMult * cfg.deformationMultiplier) end -- Multiply by our factor
|
||
|
if cfg.weaponsDamageMultiplier ~= -1 then SetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fWeaponDamageMult', cfg.weaponsDamageMultiplier/cfg.damageFactorBody) end -- Set weaponsDamageMultiplier and compensate for damageFactorBody
|
||
|
|
||
|
--Get the CollisionDamageMultiplier
|
||
|
fCollisionDamageMult = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fCollisionDamageMult')
|
||
|
--Modify it by pulling all number a towards 1.0
|
||
|
local newFCollisionDamageMultiplier = fCollisionDamageMult ^ cfg.collisionDamageExponent -- Pull the handling file value closer to 1
|
||
|
SetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fCollisionDamageMult', newFCollisionDamageMultiplier)
|
||
|
|
||
|
--Get the EngineDamageMultiplier
|
||
|
fEngineDamageMult = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fEngineDamageMult')
|
||
|
--Modify it by pulling all number a towards 1.0
|
||
|
local newFEngineDamageMult = fEngineDamageMult ^ cfg.engineDamageExponent -- Pull the handling file value closer to 1
|
||
|
SetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fEngineDamageMult', newFEngineDamageMult)
|
||
|
|
||
|
-- If body damage catastrophic, reset somewhat so we can get new damage to multiply
|
||
|
if healthBodyCurrent < cfg.cascadingFailureThreshold then
|
||
|
healthBodyNew = cfg.cascadingFailureThreshold
|
||
|
end
|
||
|
pedInSameVehicleLast = true
|
||
|
end
|
||
|
|
||
|
-- set the actual new values
|
||
|
if healthEngineNew ~= healthEngineCurrent then
|
||
|
SetVehicleEngineHealth(vehicle, healthEngineNew)
|
||
|
local dmgFactr = (healthEngineCurrent - healthEngineNew)
|
||
|
if dmgFactr > 0.8 then
|
||
|
if DamageRandomComponent ~= nil then
|
||
|
DamageRandomComponent()
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if healthBodyNew ~= healthBodyCurrent then
|
||
|
SetVehicleBodyHealth(vehicle, healthBodyNew)
|
||
|
if DamageRandomComponent ~= nil then
|
||
|
DamageRandomComponent()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if healthPetrolTankNew ~= healthPetrolTankCurrent then
|
||
|
SetVehiclePetrolTankHealth(vehicle, healthPetrolTankNew)
|
||
|
end
|
||
|
|
||
|
-- Store current values, so we can calculate delta next time around
|
||
|
healthEngineLast = healthEngineNew
|
||
|
healthBodyLast = healthBodyNew
|
||
|
healthPetrolTankLast = healthPetrolTankNew
|
||
|
lastVehicle=vehicle
|
||
|
if cfg.randomTireBurstInterval ~= 0 and GetEntitySpeed(vehicle) > 10 then tireBurstLottery() end
|
||
|
else
|
||
|
if pedInSameVehicleLast == true then
|
||
|
-- We just got out of the vehicle
|
||
|
lastVehicle = GetVehiclePedIsIn(ped, true)
|
||
|
if cfg.deformationMultiplier ~= -1 then SetVehicleHandlingFloat(lastVehicle, 'CHandlingData', 'fDeformationDamageMult', fDeformationDamageMult) end -- Restore deformation multiplier
|
||
|
SetVehicleHandlingFloat(lastVehicle, 'CHandlingData', 'fBrakeForce', fBrakeForce) -- Restore Brake Force multiplier
|
||
|
if cfg.weaponsDamageMultiplier ~= -1 then SetVehicleHandlingFloat(lastVehicle, 'CHandlingData', 'fWeaponDamageMult', cfg.weaponsDamageMultiplier) end -- Since we are out of the vehicle, we should no longer compensate for bodyDamageFactor
|
||
|
SetVehicleHandlingFloat(lastVehicle, 'CHandlingData', 'fCollisionDamageMult', fCollisionDamageMult) -- Restore the original CollisionDamageMultiplier
|
||
|
SetVehicleHandlingFloat(lastVehicle, 'CHandlingData', 'fEngineDamageMult', fEngineDamageMult) -- Restore the original EngineDamageMultiplier
|
||
|
end
|
||
|
pedInSameVehicleLast = false
|
||
|
end
|
||
|
end
|
||
|
end)
|