2025-04-07 01:41:12 +00:00

332 lines
9.6 KiB
Lua

local Progress = lib.load('data.progress')
local Settings = lib.load('data.vehicle')
local BONES <const> = {
[0] = 'wheel_lf',
'wheel_rf',
'wheel_lm',
'wheel_rm',
'wheel_lr',
'wheel_rr'
}
---@class Handler : OxClass
---@field private private { active: boolean, limited: boolean, control: boolean, class: number | false, data: table, oxfuel: boolean, units: number, electric: boolean }
---@diagnostic disable-next-line: assign-type-mismatch
local Handler = lib.class('vehiclehandler')
function Handler:constructor()
self:setActive(false)
self:setLimited(false)
self:setControl(true)
self.private.oxfuel = GetResourceState('ox_fuel') == 'started' and true or false
self.private.units = Settings.units == 'mph' and 2.23694 or 3.6
self.private.electric = false
end
function Handler:isActive() return self.private.active end
function Handler:isLimited() return self.private.limited end
function Handler:canControl() return self.private.control end
function Handler:getClass() return self.private.class end
function Handler:isFuelOx() return self.private.oxfuel end
function Handler:getUnits() return self.private.units end
function Handler:isElectric() return self.private.electric end
function Handler:getData(state)
if not state or type(state) ~= 'string' then return end
return self.private.data[state]
end
function Handler:isValid()
if not cache.ped then return false end
if cache.vehicle or IsPedInAnyPlane(cache.ped) then return true end
return false
end
function Handler:isTireBroken(vehicle, coords)
if not vehicle or not coords then return false end
for k,v in pairs(BONES) do
local bone = GetEntityBoneIndexByName(vehicle, v)
if bone ~= -1 then
if IsVehicleTyreBurst(vehicle, k, true) then
local pos = GetWorldPositionOfEntityBone(vehicle, bone)
if #(coords - pos) < 2.5 then
return true
end
end
end
end
return false
end
function Handler:setActive(state)
if state ~= nil and type(state) == 'boolean' then
self.private.active = state
if state then
self.private.class = GetVehicleClass(cache.vehicle) or false
self.private.electric = GetIsVehicleElectric(GetEntityModel(cache.vehicle))
else
self.private.class = false
self.private.electric = false
self.private.data = {['engine'] = 0, ['body'] = 0, ['speed'] = 0}
end
end
end
function Handler:setLimited(state)
if state ~= nil and type(state) == 'boolean' then
self.private.limited = state
end
end
function Handler:setControl(state)
if state ~= nil and type(state) == 'boolean' then
self.private.control = state
end
end
function Handler:setData(data)
if not data then return end
self.private.data = data
return data['engine'], data['body'], data['speed']
end
function Handler:getEngineData(vehicle)
if not vehicle or vehicle == 0 then return end
local backengine = Settings.backengine[GetEntityModel(vehicle)]
local distance = backengine and -2.5 or 2.5
local index = backengine and 5 or 4
local offset = GetOffsetFromEntityInWorldCoords(vehicle, 0, distance, 0)
local engine = GetVehicleEngineHealth(vehicle)
return backengine, offset, index, engine
end
function Handler:breakTire(vehicle, index)
if vehicle == nil or type(vehicle) ~= 'number' then return end
if index == nil or type(index) ~= 'number' then return end
local bone = GetEntityBoneIndexByName(vehicle, BONES[index])
if bone ~= -1 then
if not IsVehicleTyreBurst(vehicle, index, true) then
lib.callback('vehiclehandler:sync', false, function()
SetVehicleTyreBurst(vehicle, index, true, 1000.0)
BreakOffVehicleWheel(vehicle, index, false, true, true, false)
end)
end
end
end
function Handler:fixTire(vehicle, coords)
local found = self:isTireBroken(vehicle, coords)
if found then
local lastengine = GetVehicleEngineHealth(vehicle)
local lastbody = GetVehicleBodyHealth(vehicle)
local lastdirt = GetVehicleDirtLevel(vehicle)
local success = false
LocalPlayer.state:set("inv_busy", true, true)
if lib.progressCircle(Progress['tirekit']) then
success = true
lib.callback('vehiclehandler:sync', false, function()
SetVehicleFixed(vehicle)
SetVehicleEngineHealth(vehicle, lastengine)
SetVehicleBodyHealth(vehicle, lastbody)
SetVehicleDirtLevel(vehicle, lastdirt)
end)
end
LocalPlayer.state:set("inv_busy", false, true)
return success
end
return false
end
function Handler:fixVehicle(vehicle, coords, fixtype)
local backengine, offset, hoodindex, engine = self:getEngineData(vehicle)
if fixtype == 'smallkit' and engine < 500 or fixtype == 'bigkit' and engine < 1000 then
if #(coords - offset) < 2.0 then
local success = false
LocalPlayer.state:set("inv_busy", true, true)
if hoodindex then
lib.callback('vehiclehandler:sync', false, function()
SetVehicleDoorOpen(vehicle, hoodindex, false, false)
end)
end
if lib.progressCircle(Progress[fixtype]) then
success = true
end
if hoodindex then
lib.callback('vehiclehandler:sync', false, function()
SetVehicleDoorShut(vehicle, hoodindex, false)
end)
repeat Wait(100)
until not IsVehicleDoorFullyOpen(vehicle, hoodindex)
end
if success then
lib.callback('vehiclehandler:sync', false, function()
if fixtype == 'smallkit' then
SetVehicleEngineHealth(vehicle, 500.0)
if GetVehicleBodyHealth(vehicle) < 500 then
SetVehicleBodyHealth(vehicle, 500.0)
end
elseif fixtype == 'bigkit' then
SetVehicleFixed(vehicle)
end
SetVehicleUndriveable(vehicle, false)
end)
end
LocalPlayer.state:set("inv_busy", false, true)
return success
else
if backengine then
lib.notify({
title = 'Engine bay is in back',
type = 'error'
})
end
end
else
lib.notify({
title = 'Cannot repair vehicle any further',
type = 'error'
})
end
return false
end
function Handler:basicfix(fixtype)
if not cache.ped then return false end
if not fixtype or type(fixtype) ~= 'string' then return false end
local coords = GetEntityCoords(cache.ped)
local vehicle,_ = lib.getClosestVehicle(coords, 3.0, false)
if vehicle == nil or vehicle == 0 then return false end
if fixtype == 'tirekit' then
return self:fixTire(vehicle, coords)
elseif fixtype == 'smallkit' or fixtype == 'bigkit' then
return self:fixVehicle(vehicle, coords, fixtype)
end
end
function Handler:basicwash()
if not cache.ped then return false end
local pos = GetEntityCoords(cache.ped)
local vehicle,_ = lib.getClosestVehicle(pos, 3.0, false)
if vehicle == nil or vehicle == 0 then return false end
local vehpos = GetEntityCoords(vehicle)
if #(pos - vehpos) > 3.0 or cache.vehicle then return false end
local success = false
LocalPlayer.state:set("inv_busy", true, true)
TaskStartScenarioInPlace(cache.ped, "WORLD_HUMAN_MAID_CLEAN", 0, true)
if lib.progressCircle(Progress['cleankit']) then
success = true
lib.callback('vehiclehandler:sync', false, function()
SetVehicleDirtLevel(vehicle, 0.0)
WashDecalsFromVehicle(vehicle, 1.0)
end)
end
ClearAllPedProps(cache.ped)
ClearPedTasks(cache.ped)
LocalPlayer.state:set("inv_busy", false, true)
return success
end
function Handler:adminfix()
if not self:isValid() then return false end
lib.callback('vehiclehandler:sync', false, function()
SetVehicleFixed(cache.vehicle)
ResetVehicleWheels(cache.vehicle, true)
if self:isFuelOx() then
Entity(cache.vehicle).state.fuel = 100.0
end
SetVehicleFuelLevel(cache.vehicle, 100.0)
DecorSetFloat(cache.vehicle, '_FUEL_LEVEL', GetVehicleFuelLevel(cache.vehicle))
SetVehicleUndriveable(cache.vehicle, false)
SetVehicleEngineOn(cache.vehicle, true, true, true)
end)
return true
end
function Handler:adminwash()
if not self:isValid() then return false end
lib.callback('vehiclehandler:sync', false, function()
SetVehicleDirtLevel(cache.vehicle, 0.0)
WashDecalsFromVehicle(cache.vehicle, 1.0)
end)
return true
end
function Handler:adminfuel(newlevel)
if not self:isValid() then return false end
if not newlevel then return false end
newlevel = lib.math.clamp(newlevel, 0.0, 100.0) + 0.0
lib.callback('vehiclehandler:sync', false, function()
if self:isFuelOx() then
Entity(cache.vehicle).state.fuel = newlevel
end
SetVehicleFuelLevel(cache.vehicle, newlevel)
DecorSetFloat(cache.vehicle, '_FUEL_LEVEL', GetVehicleFuelLevel(cache.vehicle))
SetVehicleUndriveable(cache.vehicle, false)
SetVehicleEngineOn(cache.vehicle, true, true, true)
end)
return true
end
return Handler