local Progress = lib.load('data.progress') local Settings = lib.load('data.vehicle') local BONES = { [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