From cae0aa5e6ad4d1955f89c9885f97deb835a7d534 Mon Sep 17 00:00:00 2001 From: Nordi98 Date: Tue, 17 Jun 2025 17:17:59 +0200 Subject: [PATCH] lc_fuel update --- resources/[carscripts]/lc_fuel/README.md | 30 +- .../[carscripts]/lc_fuel/client/client.lua | 3202 +++++++++-------- .../lc_fuel/client/client_electric.lua | 304 +- .../lc_fuel/client/client_fuel_chart.lua | 152 +- .../lc_fuel/client/client_fuel_type.lua | 10 + .../lc_fuel/client/client_gas.lua | 278 +- .../lc_fuel/client/client_refuel.lua | 972 ++--- resources/[carscripts]/lc_fuel/fxmanifest.lua | 95 +- resources/[carscripts]/lc_fuel/lang/de.lua | 90 +- resources/[carscripts]/lc_fuel/lang/en.lua | 88 +- resources/[carscripts]/lc_fuel/lang/es.lua | 90 +- resources/[carscripts]/lc_fuel/lang/fr.lua | 88 +- resources/[carscripts]/lc_fuel/lang/ja.lua | 88 +- resources/[carscripts]/lc_fuel/lang/tr.lua | 90 +- resources/[carscripts]/lc_fuel/lang/zh-cn.lua | 88 +- .../[carscripts]/lc_fuel/nui/css/style.css | 2006 +++++------ resources/[carscripts]/lc_fuel/nui/lang/de.js | 168 +- resources/[carscripts]/lc_fuel/nui/lang/en.js | 168 +- resources/[carscripts]/lc_fuel/nui/lang/es.js | 170 +- resources/[carscripts]/lc_fuel/nui/lang/fr.js | 170 +- resources/[carscripts]/lc_fuel/nui/lang/ja.js | 168 +- resources/[carscripts]/lc_fuel/nui/lang/tr.js | 170 +- .../[carscripts]/lc_fuel/nui/lang/zh-cn.js | 168 +- resources/[carscripts]/lc_fuel/nui/panel.js | 1446 ++++---- .../scripts/chartjs-plugin-streaming@2.0.0 | 14 +- resources/[carscripts]/lc_fuel/nui/ui.html | 556 +-- .../[carscripts]/lc_fuel/server/server.lua | 1161 +++--- resources/[carscripts]/lc_fuel/version | 2 +- 28 files changed, 6064 insertions(+), 5968 deletions(-) create mode 100644 resources/[carscripts]/lc_fuel/client/client_fuel_type.lua diff --git a/resources/[carscripts]/lc_fuel/README.md b/resources/[carscripts]/lc_fuel/README.md index c7717a525..9d55eaf4e 100644 --- a/resources/[carscripts]/lc_fuel/README.md +++ b/resources/[carscripts]/lc_fuel/README.md @@ -1,16 +1,16 @@ -# LC_Fuel - FiveM - -## About -A fuel system that adds realism and depth to vehicle management. Whether your players drive gas-powered cars, diesel, or electric vehicles, this script ensures an immersive refueling experience. - -## Installation -Install steps available on our docs: [Installation - LC Fuel](https://docs.lixeirocharmoso.com/lc_fuel/installation) - -## Exports -Exports available in our docs: [Exports - LC Fuel](https://docs.lixeirocharmoso.com/lc_fuel/exports) - -## Our links -- Discord: [https://discord.gg/U5YDgbh](https://discord.gg/U5YDgbh) -- Tebex: [https://lixeirocharmoso.tebex.io](https://lixeirocharmoso.tebex.io) -- Docs: [https://docs.lixeirocharmoso.com](https://docs.lixeirocharmoso.com/lc_fuel) +# LC_Fuel - FiveM + +## About +A fuel system that adds realism and depth to vehicle management. Whether your players drive gas-powered cars, diesel, or electric vehicles, this script ensures an immersive refueling experience. + +## Installation +Install steps available on our docs: [Installation - LC Fuel](https://docs.lixeirocharmoso.com/lc_fuel/installation) + +## Exports +Exports available in our docs: [Exports - LC Fuel](https://docs.lixeirocharmoso.com/lc_fuel/exports) + +## Our links +- Discord: [https://discord.gg/U5YDgbh](https://discord.gg/U5YDgbh) +- Tebex: [https://lixeirocharmoso.tebex.io](https://lixeirocharmoso.tebex.io) +- Docs: [https://docs.lixeirocharmoso.com](https://docs.lixeirocharmoso.com/lc_fuel) - Showcase: [https://youtu.be/M6__IeCwxH8](https://youtu.be/M6__IeCwxH8) \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/client/client.lua b/resources/[carscripts]/lc_fuel/client/client.lua index 04fb3f3eb..630b2b034 100644 --- a/resources/[carscripts]/lc_fuel/client/client.lua +++ b/resources/[carscripts]/lc_fuel/client/client.lua @@ -1,1598 +1,1604 @@ --- Shared variables -Utils = Utils or exports['lc_utils']:GetUtils() -cachedTranslations = {} -mainUiOpen = false - -fuelNozzle = 0 -fuelRope = 0 -currentPump = 0 -JERRY_CAN_HASH = 883325847 - --- Fuel chart variables -isFuelConsumptionChartOpen = false -isRecording = true - --- Local variables -local fuelDecor = "_FUEL_LEVEL" -local currentConsumption = 0.0 -local fuelSynced = true -local closestVehicleToPump = 0 -local isNuiVariablesLoaded = false - -DecorRegister(fuelDecor, 1) --- Check if it actually registered -if DecorIsRegisteredAsType(fuelDecor, 1) then - print("[Fuel Script] SUCCESS: fuelDecor was registered successfully!") -else - print("[Fuel Script] ERROR: fuelDecor registration failed!") -end - ------------------------------------------------------------------------------------------------------------------------------------------ --- Threads ------------------------------------------------------------------------------------------------------------------------------------------ - --- Thread to handle the fuel consumption -function createFuelConsumptionThread() - CreateThread(function() - local currentVehicle = nil - local currentVehicleFuelType = "default" -- default while the callback loads - DecorRegister(fuelDecor, 1) - while true do - Wait(1000) - local ped = PlayerPedId() - if IsPedInAnyVehicle(ped, false) then - local vehicle = GetVehiclePedIsIn(ped, false) - if GetPedInVehicleSeat(vehicle, -1) == ped and not IsVehicleBlacklisted(vehicle) then - if currentVehicle == nil or currentVehicle ~= vehicle then - currentVehicle = vehicle - currentVehicleFuelType = getVehicleFuelTypeFromServer(vehicle) - fuelSynced = false - end - HandleFuelConsumption(vehicle, currentVehicleFuelType) - end - else - if isFuelConsumptionChartOpen then - closeFuelConsumptionChartUI() - end - currentVehicleFuelType = "default" - currentVehicle = nil - fuelSynced = false - end - end - end) -end - -function HandleFuelConsumption(vehicle, fuelType) - -- If no decorator exists, set a random fuel % - if not DecorExistOn(vehicle, fuelDecor) then - SetFuel(vehicle, math.random(200, 800) / 10) - elseif not fuelSynced then - -- Sync once - SetFuel(vehicle, GetFuel(vehicle)) - fuelSynced = true - end - - -- If engine is off, do nothing - if not GetIsVehicleEngineRunning(vehicle) then - return - end - - -- Turns engine off if have no fuel - local currentFuelLevel = GetVehicleFuelLevel(vehicle) - if currentFuelLevel <= 0.0 then - SetVehicleEngineOn(vehicle, false, true, false) - return - end - - -- Get the vehicle's actual tank size in liters - local tankSize = getVehicleTankSize(vehicle) - - -- Base consumption from config and RPM - currentConsumption = Config.FuelUsage[Utils.Math.round(GetVehicleCurrentRpm(vehicle), 1)] * (Config.FuelConsumptionPerClass[GetVehicleClass(vehicle)] or 1.0) * (Config.FuelConsumptionPerFuelType[fuelType] or 1.0) / 10 - - -- Scale consumption according to tank size. - -- A smaller tank will lose percentage faster; a bigger tank slower. - local scaledConsumption = currentConsumption * (100 / tankSize) - - -- Subtract from the current fuel % - local newFuelLevel = currentFuelLevel - scaledConsumption - - -- Write the new fuel % back - SetFuel(vehicle, newFuelLevel) - - -- Store data for chart - if Config.FuelConsumptionChart.enabled then - storeDataForChart(vehicle, newFuelLevel, currentConsumption) - end - - validateDieselFuelMismatch(vehicle, fuelType) -end - -function validateDieselFuelMismatch(vehicle, fuelType) - if (fuelType == "diesel" and not IsVehicleDiesel(vehicle)) or (fuelType ~= "diesel" and IsVehicleDiesel(vehicle)) then - SetTimeout(5000, function() - if IsVehicleDriveable(vehicle, false) then - SetVehicleEngineHealth(vehicle, 0.0) - SetVehicleUndriveable(vehicle, true) - exports['lc_utils']:notify("error", Utils.translate("vehicle_wrong_fuel")) - end - end) - end -end - -function createDebugNozzleOffsetThread() - CreateThread(function() - while true do - local vehicle = GetVehiclePlayerIsLookingAt() - if not vehicle or not DoesEntityExist(vehicle) then - Wait(2) - goto continue -- Skip if no valid vehicle found - end - - local hit, hitCoords = GetLookAtHitCoords() - if hit and DoesEntityExist(vehicle) then - local offset = GetNozzleOffset(vehicle, hitCoords) - - if offset then - local vehCoords = GetEntityCoords(vehicle) - - -- Get vehicle's axis vectors - local forwardVector, rightVector, upVector = GetEntityMatrix(vehicle) - - -- Direction from vehicle to hit point - local directionToHit = hitCoords - vehCoords - directionToHit = directionToHit / #(directionToHit) -- normalize - - -- Dot product with right vector - local side = rightVector.x * directionToHit.x + rightVector.y * directionToHit.y + rightVector.z * directionToHit.z - - -- Adjust offset.right - if side > 0 then - -- hit is on the right side - offset.right = offset.right + 0.07 - else - -- hit is on the left side - offset.right = offset.right - 0.07 - end - - local boneWorldPos = GetVehicleCapPos(vehicle) - - -- local forwardVector, rightVector, upVector, _ = GetEntityMatrix(vehicle) - - -- Scale vectors by the local offset - local forwardOffset = forwardVector * offset.forward - local rightOffset = rightVector * offset.right - local upOffset = upVector * offset.up - - -- Final world position of the nozzle point - local finalWorldPos = vector3( - boneWorldPos.x + forwardOffset.x + rightOffset.x + upOffset.x, - boneWorldPos.y + forwardOffset.y + rightOffset.y + upOffset.y, - boneWorldPos.z + forwardOffset.z + rightOffset.z + upOffset.z - ) - - -- Draw a small marker at the computed position - DrawMarker(28, finalWorldPos.x, finalWorldPos.y, finalWorldPos.z + 0.05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.03, 0.03, 0.03, 255, 0, 0, 180, false, true, 2, nil, nil, false) - - if IsControlJustPressed(0,38) then - local model = GetEntityModel(vehicle) - local modelName = GetDisplayNameFromVehicleModel(model):lower() - - local zRotation = side > 0 and 180 or 0 - - local clipboardText = string.format( - '["%s"] = { distance = 1.3, nozzleOffset = { forward = %.2f, right = %.2f, up = %.2f }, nozzleRotation = { x = 0, y = 0, z = %d } },', - modelName, - offset.forward, offset.right, offset.up, - zRotation - ) - - SendNUIMessage({ - type = "copyToClipboard", - text = clipboardText - }) - - exports['lc_utils']:notify("success", "Copied to clipboard: " .. clipboardText) - exports['lc_utils']:notify("warning", "Make sure the vehicle spawn name is correct. You may need to manually fine-tune the offset and rotation.") - end - end - end - Wait(2) - ::continue:: - end - end) -end - ------------------------------------------------------------------------------------------------------------------------------------------ --- UI ------------------------------------------------------------------------------------------------------------------------------------------ - -function clientOpenUI(pump, pumpModel, isElectricPump) - currentPump = pump - local ped = PlayerPedId() - local playerCoords = GetEntityCoords(ped) - - closestVehicleToPump = GetClosestVehicle(playerCoords) - local isElectricVehicle = IsVehicleElectric(closestVehicleToPump) - if not (isElectricVehicle and isElectricPump) and not (not isElectricVehicle and not isElectricPump) then - -- Reset the near vehicle to 0 when it does not match the pump type (only allows electric vehicle in electric chargers and gas vehicles in gas pumps) - closestVehicleToPump = 0 - end - - pumpLocation = nil - - if closestVehicleToPump and #(playerCoords - GetEntityCoords(closestVehicleToPump)) < 5 then - -- Load the nearest vehicle fuel and plate (fuel type) - local vehicleFuel = GetFuel(closestVehicleToPump) - local vehiclePlate = GetVehicleNumberPlateText(closestVehicleToPump) - local vehicleTankSize = getVehicleTankSize(closestVehicleToPump) - local vehicleDisplayFuelAmount = getVehicleDisplayFuelAmount(vehicleFuel, vehicleTankSize) - TriggerServerEvent("lc_fuel:serverOpenUI", isElectricPump, pumpModel, vehicleDisplayFuelAmount, vehicleTankSize, vehiclePlate) - else - -- Allow the user to open the UI even without vehicles nearby - TriggerServerEvent("lc_fuel:serverOpenUI", isElectricPump, pumpModel) - end -end - -function loadNuiVariables() - if isNuiVariablesLoaded then - return - end - - -- Load NUI variables - SendNUIMessage({ - utils = { config = Utils.Config, lang = Utils.Lang }, - resourceName = GetCurrentResourceName() - }) - - local maxIterations = 100 -- Maximum number of iterations (100 * 100ms = 10 seconds) - local iterations = 0 - - while not isNuiVariablesLoaded do - Wait(100) - iterations = iterations + 1 - - if iterations >= maxIterations then - print("Error: Timeout while loading NUI variables after " .. iterations .. " attempts.") - return - end - end -end - - -RegisterNetEvent('lc_fuel:clientOpenUI') -AddEventHandler('lc_fuel:clientOpenUI', function(data) - loadNuiVariables() - data.currentFuelType = dealWithDefaultFuelType(closestVehicleToPump, data.currentFuelType) - SendNUIMessage({ - openMainUI = true, - data = data - }) - mainUiOpen = true - TriggerScreenblurFadeIn(1000) - FreezeEntityPosition(PlayerPedId(), true) - SetNuiFocus(true,true) -end) - -RegisterNUICallback('post', function(body, cb) - if cooldown == nil then - cooldown = true - - if body.event == "close" then - closeUI() - elseif body.event == "closeFuelConsumptionChartUI" then - closeFuelConsumptionChartUI() - elseif body.event == "removeFocusFuelConsumptionChartUI" then - SetNuiFocus(false,false) - elseif body.event == "startRecordingGraph" then - isRecording = true - elseif body.event == "stopRecordingGraph" then - isRecording = false - elseif body.event == "notify" then - exports['lc_utils']:notify(body.data.type,body.data.msg) - elseif body.event == "changeVehicleFuelType" then - changeVehicleFuelType(closestVehicleToPump, body.data.selectedFuelType) - else - TriggerServerEvent('lc_fuel:'..body.event,body.data) - end - cb(200) - - SetTimeout(5,function() - cooldown = nil - end) - end -end) - -RegisterNUICallback('setNuiVariablesLoaded', function(body, cb) - isNuiVariablesLoaded = true - cb(200) -end) - -function closeUI() - mainUiOpen = false - FreezeEntityPosition(PlayerPedId(), false) - TriggerScreenblurFadeOut(1000) - SetNuiFocus(false,false) - SendNUIMessage({ hideMainUI = true }) -end - -RegisterNetEvent('lc_fuel:closeUI') -AddEventHandler('lc_fuel:closeUI', function() - closeUI() -end) - ------------------------------------------------------------------------------------------------------------------------------------------ --- Exports ------------------------------------------------------------------------------------------------------------------------------------------ - -function GetFuel(vehicle) - if not DoesEntityExist(vehicle) then - warn(("[GetFuel] Vehicle entity does not exist. Received: %s. This is usually caused by a misconfiguration in the export."):format(tostring(vehicle))) - return 0 - end - return DecorGetFloat(vehicle, fuelDecor) -end -exports('GetFuel', GetFuel) - -function SetFuel(vehicle, fuel) - if not DoesEntityExist(vehicle) then - warn(("[SetFuel] Vehicle entity does not exist. Received: %s. This is usually caused by a misconfiguration in the export."):format(tostring(vehicle))) - return - end - - if type(fuel) ~= "number" then - warn(("[SetFuel] Invalid fuel value received: %s. Fuel must be a number between 0 and 100."):format(tostring(fuel))) - return - end - - -- Normalize the fuel values if received negative values or higher than 100 - fuel = math.max(0.0, math.min(fuel, 100.0)) - - SetVehicleFuelLevel(vehicle, fuel + 0.0) - DecorSetFloat(vehicle, fuelDecor, GetVehicleFuelLevel(vehicle)) -end -exports('SetFuel', SetFuel) - --- Just another way to call the exports in case someone does it like this... -function getFuel(vehicle) - return GetFuel(vehicle) -end -exports('getFuel', getFuel) - -function setFuel(vehicle, fuel) - SetFuel(vehicle, fuel) -end -exports('setFuel', setFuel) - --- Alias LegacyFuel's exports to point to our lc_fuel functions: -AddEventHandler('__cfx_export_LegacyFuel_SetFuel', function(setCB) - -- Redirect LegacyFuel:SetFuel to use lc_fuel's SetFuel function - setCB(SetFuel) -end) - -AddEventHandler('__cfx_export_LegacyFuel_GetFuel', function(setCB) - -- Redirect LegacyFuel:GetFuel to use lc_fuel's GetFuel function - setCB(GetFuel) -end) - ------------------------------------------------------------------------------------------------------------------------------------------ --- Utils ------------------------------------------------------------------------------------------------------------------------------------------ - -function getVehicleFuelTypeFromServer(vehicle) - local returnFuelType = nil - - Utils.Callback.TriggerServerCallback('lc_fuel:getVehicleFuelType', function(fuelType) - returnFuelType = dealWithDefaultFuelType(vehicle, fuelType) - end, GetVehicleNumberPlateText(vehicle)) - - while returnFuelType == nil do - Wait(10) - end - return returnFuelType -end - -function dealWithDefaultFuelType(vehicle, fuelType) - -- Define if the vehicle is diesel or gasoline for the ones that have never been refueled - if fuelType == "default" then - if IsVehicleDiesel(vehicle) then - fuelType = "diesel" - else - fuelType = "regular" - end - end - return fuelType -end - -function changeVehicleFuelType(vehicle, fuelType) - local ped = PlayerPedId() - local playerCoords = GetEntityCoords(ped) - if vehicle and #(playerCoords - GetEntityCoords(vehicle)) < 5 then - SetFuel(vehicle, 0.0) - exports['lc_utils']:notify("info",Utils.translate("vehicle_tank_emptied")) - TriggerServerEvent("lc_fuel:setVehicleFuelType", GetVehicleNumberPlateText(vehicle), fuelType) - else - exports['lc_utils']:notify("error",Utils.translate("vehicle_not_found")) - end -end - -function getVehicleDisplayFuelAmount(currentFuel, tankSize) - local displayFuelAmount = currentFuel * tankSize / 100 - return displayFuelAmount -end - -function getVehicleTankSize(vehicle) - local vehicleHash = GetEntityModel(vehicle) - if Config.FuelTankSize.perVehicleHash[vehicleHash] then - return Config.FuelTankSize.perVehicleHash[vehicleHash] - end - return Config.FuelTankSize.perClass[GetVehicleClass(vehicle)] or 100 -end - -function GetPumpOffset(pump) - local heightOffset = { forward = 0.0, right = 0.0, up = 2.1 } - local pumpModel = GetEntityModel(pump) - - for _, v in pairs(Config.Electric.chargersProps) do - if pumpModel == joaat(v.prop) then - heightOffset = v.ropeOffset - end - end - - for _, v in pairs(Config.GasPumpProps) do - if pumpModel == joaat(v.prop) then - heightOffset = v.ropeOffset - end - end - - return heightOffset -end - -function CreateRopeToPump(pumpCoords) - local offset = GetPumpOffset(currentPump) - - RopeLoadTextures() - while not RopeAreTexturesLoaded() do - Wait(0) - end - - local forwardVector, rightVector, upVector, _ = GetEntityMatrix(currentPump) - - -- Adjust the offsets - local forwardOffset = forwardVector * offset.forward - local rightoffset = rightVector * offset.right - local upOffset = upVector * offset.up - local finalOffset = forwardOffset + rightoffset + upOffset - - local ropeObj = AddRope(pumpCoords.x + finalOffset.x, pumpCoords.y + finalOffset.y, pumpCoords.z + finalOffset.z, 0.0, 0.0, 0.0, 4.0, 1, 10.0, 0.0, 1.0, false, false, false, 1.0, true) - while not ropeObj do - Wait(0) - end - ActivatePhysics(ropeObj) - Wait(100) - - local nozzlePos = GetOffsetFromEntityInWorldCoords(fuelNozzle, 0.0, -0.033, -0.195) - ---@diagnostic disable-next-line: param-type-mismatch - AttachEntitiesToRope(ropeObj, currentPump, fuelNozzle, pumpCoords.x + finalOffset.x, pumpCoords.y + finalOffset.y, pumpCoords.z + finalOffset.z, nozzlePos.x, nozzlePos.y, nozzlePos.z, 30.0, false, false, nil, nil) - return ropeObj -end - -function GetVehicles() - return GetGamePool('CVehicle') -end - -function GetClosestVehicle(coords, modelFilter) - return GetClosestEntity(GetVehicles(), false, coords, modelFilter) -end - -function GetClosestEntity(entities, isPlayerEntities, coords, modelFilter) - local closestEntity, closestEntityDistance, filteredEntities = -1, -1, nil - - if coords then - coords = vector3(coords.x, coords.y, coords.z) - else - local playerPed = PlayerPedId() - coords = GetEntityCoords(playerPed) - end - - if modelFilter then - filteredEntities = {} - - for _, entity in pairs(entities) do - if modelFilter[GetEntityModel(entity)] then - filteredEntities[#filteredEntities + 1] = entity - end - end - end - - for k, entity in pairs(filteredEntities or entities) do - local distance = #(coords - GetEntityCoords(entity)) - - if closestEntityDistance == -1 or distance < closestEntityDistance then - closestEntity, closestEntityDistance = isPlayerEntities and k or entity, distance - end - end - - return closestEntity, closestEntityDistance -end - -function GetVehicleCapPos(vehicle) - local closestCapPos - local tanks = vehicleCapBoneList() - for _, v in pairs(tanks) do - local vehicleTank = GetEntityBoneIndexByName(vehicle, v) - if vehicleTank ~= -1 then - closestCapPos = GetWorldPositionOfEntityBone(vehicle, vehicleTank) - break - end - end - return closestCapPos -end - -function Round(num, numDecimalPlaces) - error("Do not use this") -end - -function IsVehicleBlacklisted(vehicle) - if vehicle and vehicle ~= 0 then - local vehicleHash = GetEntityModel(vehicle) - -- Blacklist electric vehicles if electric recharge is disabled - if not Config.Electric.enabled and IsVehicleElectric(vehicle) then - return true - end - - -- Check if the vehicle is in the blacklist - if Config.BlacklistedVehiclesHash[vehicleHash] then - return true - end - return false - end - return true -end - -function IsVehicleDiesel(vehicle) - if vehicle and vehicle ~= 0 then - local vehicleHash = GetEntityModel(vehicle) - -- Check if the vehicle is in the diesel list - if Config.DieselVehiclesHash[vehicleHash] then - return true - end - end - return false -end - -function IsVehicleElectric(vehicle) - if vehicle and vehicle ~= 0 then - local vehicleHash = GetEntityModel(vehicle) - -- Check if the vehicle is in the diesel list - if Config.Electric.vehiclesListHash[vehicleHash] then - return true - end - end - return false -end - -function GetClosestPump(coords, isElectric) - local pump = nil - local currentPumpModel = nil - local pumpList = isElectric and Config.Electric.chargersProps or Config.GasPumpProps - - for i = 1, #pumpList do - currentPumpModel = pumpList[i].prop - pump = GetClosestObjectOfType(coords.x, coords.y, coords.z, 2.5, joaat(currentPumpModel), true, true, true) - if pump ~= 0 then return pump, currentPumpModel end - end - - return nil, nil -end - -function createBlips() - if Config.Blips.onlyShowNearestBlip then - Citizen.CreateThread(function() - local currentBlip = 0 - local currentBlipIndex = 0 - - while true do - local coords = GetEntityCoords(PlayerPedId()) - local closestBlipDistance = math.maxinteger - local closestBlipCoords - local closestBlipId - - for blipId, blipCoord in pairs(Config.Blips.locations) do - local distanceToBlip = #(coords - blipCoord) - - if distanceToBlip < closestBlipDistance then - closestBlipDistance = distanceToBlip - closestBlipCoords = blipCoord - closestBlipId = blipId - end - end - - if currentBlipIndex ~= closestBlipId then - if DoesBlipExist(currentBlip) then - Utils.Blips.removeBlip(currentBlip) - end - - if closestBlipCoords then - currentBlip = Utils.Blips.createBlipForCoords(closestBlipCoords.x,closestBlipCoords.y,closestBlipCoords.z,Config.Blips.blipId,Config.Blips.color,Utils.translate('blip_text'),Config.Blips.scale,false) - end - - currentBlipIndex = closestBlipId - end - - Citizen.Wait(5000) - end - end) - else - local text = Utils.translate('blip_text') - for _, blipCoords in pairs(Config.Blips.locations) do - Utils.Blips.createBlipForCoords(blipCoords.x,blipCoords.y,blipCoords.z,Config.Blips.blipId,Config.Blips.color,text,Config.Blips.scale,false) - end - end -end - -function convertConfigVehiclesDisplayNameToHash() - Config.BlacklistedVehiclesHash = {} - for _, value in pairs(Config.BlacklistedVehicles) do - Config.BlacklistedVehiclesHash[joaat(value)] = true - end - Config.Electric.vehiclesListHash = {} - for _, value in pairs(Config.Electric.vehiclesList) do - Config.Electric.vehiclesListHash[joaat(value)] = true - end - Config.DieselVehiclesHash = {} - for _, value in pairs(Config.DieselVehicles) do - Config.DieselVehiclesHash[joaat(value)] = true - end - Config.FuelTankSize.perVehicleHash = {} - for key, value in pairs(Config.FuelTankSize.perVehicle) do - Config.FuelTankSize.perVehicleHash[joaat(key)] = value - end - Config.CustomVehicleParametersHash = {} - Utils.Table.deepMerge(Config.HiddenCustomVehicleParameters, Config.CustomVehicleParameters) - for key, value in pairs(Config.HiddenCustomVehicleParameters) do - Config.CustomVehicleParametersHash[joaat(key)] = value - end - Config.CustomVehicleParametersHash.default = Config.CustomVehicleParameters.default -- Adds back the default -end - -RegisterNetEvent('lc_fuel:Notify') -AddEventHandler('lc_fuel:Notify', function(type,message) - exports['lc_utils']:notify(type,message) -end) - -Citizen.CreateThread(function() - Wait(1000) - SetNuiFocus(false,false) - SetNuiFocusKeepInput(false) - FreezeEntityPosition(PlayerPedId(), false) - - Utils.loadLanguageFile(Lang) - - cachedTranslations = { - open_refuel = Utils.translate('markers.open_refuel'), - open_recharge = Utils.translate('markers.open_recharge'), - interact_with_vehicle = Utils.translate('markers.interact_with_vehicle'), - return_nozzle = Utils.translate('markers.return_nozzle'), - } - - convertConfigVehiclesDisplayNameToHash() - - if Config.Blips and Config.Blips.enabled then - createBlips() - end - - -- Gas - if Utils.Config.custom_scripts_compatibility.target == "disabled" then - createGasMarkersThread() - else - createGasTargetsThread() - end - createCustomPumpModelsThread() - - -- Electrics - if Config.Electric.enabled then - CreateThread(function() - createElectricZones() - - if Utils.Config.custom_scripts_compatibility.target == "disabled" then - createElectricMarkersThread() - else - createElectricTargetsThread() - end - end) - end - - -- Refuel - if Utils.Config.custom_scripts_compatibility.target ~= "disabled" then - createTargetForVehicleIteraction() - end - - -- Other threads - createFuelConsumptionThread() - if Config.JerryCan.enabled and Utils.Config.custom_scripts_compatibility.target == "disabled" then - createJerryCanThread() - end - - if Config.DebugNozzleOffset then - createDebugNozzleOffsetThread() - end -end) - --- Debug nozzle offset functions -function GetVehiclePlayerIsLookingAt() - local playerPed = PlayerPedId() - local camCoords = GetGameplayCamCoord() - local camRot = GetGameplayCamRot(2) - local direction = RotationToDirection(camRot) - local maxDistance = 10.0 - - local destCoords = camCoords + direction * maxDistance - local rayHandle = StartShapeTestRay(camCoords.x, camCoords.y, camCoords.z, destCoords.x, destCoords.y, destCoords.z, 10, playerPed, 0) - local _, hit, endCoords, _, entityHit = GetShapeTestResult(rayHandle) - - if hit == 1 and IsEntityAVehicle(entityHit) then - return entityHit - end - return nil -end - -function GetLookAtHitCoords() - local playerPed = PlayerPedId() - local camCoords = GetGameplayCamCoord() - local camRot = GetGameplayCamRot(2) - local direction = RotationToDirection(camRot) - - local endCoords = vec3( - camCoords.x + direction.x * 10.0, - camCoords.y + direction.y * 10.0, - camCoords.z + direction.z * 10.0 - ) - - local rayHandle = StartShapeTestRay(camCoords.x, camCoords.y, camCoords.z, endCoords.x, endCoords.y, endCoords.z, -1, playerPed, 0) - local _, hit, hitCoords = GetShapeTestResult(rayHandle) - return hit, hitCoords -end - -function RotationToDirection(rot) - local z = math.rad(rot.z) - local x = math.rad(rot.x) - local cosX = math.cos(x) - - return vec3( - -math.sin(z) * cosX, - math.cos(z) * cosX, - math.sin(x) - ) -end - --- Gets the offset from 'petrolcap' to the point you're looking at -function GetNozzleOffset(vehicle, hitCoords) - -- World position of the bone - local boneWorldPos = GetVehicleCapPos(vehicle) - - -- Directional vector from bone to hit point - local direction = { - x = hitCoords.x - boneWorldPos.x, - y = hitCoords.y - boneWorldPos.y, - z = hitCoords.z - boneWorldPos.z - } - - -- Convert direction vector to vehicle-local offset - local forwardVector, rightVector, upVector, _ = GetEntityMatrix(vehicle) - - local offset = { - right = direction.x * rightVector.x + direction.y * rightVector.y + direction.z * rightVector.z, - forward = direction.x * forwardVector.x + direction.y * forwardVector.y + direction.z * forwardVector.z, - up = direction.x * upVector.x + direction.y * upVector.y + direction.z * upVector.z - } - - return offset -end - -if Config.EnableHUD then - local function DrawAdvancedText(x,y ,w,h,sc, text, r,g,b,a,font,jus) - SetTextFont(font) - SetTextProportional(false) - SetTextScale(sc, sc) - SetTextJustification(jus) - SetTextColour(r, g, b, a) - SetTextDropShadow() - SetTextEdge(1, 0, 0, 0, 255) - SetTextDropShadow() - SetTextOutline() - SetTextEntry("STRING") - AddTextComponentString(text) - DrawText(x - 0.1+w, y - 0.02+h) - end - - local mph = "0" - local kmh = "0" - local fuel = "0" - local displayHud = false - - local x = 0.01135 - local y = 0.002 - - Citizen.CreateThread(function() - while true do - local ped = PlayerPedId() - - if IsPedInAnyVehicle(ped, false) then - local vehicle = GetVehiclePedIsIn(ped, false) - local speed = GetEntitySpeed(vehicle) - - mph = tostring(math.ceil(speed * 2.236936)) - kmh = tostring(math.ceil(speed * 3.6)) - fuel = tostring(Utils.Math.round(GetVehicleFuelLevel(vehicle),2)) - - displayHud = true - else - displayHud = false - - Citizen.Wait(500) - end - - Citizen.Wait(50) - end - end) - - Citizen.CreateThread(function() - while true do - if displayHud then - DrawAdvancedText(0.130 - x, 0.77 - y, 0.005, 0.0028, 0.6, mph, 255, 255, 255, 255, 6, 1) - DrawAdvancedText(0.174 - x, 0.77 - y, 0.005, 0.0028, 0.6, kmh, 255, 255, 255, 255, 6, 1) - DrawAdvancedText(0.2155 - x, 0.77 - y, 0.005, 0.0028, 0.6, fuel, 255, 255, 255, 255, 6, 1) - DrawAdvancedText(0.2615 - x, 0.77 - y, 0.005, 0.0028, 0.6, tostring(currentConsumption), 255, 255, 255, 255, 6, 1) - DrawAdvancedText(0.145 - x, 0.7765 - y, 0.005, 0.0028, 0.4, "mp/h km/h Fuel Consumption", 255, 255, 255, 255, 6, 1) - else - Citizen.Wait(50) - end - - Citizen.Wait(0) - end - end) -end - -AddEventHandler('onResourceStop', function(resourceName) - if GetCurrentResourceName() ~= resourceName then return end - - deleteRopeAndNozzleProp() -end) - -function deleteRopeAndNozzleProp() - if DoesRopeExist(fuelRope) then - RopeUnloadTextures() - DeleteRope(fuelRope) - end - if DoesEntityExist(fuelNozzle) then - DeleteEntity(fuelNozzle) - end -end - -function vehicleCapBoneList() - return { "petrolcap", "petroltank", "petroltank_l", "petroltank_r", "wheel_lr", "wheel_lf", "engine", "chassis_dummy" } -end - --- Do not change this, use the Config.CustomVehicleParameters in config.lua -Config.HiddenCustomVehicleParameters = { - -- Cars - ["asbo"] = { distance = 1.2, nozzleOffset = { forward = 0.0, right = -0.21, up = 0.50} }, - ["blista"] = { distance = 1.2, nozzleOffset = { forward = 0.0, right = -0.21, up = 0.50} }, - ["brioso"] = { distance = 1.2, nozzleOffset = { forward = 0.0, right = -0.10, up = 0.60} }, - ["club"] = { distance = 1.2, nozzleOffset = { forward = -0.2, right = -0.13, up = 0.50} }, - ["kanjo"] = { distance = 1.2, nozzleOffset = { forward = -0.2, right = -0.17, up = 0.50} }, - ["issi2"] = { distance = 1.2, nozzleOffset = { forward = -0.2, right = -0.15, up = 0.50} }, - ["issi3"] = { distance = 1.2, nozzleOffset = { forward = -0.27, right = -0.13, up = 0.54} }, - ["issi4"] = { distance = 1.2, nozzleOffset = { forward = -0.27, right = -0.13, up = 0.70} }, - ["issi5"] = { distance = 1.2, nozzleOffset = { forward = -0.27, right = -0.13, up = 0.70} }, - ["issi6"] = { distance = 1.2, nozzleOffset = { forward = -0.27, right = -0.13, up = 0.70} }, - ["panto"] = { distance = 1.2, nozzleOffset = { forward = -0.10, right = -0.15, up = 0.65} }, - ["prairie"] = { distance = 1.2, nozzleOffset = { forward = -0.20, right = -0.20, up = 0.45} }, - ["rhapsody"] = { distance = 1.2, nozzleOffset = { forward = -0.20, right = -0.20, up = 0.45} }, - ["brioso2"] = { distance = 1.2, nozzleOffset = { forward = -0.25, right = -0.13, up = 0.40} }, - ["weevil"] = { distance = 1.2, nozzleOffset = { forward = -0.02, right = -0.03, up = 0.63} }, - ["issi7"] = { distance = 1.2, nozzleOffset = { forward = -0.03, right = -0.12, up = 0.57} }, - ["blista2"] = { distance = 1.2, nozzleOffset = { forward = -0.25, right = -0.23, up = 0.50} }, - ["blista3"] = { distance = 1.2, nozzleOffset = { forward = -0.25, right = -0.23, up = 0.50} }, - ["brioso3"] = { distance = 1.2, nozzleOffset = { forward = -0.25, right = -0.06, up = 0.40} }, - ["boor"] = { distance = 1.2, nozzleOffset = { forward = 0.0, right = -0.18, up = 0.50} }, - ["asea"] = { distance = 1.2, nozzleOffset = { forward = -0.28, right = -0.21, up = 0.50} }, - ["asterope"] = { distance = 1.2, nozzleOffset = { forward = -0.28, right = -0.16, up = 0.50} }, - ["cog55"] = { distance = 1.2, nozzleOffset = { forward = -0.44, right = -0.21, up = 0.45} }, - ["cognoscenti"] = { distance = 1.2, nozzleOffset = { forward = -0.44, right = -0.21, up = 0.45} }, - ["emperor"] = { distance = 1.2, nozzleOffset = { forward = -0.44, right = -0.22, up = 0.40} }, - ["fugitive"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.15, up = 0.40} }, - ["glendale"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.22, up = 0.40} }, - ["glendale2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.22, up = 0.30} }, - ["ingot"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.23, up = 0.45} }, - ["intruder"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.23, up = 0.40} }, - ["premier"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.16, up = 0.52} }, - ["primo"] = { distance = 1.2, nozzleOffset = { forward = -0.52, right = -0.18, up = 0.40} }, - ["primo2"] = { distance = 1.2, nozzleOffset = { forward = -0.52, right = -0.20, up = 0.35} }, - ["regina"] = { distance = 1.2, nozzleOffset = { forward = -0.52, right = -0.24, up = 0.40} }, - ["stafford"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.17, up = 0.50} }, - ["stanier"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.21, up = 0.40} }, - ["stratum"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.35} }, - ["stretch"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.21, up = 0.35} }, - ["superd"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.40} }, - ["tailgater"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.45} }, - ["warrener"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.45} }, - ["washington"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.22, up = 0.45} }, - ["tailgater2"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.14, up = 0.45} }, - ["cinquemila"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.21, up = 0.55} }, - ["astron"] = { distance = 1.2, nozzleOffset = { forward = -0.20, right = -0.22, up = 0.55} }, - ["baller7"] = { distance = 1.2, nozzleOffset = { forward = -0.62, right = -0.16, up = 0.60} }, - ["comet7"] = { distance = 1.2, nozzleOffset = { forward = -0.37, right = -0.19, up = 0.45} }, - ["deity"] = { distance = 1.2, nozzleOffset = { forward = -0.37, right = -0.21, up = 0.50} }, - ["jubilee"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.16, up = 0.60} }, - ["oracle"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.40} }, - ["schafter2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.45} }, - ["warrener2"] = { distance = 1.2, nozzleOffset = { forward = -0.02, right = -0.20, up = 0.40} }, - ["rhinehart"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.15, up = 0.50} }, - ["eudora"] = { distance = 1.2, nozzleOffset = { forward = 0.29, right = -0.38, up = 0.22} }, - - ["rebla"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.19, up = 0.60} }, - ["baller"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.23, up = 0.60} }, - ["baller2"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.60} }, - ["baller3"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.60} }, - ["baller4"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.60} }, - ["baller5"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.60} }, - ["baller6"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.60} }, - ["bjxl"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.21, up = 0.60} }, - ["cavalcade"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.21, up = 0.65} }, - ["cavalcade2"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.21, up = 0.65} }, - ["contender"] = { distance = 1.2, nozzleOffset = { forward = 0.75, right = -0.17, up = 0.50} }, - ["dubsta"] = { distance = 1.2, nozzleOffset = { forward = 0.25, right = -0.17, up = 0.70} }, - ["dubsta2"] = { distance = 1.2, nozzleOffset = { forward = 0.25, right = -0.17, up = 0.70} }, - ["fq2"] = { distance = 1.2, nozzleOffset = { forward = -0.32, right = -0.23, up = 0.53} }, - ["granger"] = { distance = 1.2, nozzleOffset = { forward = 0.65, right = -0.27, up = 0.60} }, - ["granger2"] = { distance = 1.2, nozzleOffset = { forward = 0.45, right = -0.26, up = 0.60} }, - ["gresley"] = { distance = 1.2, nozzleOffset = { forward = 0.05, right = -0.17, up = 0.66} }, - ["habanero"] = { distance = 1.2, nozzleOffset = { forward = -0.47, right = -0.17, up = 0.50} }, - ["huntley"] = { distance = 1.2, nozzleOffset = { forward = 0.07, right = -0.24, up = 0.65} }, - ["landstalker"] = { distance = 1.2, nozzleOffset = { forward = 0.40, right = -0.23, up = 0.60} }, - ["landstalker2"] = { distance = 1.2, nozzleOffset = { forward = 0.25, right = -0.24, up = 0.60} }, - ["novak"] = { distance = 1.2, nozzleOffset = { forward = -0.25, right = -0.21, up = 0.60} }, - ["patriot"] = { distance = 1.2, nozzleOffset = { forward = 0.2, right = -0.22, up = 0.75} }, - ["patriot2"] = { distance = 1.2, nozzleOffset = { forward = 0.2, right = -0.22, up = 0.75} }, - ["patriot3"] = { distance = 1.2, nozzleOffset = { forward = 0.50, right = -0.29, up = 0.65} }, - ["radi"] = { distance = 1.2, nozzleOffset = { forward = -0.30, right = -0.17, up = 0.60} }, - ["rocoto"] = { distance = 1.2, nozzleOffset = { forward = -0.30, right = -0.20, up = 0.60} }, - ["seminole"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.20, up = 0.65} }, - ["seminole2"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.20, up = 0.55} }, - ["serrano"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.19, up = 0.60} }, - ["toros"] = { distance = 1.2, nozzleOffset = { forward = -0.26, right = -0.26, up = 0.68} }, - ["xls"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.20, up = 0.65} }, - - ["cogcabrio"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.50} }, - ["exemplar"] = { distance = 1.2, nozzleOffset = { forward = -0.27, right = -0.19, up = 0.45} }, - ["f620"] = { distance = 1.2, nozzleOffset = { forward = -0.29, right = -0.25, up = 0.40} }, - ["felon"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.18, up = 0.40} }, - ["felon2"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.18, up = 0.40} }, - ["jackal"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.11, up = 0.50} }, - ["oracle2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.15, up = 0.50} }, - ["sentinel"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.11, up = 0.50} }, - ["sentinel2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.11, up = 0.50} }, - ["windsor"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.15, up = 0.50} }, - ["windsor2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.15, up = 0.50} }, - ["zion"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.17, up = 0.50} }, - ["zion2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.17, up = 0.50} }, - ["previon"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.21, up = 0.50} }, - ["champion"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.11, up = 0.40} }, - ["futo"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.15, up = 0.40} }, - ["sentinel3"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.22, up = 0.30} }, - ["kanjosj"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.45} }, - ["postlude"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.15, up = 0.45} }, - ["tahoma"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.35} }, - ["broadway"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.35} }, - - ["blade"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.31, up = 0.40} }, - ["buccaneer"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.28, up = 0.40} }, - ["chino"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.28, up = 0.35} }, - ["chino2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.25} }, - ["clique"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.21, up = 0.25} }, - ["coquette3"] = { distance = 1.2, nozzleOffset = { forward = 0.43, right = -0.31, up = 0.25} }, - ["deviant"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, - ["dominator"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, - ["dominator2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, - ["dominator3"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.40} }, - ["dominator4"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.21, up = 0.40} }, - ["dominator7"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.40} }, - ["dominator8"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.40} }, - ["dukes"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.31, up = 0.40} }, - ["dukes2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.31, up = 0.40} }, - ["dukes3"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.25, up = 0.40} }, - ["faction"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.17, up = 0.40} }, - ["faction2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.16, up = 0.30} }, - ["faction3"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.16, up = 0.70} }, - ["ellie"] = { distance = 1.2, nozzleOffset = { forward = -0.30, right = -0.05, up = 0.67} }, - ["gauntlet"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.40} }, - ["gauntlet2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.40} }, - ["gauntlet3"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.25, up = 0.50} }, - ["gauntlet4"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.18, up = 0.45} }, - ["gauntlet5"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.25, up = 0.50} }, - ["hermes"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.31, up = 0.20} }, - ["hotknife"] = { distance = 1.2, nozzleOffset = { forward = 0.40, right = -0.00, up = 0.30} }, - ["hustler"] = { distance = 1.2, nozzleOffset = { forward = -0.62, right = 0.05, up = 0.20} }, - ["impaler"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.27, up = 0.35} }, - ["impaler2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.24, up = 0.35} }, - ["impaler3"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.22, up = 0.45} }, - ["impaler4"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.35} }, - ["imperator"] = { distance = 1.2, nozzleOffset = { forward = -0.05, right = -0.15, up = 0.65} }, - ["imperator2"] = { distance = 1.2, nozzleOffset = { forward = -0.05, right = -0.15, up = 0.65} }, - ["imperator3"] = { distance = 1.2, nozzleOffset = { forward = -0.05, right = -0.15, up = 0.65} }, - ["lurcher"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.30, up = 0.35} }, - ["nightshade"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.07, up = 0.35} }, - ["phoenix"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.23, up = 0.35} }, - ["picador"] = { distance = 1.2, nozzleOffset = { forward = 0.75, right = -0.23, up = 0.45} }, - ["ratloader2"] = { distance = 1.2, nozzleOffset = { forward = 1.05, right = -0.07, up = 0.35} }, - ["ruiner"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.35} }, - ["ruiner2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.35} }, - ["sabregt"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.20, up = 0.35} }, - ["sabregt2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.20, up = 0.30} }, - ["slamvan"] = { distance = 1.2, nozzleOffset = { forward = 0.90, right = 0.03, up = 0.25} }, - ["slamvan2"] = { distance = 1.2, nozzleOffset = { forward = 0.90, right = -0.18, up = 0.30} }, - ["slamvan3"] = { distance = 1.2, nozzleOffset = { forward = 0.85, right = -0.03, up = 0.10} }, - ["stalion"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.35} }, - ["stalion2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.35} }, - ["tampa"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.20, up = 0.35} }, - ["tulip"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.35} }, - ["vamos"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.39} }, - ["vigero"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.22, up = 0.39} }, - ["virgo"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.22, up = 0.39} }, - ["virgo2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.28, up = 0.30} }, - ["virgo3"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.25, up = 0.30} }, - ["voodoo"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.29, up = 0.42} }, - ["yosemite"] = { distance = 1.2, nozzleOffset = { forward = 1.20, right = -0.29, up = 0.25} }, - ["yosemite2"] = { distance = 1.2, nozzleOffset = { forward = 1.22, right = -0.13, up = 0.35} }, - ["buffalo4"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.22, up = 0.50} }, - ["manana"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.25, up = 0.30} }, - ["manana2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.25, up = 0.30} }, - ["tampa2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.10, up = 0.30} }, - ["ruiner4"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.19, up = 0.35} }, - ["vigero2"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.50} }, - ["weevil2"] = { distance = 1.2, nozzleOffset = { forward = 1.90, right = 0.15, up = 0.25} }, - ["buffalo5"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.50} }, - ["tulip2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.24, up = 0.35} }, - ["clique2"] = { distance = 1.2, nozzleOffset = { forward = 0.05, right = -0.26, up = 0.60} }, - ["brigham"] = { distance = 1.2, nozzleOffset = { forward = 0.15, right = -0.30, up = 0.40} }, - ["greenwood"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.21, up = 0.50} }, - - ["ardent"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.19, up = 0.35} }, - ["btype"] = { distance = 1.2, nozzleOffset = { forward = 0.25, right = -0.05, up = 0.78} }, - ["btype2"] = { distance = 1.2, nozzleOffset = { forward = 0.36, right = 0.07, up = 0.55} }, - ["btype3"] = { distance = 1.2, nozzleOffset = { forward = 0.25, right = -0.05, up = 0.78} }, - ["casco"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.21, up = 0.30} }, - ["deluxo"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.13, up = 0.40} }, - ["dynasty"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.21, up = 0.40} }, - ["fagaloa"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.21, up = 0.35} }, - ["feltzer3"] = { distance = 1.2, nozzleOffset = { forward = -0.31, right = -0.13, up = 0.55} }, - ["gt500"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.19, up = 0.25} }, - ["infernus2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.27, up = 0.35} }, - ["jb700"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.21, up = 0.35} }, - ["jb7002"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.21, up = 0.35} }, - ["mamba"] = { distance = 1.2, nozzleOffset = { forward = -0.30, right = -0.13, up = 0.50} }, - ["michelli"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.18, up = 0.30} }, - ["monroe"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.21, up = 0.30} }, - ["nebula"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.30} }, - ["peyote"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.26, up = 0.30} }, - ["peyote3"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.26, up = 0.30} }, - ["pigalle"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.30} }, - ["rapidgt3"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.30} }, - ["retinue"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.30} }, - ["retinue2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.30} }, - ["savestra"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.15, up = 0.40} }, - ["stinger"] = { distance = 1.2, nozzleOffset = { forward = -0.02, right = -0.13, up = 0.65} }, - ["stingergt"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.20, up = 0.30} }, - ["stromberg"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.23, up = 0.35} }, - ["swinger"] = { distance = 1.2, nozzleOffset = { forward = 0.45, right = -0.28, up = 0.25} }, - ["torero"] = { distance = 1.2, nozzleOffset = { forward = 0.75, right = -0.21, up = 0.35} }, - ["tornado"] = { distance = 1.2, nozzleOffset = { forward = 0.45, right = -0.28, up = 0.25} }, - ["tornado2"] = { distance = 1.2, nozzleOffset = { forward = 0.45, right = -0.28, up = 0.25} }, - ["tornado5"] = { distance = 1.2, nozzleOffset = { forward = 0.45, right = -0.28, up = 0.25} }, - ["turismo2"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.23, up = 0.40} }, - ["viseris"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.18, up = 0.40} }, - ["z190"] = { distance = 1.2, nozzleOffset = { forward = -0.68, right = -0.10, up = 0.47} }, - ["ztype"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.23, up = 0.30} }, - ["zion3"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.23, up = 0.30} }, - ["cheburek"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.20, up = 0.30} }, - ["toreador"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.22, up = 0.35} }, - ["peyote2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.28, up = 0.30} }, - ["coquette2"] = { distance = 1.2, nozzleOffset = { forward = 0.43, right = -0.24, up = 0.25} }, - - ["alpha"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.21, up = 0.40} }, - ["banshee"] = { distance = 1.3, nozzleOffset = { forward = -0.55, right = -0.09, up = 0.40} }, - ["bestiagts"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.25, up = 0.45} }, - ["buffalo"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.35} }, - ["buffalo2"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.35} }, - ["carbonizzare"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.27, up = 0.50} }, - ["comet2"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.35} }, - ["comet3"] = { distance = 1.3, nozzleOffset = { forward = -0.52, right = -0.07, up = 0.20} }, - ["comet4"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.20, up = 0.35} }, - ["comet5"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.35} }, - ["coquette"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.28, up = 0.25} }, - ["coquette4"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.28, up = 0.25} }, - ["drafter"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.18, up = 0.45} }, - ["elegy"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.28, up = 0.30} }, - ["elegy2"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.18, up = 0.50} }, - ["feltzer2"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.15, up = 0.45} }, - ["flashgt"] = { distance = 1.3, nozzleOffset = { forward = -0.31, right = -0.26, up = 0.50} }, - ["furoregt"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.50} }, - ["gb200"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.20, up = 0.40} }, - ["komoda"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, - ["italigto"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.27, up = 0.40} }, - ["jugular"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.16, up = 0.55} }, - ["jester"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.23, up = 0.35} }, - ["jester2"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.23, up = 0.35} }, - ["jester3"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.16, up = 0.40} }, - ["kuruma"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.19, up = 0.40} }, - ["kuruma2"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.19, up = 0.40} }, - ["locust"] = { distance = 1.3, nozzleOffset = { forward = 0.44, right = 0.00, up = 0.61} }, - ["lynx"] = { distance = 1.3, nozzleOffset = { forward = 0.43, right = -0.24, up = 0.40} }, - ["massacro"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.21, up = 0.42} }, - ["massacro2"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.21, up = 0.42} }, - ["neo"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.23, up = 0.42} }, - ["ninef"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.24, up = 0.35} }, - ["ninef2"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.24, up = 0.35} }, - ["omnis"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.27, up = 0.31} }, - ["paragon"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.25, up = 0.40} }, - ["pariah"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.19, up = 0.45} }, - ["penumbra"] = { distance = 1.3, nozzleOffset = { forward = -0.25, right = -0.19, up = 0.50} }, - ["penumbra2"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.40} }, - ["rapidgt"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.17, up = 0.40} }, - ["rapidgt2"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.17, up = 0.40} }, - ["raptor"] = { distance = 1.3, nozzleOffset = { forward = -0.20, right = -0.40, up = 0.25} }, - ["revolter"] = { distance = 1.3, nozzleOffset = { forward = -0.55, right = -0.17, up = 0.45} }, - ["ruston"] = { distance = 1.3, nozzleOffset = { forward = 0.53, right = -0.00, up = 0.53} }, - ["schafter3"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.40} }, - ["schafter4"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.40} }, - ["schlagen"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.40} }, - ["seven70"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.23, up = 0.30} }, - ["specter"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.23, up = 0.30} }, - ["streiter"] = { distance = 1.3, nozzleOffset = { forward = -0.55, right = -0.08, up = 0.50} }, - ["sugoi"] = { distance = 1.3, nozzleOffset = { forward = -0.25, right = -0.15, up = 0.50} }, - ["sultan"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, - ["sultan2"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, - ["surano"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.26, up = 0.40} }, - ["tropos"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.08, up = 0.40} }, - ["verlierer2"] = { distance = 1.3, nozzleOffset = { forward = 1.60, right = -0.19, up = 0.30} }, - ["vstr"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.55} }, - ["zr350"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.21, up = 0.35} }, - ["calico"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.16, up = 0.45} }, - ["futo2"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.35} }, - ["euros"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.15, up = 0.55} }, - ["remus"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.16, up = 0.45} }, - ["comet6"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.35} }, - ["growler"] = { distance = 1.3, nozzleOffset = { forward = -0.20, right = -0.23, up = 0.45} }, - ["vectre"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.17, up = 0.45} }, - ["cypher"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.21, up = 0.45} }, - ["sultan3"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, - ["rt3000"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.17, up = 0.45} }, - ["sultanrs"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.13, up = 0.40} }, - ["visione"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.10, up = 0.40} }, - ["cheetah2"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.35} }, - ["stingertt"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.25, up = 0.35} }, - ["sentinel4"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.35} }, - ["sm722"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.35} }, - ["tenf"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.23, up = 0.43} }, - ["tenf2"] = { distance = 1.3, nozzleOffset = { forward = 0.43, right = -0.15, up = 0.43} }, - ["everon2"] = { distance = 1.3, nozzleOffset = { forward = -1.03, right = -0.24, up = 0.50} }, - ["issi8"] = { distance = 1.3, nozzleOffset = { forward = -0.20, right = -0.21, up = 0.55} }, - ["corsita"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.24, up = 0.30} }, - ["gauntlet6"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.21, up = 0.31} }, - ["coureur"] = { distance = 1.3, nozzleOffset = { forward = -0.10, right = -0.22, up = 0.45} }, - ["r300"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.26, up = 0.40} }, - ["panthere"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.14, up = 0.40} }, - - ["adder"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.19, up = 0.50} }, - ["autarch"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.23, up = 0.30} }, - ["banshee2"] = { distance = 1.3, nozzleOffset = { forward = -0.55, right = -0.09, up = 0.40} }, - ["bullet"] = { distance = 1.3, nozzleOffset = { forward = -0.00, right = -0.30, up = 0.05} }, - ["cheetah"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.31, up = 0.35} }, - ["entity2"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.27, up = 0.35} }, - ["entityxf"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.31, up = 0.35} }, - ["emerus"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.28, up = 0.35} }, - ["fmj"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.30} }, - ["furia"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.26, up = 0.35} }, - ["gp1"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.27, up = 0.30} }, - ["infernus"] = { distance = 1.3, nozzleOffset = { forward = 0.91, right = -0.14, up = 0.65} }, - ["italigtb"] = { distance = 1.3, nozzleOffset = { forward = 0.25, right = -0.20, up = 0.35} }, - ["italigtb2"] = { distance = 1.3, nozzleOffset = { forward = 0.25, right = -0.20, up = 0.40} }, - ["krieger"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.21, up = 0.25} }, - ["le7b"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.35, up = 0.25} }, - ["nero"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.29, up = 0.25} }, - ["nero2"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.29, up = 0.25} }, - ["osiris"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.27, up = 0.25} }, - ["penetrator"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.25, up = 0.25} }, - ["pfister811"] = { distance = 1.3, nozzleOffset = { forward = 0.75, right = -0.28, up = 0.35} }, - ["prototipo"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.27, up = 0.35} }, - ["reaper"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.15, up = 0.35} }, - ["s80"] = { distance = 1.3, nozzleOffset = { forward = 0.40, right = -0.31, up = 0.30} }, - ["sc1"] = { distance = 1.3, nozzleOffset = { forward = 0.40, right = -0.25, up = 0.30} }, - ["sheava"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.17, up = 0.35} }, - ["t20"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.27, up = 0.30} }, - ["taipan"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.25, up = 0.30} }, - ["tempesta"] = { distance = 1.3, nozzleOffset = { forward = 0.25, right = -0.10, up = 0.60} }, - ["thrax"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.22, up = 0.30} }, - ["tigon"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.27, up = 0.30} }, - ["turismor"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.28, up = 0.30} }, - ["tyrant"] = { distance = 1.3, nozzleOffset = { forward = 0.30, right = -0.29, up = 0.50} }, - ["tyrus"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.26, up = 0.30} }, - ["vacca"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.32, up = 0.35} }, - ["vagner"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.29, up = 0.35} }, - ["xa21"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.27, up = 0.35} }, - ["zentorno"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.29, up = 0.35} }, - ["zorrusso"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.27, up = 0.35} }, - ["ignus"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.32, up = 0.35} }, - ["zeno"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.24, up = 0.35} }, - ["deveste"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.21, up = 0.25} }, - ["lm87"] = { distance = 1.3, nozzleOffset = { forward = 0.40, right = -0.34, up = 0.25} }, - ["torero2"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.28, up = 0.35} }, - ["entity3"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.27, up = 0.25} }, - ["virtue"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.19, up = 0.35} }, - - ["bfinjection"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = 0.02, up = 0.35} }, - ["bifta"] = { distance = 1.3, nozzleOffset = { forward = 0.03, right = -0.65, up = 0.10} }, - - -- offroad - ["blazer"] = { distance = 1.3, nozzleOffset = { forward = -0.08, right = -0.29, up = 0.20} }, - ["blazer2"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.25, up = 0.25} }, - ["blazer3"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.25, up = 0.15} }, - ["blazer4"] = { distance = 1.3, nozzleOffset = { forward = -0.00, right = -0.22, up = 0.12} }, - ["blazer5"] = { distance = 1.3, nozzleOffset = { forward = -0.10, right = -0.55, up = 0.25} }, - ["brawler"] = { distance = 1.3, nozzleOffset = { forward = -0.16, right = -0.13, up = 0.90} }, - ["caracara"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.16, up = 0.80} }, - ["caracara2"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.16, up = 0.80} }, - ["dubsta3"] = { distance = 1.3, nozzleOffset = { forward = -0.75, right = -0.97, up = 0.40} }, - ["dune"] = { distance = 1.3, nozzleOffset = { forward = 0.05, right = -0.65, up = 0.05} }, - ["everon"] = { distance = 1.3, nozzleOffset = { forward = -0.80, right = -0.04, up = 0.80} }, - ["freecrawler"] = { distance = 1.3, nozzleOffset = { forward = -0.10, right = -0.20, up = 1.00} }, - ["hellion"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.17, up = 0.40} }, - ["kalahari"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.17, up = 0.40} }, - ["kamacho"] = { distance = 1.3, nozzleOffset = { forward = 1.05, right = -0.02, up = 0.60} }, - ["mesa3"] = { distance = 1.3, nozzleOffset = { forward = 0.25, right = 0.02, up = 0.85} }, - ["outlaw"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.13, up = 0.70} }, - ["rancherxl"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.23, up = 0.50} }, - ["rebel2"] = { distance = 1.3, nozzleOffset = { forward = 1.10, right = -0.09, up = 0.55} }, - ["riata"] = { distance = 1.3, nozzleOffset = { forward = 0.90, right = -0.02, up = 0.80} }, - ["sandking"] = { distance = 1.3, nozzleOffset = { forward = 0.90, right = -0.17, up = 0.70} }, - ["sandking2"] = { distance = 1.3, nozzleOffset = { forward = 0.90, right = -0.17, up = 0.70} }, - ["trophytruck"] = { distance = 1.3, nozzleOffset = { forward = 0.90, right = -0.10, up = 0.70} }, - ["trophytruck2"] = { distance = 1.3, nozzleOffset = { forward = 0.90, right = -0.10, up = 0.70} }, - ["vagrant"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = 0.15, up = 0.25} }, - ["verus"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.30, up = 0.08} }, - ["winky"] = { distance = 1.3, nozzleOffset = { forward = -1.00, right = -0.19, up = 0.50} }, - ["yosemite3"] = { distance = 1.3, nozzleOffset = { forward = 0.83, right = -0.19, up = 0.50} }, - ["mesa"] = { distance = 1.3, nozzleOffset = { forward = 0.30, right = -0.11, up = 0.66} }, - ["ratel"] = { distance = 1.3, nozzleOffset = { forward = 0.61, right = 0.16, up = 1.11} }, - ["l35"] = { distance = 1.3, nozzleOffset = { forward = 0.80, right = -0.17, up = 0.55} }, - ["monstrociti"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.23, up = 0.45} }, - ["draugur"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.16, up = 0.80} }, - - -- truck - ["guardian"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.16, up = 0.40} }, - ["mixer2"] = { distance = 1.3, nozzleOffset = { forward = 0.0, right = 0.11, up = -0.06} , nozzleRotation = { x = 0, y = 0, z = 180} }, - ["tiptruck2"] = { distance = 3.5, nozzleOffset = { forward = 2.00, right = -2.25, up = -0.24} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["tiptruck"] = { distance = 1.3, nozzleOffset = { forward = 0.01, right = -0.19, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["rubble"] = { distance = 1.3, nozzleOffset = { forward = 0.01, right = -0.19, up = 0.04} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["mixer"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.23, up = 0.04} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["flatbed"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.23, up = 0.04} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["dump"] = { distance = 1.3, nozzleOffset = { forward = 0.27, right = -0.57, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["bulldozer"] = { distance = 1.3, nozzleOffset = { forward = 0.70, right = -0.25, up = 0.80} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["handler"] = { distance = 1.3, nozzleOffset = { forward = 0.88, right = -0.52, up = 0.88} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["cutter"] = { distance = 1.3, nozzleOffset = { forward = 0.95, right = -0.42, up = 0.30} , nozzleRotation = { x = 0, y = 0, z = 0} }, - - -- utillity - ["slamtruck"] = { distance = 1.3, nozzleOffset = { forward = 0.70, right = -0.28, up = 0.26} }, - ["utillitruck"] = { distance = 2.0, nozzleOffset = { forward = -0.80, right = -1.25, up = 0.50} }, - ["utillitruck2"] = { distance = 2.0, nozzleOffset = { forward = -0.80, right = -1.25, up = 0.50} }, - ["utillitruck3"] = { distance = 2.0, nozzleOffset = { forward = -0.40, right = -0.30, up = 0.50} }, - ["tractor"] = { distance = 2.0, nozzleOffset = { forward = 1.50, right = 0.27, up = 0.30} }, - ["tractor2"] = { distance = 2.0, nozzleOffset = { forward = 1.60, right = 0.05, up = 0.20} }, - ["tractor3"] = { distance = 2.0, nozzleOffset = { forward = 1.60, right = 0.05, up = 0.20} }, - ["towtruck"] = { distance = 2.0, nozzleOffset = { forward = -0.45, right = -0.30, up = 0.10} }, - ["towtruck2"] = { distance = 2.0, nozzleOffset = { forward = 0.85, right = 0.05, up = 0.50} }, - ["scrap"] = { distance = 2.0, nozzleOffset = { forward = -0.52, right = -0.05, up = -0.05} }, - ["sadler"] = { distance = 1.3, nozzleOffset = { forward = 1.14, right = -0.22, up = 0.70} }, - ["ripley"] = { distance = 2.0, nozzleOffset = { forward = -0.95, right = -0.48, up = 0.40} }, - ["mower"] = { distance = 2.0, nozzleOffset = { forward = 1.00, right = 0.10, up = 0.63} }, - ["forklift"] = { distance = 1.3, nozzleOffset = { forward = 0.05, right = -0.27, up = -0.40} }, - ["docktug"] = { distance = 2.5, nozzleOffset = { forward = 0.0, right = -0.25, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, - - -- van - ["bison"] = { distance = 1.3, nozzleOffset = { forward = 0.70, right = -0.28, up = 0.26} }, - ["bobcatxl"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.28, up = 0.35} }, - ["burrito3"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.25, up = 0.35} }, - ["gburrito2"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.22, up = 0.35} }, - ["rumpo"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.25, up = 0.35} }, - ["journey"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.34, up = 0.45} }, - ["minivan"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.20, up = 0.45} }, - ["minivan2"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.20, up = 0.45} }, - ["paradise"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.23, up = 0.45} }, - ["rumpo3"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.23, up = 0.45} }, - ["speedo"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.24, up = 0.45} }, - ["speedo4"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.24, up = 0.45} }, - ["surfer"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.29, up = 0.45} }, - ["youga3"] = { distance = 1.3, nozzleOffset = { forward = 0.55, right = -0.18, up = 0.45} }, - ["youga"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.23, up = 0.45} }, - ["youga2"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.25, up = 0.40} }, - ["youga4"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.30, up = 0.40} }, - ["moonbeam"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.28, up = 0.40} }, - ["moonbeam2"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.24, up = 0.40} }, - ["boxville"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.24, up = 0.40} }, - ["boxville2"] = { distance = 1.3, nozzleOffset = { forward = -2.20, right = -0.30, up = 0.05} }, - ["boxville3"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.24, up = 0.40} }, - ["boxville4"] = { distance = 1.3, nozzleOffset = { forward = -2.20, right = -0.30, up = 0.05} }, - ["boxville5"] = { distance = 1.3, nozzleOffset = { forward = -2.20, right = -0.30, up = 0.05} }, - ["pony"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.24, up = 0.40} }, - ["pony2"] = { distance = 1.3, nozzleOffset = { forward = 0.40, right = -0.26, up = 0.40} }, - ["journey2"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.34, up = 0.45} }, - ["surfer3"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.29, up = 0.45} }, - ["speedo5"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.24, up = 0.45} }, - ["mule2"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.35, up = 0.75} }, - ["taco"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.24, up = 0.45} }, - - - -- Lspd, ems .... - ["riot"] = { distance = 2.5, nozzleOffset = { forward = -0.80, right = -1.30, up = 0.30} }, - ["riot2"] = { distance = 1.3, nozzleOffset = { forward = -0.70, right = -0.09, up = 0.65} }, - ["pbus"] = { distance = 1.3, nozzleOffset = { forward = -0.70, right = -0.30, up = 0.65} }, - ["police"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.17, up = 0.50} }, - ["police2"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.24, up = 0.50} }, - ["police3"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.17, up = 0.50} }, - ["police4"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.17, up = 0.50} }, - ["sheriff"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.17, up = 0.50} }, - ["sheriff2"] = { distance = 1.3, nozzleOffset = { forward = 0.70, right = -0.28, up = 0.60} }, - ["policeold1"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.24, up = 0.60} }, - ["policeold2"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.16, up = 0.40} }, - ["policet"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.26, up = 0.60} }, - ["policeb"] = { distance = 1.3, nozzleOffset = { forward = -0.18, right = -0.18, up = 0.10}, nozzleRotation = { x = 0, y = 0, z = 60} }, - ["polmav"] = { distance = 3.0, nozzleOffset = { forward = 0.12, right = -0.60, up = -0.45}, nozzleRotation = { x = 0, y = 0, z = 20} }, - ["ambulance"] = { distance = 1.3, nozzleOffset = { forward = 2.95, right = -0.08, up = 0.50} }, - ["firetruk"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.32, up = 0.70} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["lguard"] = { distance = 1.3, nozzleOffset = { forward = 0.67, right = -0.27, up = 0.57} }, - ["pranger"] = { distance = 1.3, nozzleOffset = { forward = 0.67, right = -0.27, up = 0.57} }, - ["fbi"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.40} }, - ["fbi2"] = { distance = 1.3, nozzleOffset = { forward = 0.67, right = -0.27, up = 0.57} }, - ["predator"] = { distance = 3.5, nozzleOffset = { forward = 1.80, right = 1.58, up = 0.17}, nozzleRotation = { x = 0, y = 0, z = 180} }, - - -- Military - ["apc"] = { distance = 2.5, nozzleOffset = { forward = -0.80, right = -1.00, up = 1.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["barracks"] = { distance = 3.5, nozzleOffset = { forward = 0.00, right = -0.25, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["barracks2"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.28, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["barracks3"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.16, up = -0.03} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["chernobog"] = { distance = 2.5, nozzleOffset = { forward = 3.70, right = -0.20, up = 0.22} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["crusader"] = { distance = 1.5, nozzleOffset = { forward = 0.25, right = -0.11, up = 0.65} }, - ["halftrack"] = { distance = 1.5, nozzleOffset = { forward = -0.60, right = -0.30, up = 0.90} }, - ["khanjali"] = { distance = 1.5, nozzleOffset = { forward = -0.55, right = -0.80, up = 0.95} }, - ["rhino"] = { distance = 1.5, nozzleOffset = { forward = -0.05, right = -0.52, up = 1.00} }, - ["scarab"] = { distance = 1.5, nozzleOffset = { forward = 0.60, right = 0.15, up = 1.08} }, - ["terbyte"] = { distance = 1.5, nozzleOffset = { forward = -0.700, right = -0.47, up = 0.65} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["vetir"] = { distance = 3.0, nozzleOffset = { forward = 1.25, right = 1.95, up = 0.40} , nozzleRotation = { x = 0, y = 0, z = 180} }, - - ["thruster"] = { distance = 1.5, nozzleOffset = { forward = -0.10, right = 0.40, up = 1.25} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["minitank"] = { distance = 1.5, nozzleOffset = { forward = 0.05, right = -0.05, up = 0.49} }, - - -- Electric cars - ["voltic"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.14, up = 0.45} }, - ["voltic2"] = { distance = 1.3, nozzleOffset = { forward = -0.12, right = 0.12, up = 0.57} }, - ["caddy"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.09, up = 0.53} }, - ["caddy2"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.09, up = 0.35} }, - ["caddy3"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.09, up = 0.35} }, - ["surge"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.14, up = 0.45} }, - ["iwagen"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.16, up = 0.50} }, - ["raiden"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.13, up = 0.50} }, - ["airtug"] = { distance = 1.3, nozzleOffset = { forward = -0.20, right = -0.18, up = 0.47} }, - ["neon"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.16, up = 0.50} }, - ["omnisegt"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.16, up = 0.40} }, - ["cyclone"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.16, up = 0.45} }, - ["tezeract"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.16, up = 0.48} }, - ["imorgon"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.16, up = 0.48} }, - ["dilettante"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.14, up = 0.48} }, - ["dilettante2"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.14, up = 0.48} }, - ["khamelion"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.20, up = 0.48} }, - - ["rcbandito"] = { distance = 1.3, nozzleOffset = { forward = -0.12, right = 0.12, up = 0.22} }, - - - -- Motorcycles - ["akuma"] = { distance = 1.1, nozzleOffset = { forward = 0.01, right = -0.20, up = 0.20} }, - ["avarus"] = { distance = 1.1, nozzleOffset = { forward = -0.22, right = 0.03, up = 0.11} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["bagger"] = { distance = 1.1, nozzleOffset = { forward = -0.26, right = 0.03, up = 0.11} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["bati"] = { distance = 1.1, nozzleOffset = { forward = -0.15, right = -0.25, up = 0.20} }, - ["bati2"] = { distance = 1.1, nozzleOffset = { forward = -0.15, right = -0.24, up = 0.20} }, - ["bf400"] = { distance = 1.1, nozzleOffset = { forward = -0.05, right = -0.24, up = 0.29} }, - ["carbonrs"] = { distance = 1.1, nozzleOffset = { forward = 0.01, right = -0.22, up = 0.20} }, - ["chimera"] = { distance = 1.1, nozzleOffset = { forward = -0.22, right = 0.00, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["cliffhanger"] = { distance = 1.1, nozzleOffset = { forward = -0.33, right = 0.05, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["daemon"] = { distance = 1.1, nozzleOffset = { forward = -0.17, right = 0.03, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["daemon2"] = { distance = 1.1, nozzleOffset = { forward = -0.17, right = 0.03, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["defiler"] = { distance = 1.1, nozzleOffset = { forward = 0.09, right = -0.23, up = 0.20} }, - ["deathbike"] = { distance = 1.1, nozzleOffset = { forward = -0.17, right = 0.03, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["deathbike2"] = { distance = 1.1, nozzleOffset = { forward = -0.25, right = 0.03, up = 0.07} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["deathbike3"] = { distance = 1.1, nozzleOffset = { forward = -0.17, right = 0.03, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["diablous"] = { distance = 1.1, nozzleOffset = { forward = -0.27, right = 0.06, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["diablous2"] = { distance = 1.1, nozzleOffset = { forward = -0.27, right = 0.03, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["double"] = { distance = 1.1, nozzleOffset = { forward = 0.01, right = -0.22, up = 0.20} }, - ["enduro"] = { distance = 1.1, nozzleOffset = { forward = -0.05, right = -0.17, up = 0.25} }, - ["esskey"] = { distance = 1.1, nozzleOffset = { forward = -0.05, right = -0.20, up = 0.20} }, - ["faggio"] = { distance = 1.1, nozzleOffset = { forward = 0.20, right = -0.28, up = 0.30} }, - ["faggio2"] = { distance = 1.1, nozzleOffset = { forward = 0.20, right = 0.25, up = -0.10} , nozzleRotation = { x = 0, y = 0, z = 180} }, - ["faggio3"] = { distance = 1.1, nozzleOffset = { forward = 0.20, right = 0.25, up = -0.10} , nozzleRotation = { x = 0, y = 0, z = 180} }, - ["fcr"] = { distance = 1.1, nozzleOffset = { forward = -0.03, right = -0.21, up = 0.10} }, - ["gargoyle"] = { distance = 1.1, nozzleOffset = { forward = -0.26, right = 0.03, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["hakuchou"] = { distance = 1.1, nozzleOffset = { forward = 0.05, right = -0.17, up = 0.10} }, - ["hakuchou2"] = { distance = 1.1, nozzleOffset = { forward = 0.00, right = -0.19, up = 0.10} }, - ["hexer"] = { distance = 1.1, nozzleOffset = { forward = -0.17, right = 0.04, up = 0.20} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["innovation"] = { distance = 1.1, nozzleOffset = { forward = -0.23, right = 0.02, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["lectro"] = { distance = 1.1, nozzleOffset = { forward = -0.12, right = -0.20, up = 0.20} }, - ["manchez"] = { distance = 1.1, nozzleOffset = { forward = -0.04, right = -0.20, up = 0.10} }, - ["nemesis"] = { distance = 1.1, nozzleOffset = { forward = -0.03, right = -0.17, up = 0.10} }, - ["nightblade"] = { distance = 1.1, nozzleOffset = { forward = -0.27, right = 0.05, up = 0.14} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["oppressor"] = { distance = 1.1, nozzleOffset = { forward = -0.27, right = 0.05, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["pcj"] = { distance = 1.1, nozzleOffset = { forward = 0.04, right = -0.20, up = 0.20} }, - ["ratbike"] = { distance = 1.1, nozzleOffset = { forward = -0.22, right = 0.03, up = 0.11} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["ruffian"] = { distance = 1.1, nozzleOffset = { forward = 0.04, right = -0.19, up = 0.20} }, - ["sanchez"] = { distance = 1.1, nozzleOffset = { forward = -0.05, right = -0.22, up = 0.25} }, - ["sanchez2"] = { distance = 1.1, nozzleOffset = { forward = -0.05, right = -0.22, up = 0.25} }, - ["sanctus"] = { distance = 1.1, nozzleOffset = { forward = -0.22, right = 0.03, up = 0.08} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["shotaro"] = { distance = 1.1, nozzleOffset = { forward = 0.06, right = -0.25, up = 0.20} }, - ["sovereign"] = { distance = 1.1, nozzleOffset = { forward = -0.07, right = -0.23, up = 0.15} }, - ["stryder"] = { distance = 1.1, nozzleOffset = { forward = 0.06, right = -0.20, up = 0.15} }, - ["thrust"] = { distance = 1.1, nozzleOffset = { forward = -0.02, right = -0.25, up = 0.15} }, - ["vader"] = { distance = 1.1, nozzleOffset = { forward = 0.10, right = -0.25, up = 0.20} }, - ["vindicator"] = { distance = 1.1, nozzleOffset = { forward = -0.02, right = -0.25, up = 0.15} }, - ["vortex"] = { distance = 1.1, nozzleOffset = { forward = -0.02, right = -0.20, up = 0.12} }, - ["wolfsbane"] = { distance = 1.1, nozzleOffset = { forward = -0.22, right = 0.03, up = 0.11} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["zombiea"] = { distance = 1.1, nozzleOffset = { forward = -0.14, right = 0.03, up = 0.11} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["zombieb"] = { distance = 1.1, nozzleOffset = { forward = -0.21, right = 0.03, up = 0.15} , nozzleRotation = { x = 0, y = 0, z = 90} }, - ["manchez2"] = { distance = 1.1, nozzleOffset = { forward = -0.04, right = -0.22, up = 0.10} }, - ["shinobi"] = { distance = 1.1, nozzleOffset = { forward = -0.04, right = -0.22, up = 0.20} }, - ["reever"] = { distance = 1.1, nozzleOffset = { forward = 0.09, right = -0.20, up = 0.10} }, - ["manchez3"] = { distance = 1.1, nozzleOffset = { forward = -0.04, right = -0.22, up = 0.10} }, - - -- truck 2 - ["pounder2"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.12, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["mule4"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.35, up = 0.75} }, - ["phantom3"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["hauler2"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.17, up = 0.02} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["phantom2"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["mule5"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.35, up = 0.75} }, - ["stockade"] = { distance = 3.0, nozzleOffset = { forward = -0.50, right = -1.36, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["pounder"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.12, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["phantom"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["packer"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.17, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["mule"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.35, up = 0.75} }, - ["hauler"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.17, up = -0.02} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["biff"] = { distance = 1.3, nozzleOffset = { forward = 0.0, right = 0.11, up = -0.06} , nozzleRotation = { x = 0, y = 0, z = 180} }, - ["benson"] = { distance = 1.3, nozzleOffset = { forward = 0.32, right = 0.40, up = 0.21} , nozzleRotation = { x = 0, y = 0, z = 180} }, - - --helicopters - ["conada2"] = { distance = 3.5, nozzleOffset = { forward = -0.10, right = -0.90, up = 0.20}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["akula"] = { distance = 3.5, nozzleOffset = { forward = -1.50, right = -0.65, up = -0.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["annihilator2"] = { distance = 3.5, nozzleOffset = { forward = -1.10, right = -1.25, up = -1.70}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["annihilator"] = { distance = 3.5, nozzleOffset = { forward = -1.20, right = -1.05, up = -1.70}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["havok"] = { distance = 1.5, nozzleOffset = { forward = -0.10, right = -0.49, up = 0.00}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["hunter"] = { distance = 3.5, nozzleOffset = { forward = -1.50, right = -0.69, up = -0.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["seasparrow"] = { distance = 1.5, nozzleOffset = { forward = -0.10, right = -0.70, up = 0.00}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["seasparrow2"] = { distance = 3.2, nozzleOffset = { forward = -0.10, right = -0.70, up = 0.00}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["supervolito"] = { distance = 2.0, nozzleOffset = { forward = -0.50, right = -0.87, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["supervolito2"] = { distance = 2.0, nozzleOffset = { forward = -0.50, right = -0.87, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["swift"] = { distance = 2.5, nozzleOffset = { forward = -1.10, right = -0.90, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["swift2"] = { distance = 2.5, nozzleOffset = { forward = -1.10, right = -0.90, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["valkyrie"] = { distance = 2.5, nozzleOffset = { forward = 1.60, right = -1.21, up = -1.80}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["valkyrie2"] = { distance = 2.5, nozzleOffset = { forward = 1.60, right = -1.21, up = -1.80}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["volatus"] = { distance = 2.5, nozzleOffset = { forward = -1.10, right = -0.86, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["savage"] = { distance = 3.5, nozzleOffset = { forward = -0.10, right = -1.09, up = -2.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["buzzard2"] = { distance = 2.2, nozzleOffset = { forward = -1.10, right = -0.76, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 10} }, - ["buzzard"] = { distance = 2.5, nozzleOffset = { forward = -1.10, right = -0.76, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 10} }, - ["cargobob"] = { distance = 4.0, nozzleOffset = { forward = -2.20, right = -1.29, up = -2.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["cargobob2"] = { distance = 4.0, nozzleOffset = { forward = -2.20, right = -1.29, up = -2.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["cargobob3"] = { distance = 4.0, nozzleOffset = { forward = -2.20, right = -1.29, up = -2.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["cargobob4"] = { distance = 4.0, nozzleOffset = { forward = -2.20, right = -1.29, up = -2.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["conada"] = { distance = 2.0, nozzleOffset = { forward = -0.09, right = -0.90, up = 0.20}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["frogger"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = -0.92, up = -0.90}, nozzleRotation = { x = 0, y = 0, z = 20} }, - ["frogger2"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = -0.92, up = -0.90}, nozzleRotation = { x = 0, y = 0, z = 20} }, - ["seasparrow3"] = { distance = 1.5, nozzleOffset = { forward = -0.10, right = -0.70, up = 0.00}, nozzleRotation = { x = 0, y = 0, z = 0} }, - ["skylift"] = { distance = 4.0, nozzleOffset = { forward = 4.95, right = 0.00, up = -3.50}, nozzleRotation = { x = 0, y = 0, z = 90} }, - ["maverick"] = { distance = 3.5, nozzleOffset = { forward = 0.00, right = -0.90, up = -1.50}, nozzleRotation = { x = 0, y = 0, z = 20} }, - - -- Boats - ["toro"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.75, up = 0.48 } , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["toro2"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.75, up = 0.48 } , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["patrolboat"] = { distance = 1.0, nozzleOffset = { forward = -0.30, right = 0.25, up = 0.20 } , nozzleRotation = { x = 0, y = 0, z = 180} }, - ["longfin"] = { distance = 1.5, nozzleOffset = { forward = 0.90 , right = -1.40, up = 0.68} , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["speeder"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.95, up = 0.65 } , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["speeder2"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.95, up = 0.65 } , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["tropic"] = { distance = 2.0, nozzleOffset = { forward = 1.00, right = -1.15, up = 0.71} , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["tropic2"] = { distance = 2.0, nozzleOffset = { forward = 1.00, right = -1.15, up = 0.71} , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["suntrap"] = { distance = 2.0, nozzleOffset = { forward = 0.55, right = -0.95, up = 0.93} , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["squalo"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.95, up = 0.57 } , nozzleRotation = { x = -60, y = 0, z = 90} }, - ["marquis"] = { distance = 1.5, nozzleOffset = { forward = 0.50, right = -1.35, up = 1.36 } , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["jetmax"] = { distance = 2.0, nozzleOffset = { forward = 0.50, right = -0.90, up = 0.54 } , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["dinghy"] = { distance = 1.0, nozzleOffset = { forward = -0.20, right = -0.69, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["dinghy2"] = { distance = 1.0, nozzleOffset = { forward = -0.20, right = -0.69, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["dinghy3"] = { distance = 1.0, nozzleOffset = { forward = -0.20, right = -0.69, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["dinghy4"] = { distance = 1.0, nozzleOffset = { forward = -0.20, right = -0.69, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["dinghy5"] = { distance = 1.0, nozzleOffset = { forward = -0.20, right = -0.69, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - - -- Jet - ["seashark"] = { distance = 2.0, nozzleOffset = { forward = 0.89, right = 0.00, up = 0.42 } , nozzleRotation = { x = -60, y = 0, z = 90} }, - ["seashark3"] = { distance = 2.0, nozzleOffset = { forward = 0.89, right = 0.00, up = 0.42 } , nozzleRotation = { x = -60, y = 0, z = 90} }, - ["seashark2"] = { distance = 2.0, nozzleOffset = { forward = 0.89, right = 0.00, up = 0.42 } , nozzleRotation = { x = -60, y = 0, z = 90} }, - - -- Submarine - ["avisa"] = { distance = 2.0, nozzleOffset = { forward = -0.40, right = -1.30, up = 0.14 } , nozzleRotation = { x = -85, y = 0, z = 90} }, - ["submersible2"] = { distance = 2.5, nozzleOffset = { forward = -0.40, right = -0.70, up = 1.20 } , nozzleRotation = { x = -90, y = 0, z = 0} }, - ["submersible"] = { distance = 2.5, nozzleOffset = { forward = -0.60, right = -0.85, up = 1.20 } , nozzleRotation = { x = -90, y = 0, z = 0} }, - - -- Big boats --- ["kosatka"] = { distance = 6.0, nozzleOffset = { forward = 0.0, right = -0.15, up = 1.0 } }, ----- BIG SUBMARINE - ["tug"] = { distance = 2.5, nozzleOffset = { forward = 0.0, right = 0.79, up = 0.20} , nozzleRotation = { x = 0, y = 0, z = 180} }, - - -- Plane - ["streamer216"] = { distance = 1.5, nozzleOffset = { forward = -0.40, right = 1.02, up = 1.10 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["raiju"] = { distance = 3.0, nozzleOffset = { forward = 1.0, right = -0.85, up = -0.25 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["alkonost"] = { distance = 11.0, nozzleOffset = { forward = -2.60, right = -5.30, up = 0.90 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["strikeforce"] = { distance = 3.0, nozzleOffset = { forward = 3.0, right = 1.37, up = 1.60 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["blimp3"] = { distance = 2.0, nozzleOffset = { forward = -0.40, right = 0.41, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["avenger"] = { distance = 2.0, nozzleOffset = { forward = -0.60, right = -0.73, up = 1.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["avenger2"] = { distance = 2.0, nozzleOffset = { forward = -0.60, right = -0.73, up = 1.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["volatol"] = { distance = 2.0, nozzleOffset = { forward = 0.80, right = 0.46, up = 1.85} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["nokota"] = { distance = 2.5, nozzleOffset = { forward = -1.50, right = 1.60, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["seabreeze"] = { distance = 2.5, nozzleOffset = { forward = -0.20, right = -0.10, up = -1.70} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["pyro"] = { distance = 3.0, nozzleOffset = { forward = 2.10, right = 1.56, up = 0.90} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["mogul"] = { distance = 2.0, nozzleOffset = { forward = 0.30, right = -0.68, up = 1.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["howard"] = { distance = 2.5, nozzleOffset = { forward = 1.80, right = 1.25, up = 1.25} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["bombushka"] = { distance = 2.5, nozzleOffset = { forward = 0.50, right = -0.71, up = 1.25} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["molotok"] = { distance = 3.5, nozzleOffset = { forward = 2.00, right = -0.85, up = 0.55} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["microlight"] = { distance = 3.5, nozzleOffset = { forward = -0.02, right = 0.48, up = 0.30} , nozzleRotation = { x = 0, y = 0, z = 45} }, - ["tula"] = { distance = 3.5, nozzleOffset = { forward = 0.70, right = -0.52, up = -0.30} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["rogue"] = { distance = 3.5, nozzleOffset = { forward = 0.10, right = -0.15, up = 0.45} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["starling"] = { distance = 3.5, nozzleOffset = { forward = 0.10, right = -0.08, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["alphaz1"] = { distance = 3.5, nozzleOffset = { forward = -1.00, right = -0.03, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["nimbus"] = { distance = 5.0, nozzleOffset = { forward = 0.00, right = -0.10, up = -0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["luxor2"] = { distance = 5.0, nozzleOffset = { forward = 0.00, right = 0.80, up = -0.30} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["velum2"] = { distance = 5.0, nozzleOffset = { forward = -0.30, right = -0.20, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["hydra"] = { distance = 2.5, nozzleOffset = { forward = -1.80, right = 1.88, up = 1.35} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["blimp2"] = { distance = 2.0, nozzleOffset = { forward = -0.40, right = 0.41, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["dodo"] = { distance = 2.0, nozzleOffset = { forward = -0.15, right = -0.13, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["miljet"] = { distance = 6.0, nozzleOffset = { forward = 2.75, right = -0.47, up = -1.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["vestra"] = { distance = 2.5, nozzleOffset = { forward = 2.50, right = 1.35, up = 1.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["cargoplane"] = { distance = 2.5, nozzleOffset = { forward = 1.50, right = 0.27, up = 1.60} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["velum"] = { distance = 5.0, nozzleOffset = { forward = -0.30, right = -0.20, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["titan"] = { distance = 2.5, nozzleOffset = { forward = 0.50, right = -0.46, up = 1.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["shamal"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.10, up = -0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["lazer"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.09, up = -0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["stunt"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.08, up = -0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["luxor"] = { distance = 5.0, nozzleOffset = { forward = 0.00, right = 0.80, up = -0.30} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["jet"] = { distance = 15.0, nozzleOffset = { forward = 1.20, right = 1.20, up = 2.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["duster"] = { distance = 2.5, nozzleOffset = { forward = -0.20, right = -0.15, up = -0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["cuban800"] = { distance = 2.5, nozzleOffset = { forward = 1.00, right = 0.51, up = 0.20} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["blimp"] = { distance = 2.0, nozzleOffset = { forward = -0.40, right = 0.41, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, - - -- Service - ["brickade"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = 0.16, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, - ["brickade2"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = 0.16, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, - ["pbus2"] = { distance = 3.0, nozzleOffset = { forward = -2.38, right = -0.30, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["wastelander"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.05, up = -0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["rallytruck"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = 0.64, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, --- ["metrotrain"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = 0.64, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, --- metro --- ["freight"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = 0.64, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, -- train --- ["cablecar"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = 0.64, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, -- cablecar - ["trash"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.15, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["trash2"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.15, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["tourbus"] = { distance = 2.0, nozzleOffset = { forward = -0.50, right = -0.33, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["taxi"] = { distance = 2.0, nozzleOffset = { forward = -0.50, right = -0.20, up = 0.45} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["rentalbus"] = { distance = 2.0, nozzleOffset = { forward = -0.50, right = -0.33, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["coach"] = { distance = 2.0, nozzleOffset = { forward = -0.80, right = -0.29, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["bus"] = { distance = 2.0, nozzleOffset = { forward = -0.80, right = -0.28, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, - ["airbus"] = { distance = 2.0, nozzleOffset = { forward = -0.80, right = -0.28, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, - - -- Race - ["openwheel2"] = { distance = 2.0, nozzleOffset = { forward = 0.0, right = -0.60, up = 0.30} , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["openwheel1"] = { distance = 2.0, nozzleOffset = { forward = 0.0, right = -0.60, up = 0.35} , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["formula2"] = { distance = 2.0, nozzleOffset = { forward = 0.0, right = -0.50, up = 0.29} , nozzleRotation = { x = -75, y = 0, z = 90} }, - ["formula"] = { distance = 2.0, nozzleOffset = { forward = 0.0, right = -0.50, up = 0.29} , nozzleRotation = { x = -75, y = 0, z = 90} }, -} +-- Shared variables +Utils = Utils or exports['lc_utils']:GetUtils() +cachedTranslations = {} +mainUiOpen = false + +fuelNozzle = 0 +fuelRope = 0 +currentPump = 0 +JERRY_CAN_HASH = 883325847 + +-- Fuel chart variables +isFuelConsumptionChartOpen = false +isRecording = true + +-- Local variables +local fuelDecor = "_FUEL_LEVEL" +local currentConsumption = 0.0 +local fuelSynced = false +local closestVehicleToPump = 0 +local isNuiVariablesLoaded = false + +DecorRegister(fuelDecor, 1) +-- Check if it actually registered +if DecorIsRegisteredAsType(fuelDecor, 1) then + print("[Fuel Script] SUCCESS: fuelDecor was registered successfully!") +else + print("[Fuel Script] ERROR: fuelDecor registration failed!") +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Threads +----------------------------------------------------------------------------------------------------------------------------------------- + +-- Thread to handle the fuel consumption +function createFuelConsumptionThread() + CreateThread(function() + local currentVehicle = nil + local currentVehicleFuelType = "default" -- default while the callback loads + DecorRegister(fuelDecor, 1) + while true do + Wait(1000) + local ped = PlayerPedId() + if IsPedInAnyVehicle(ped, false) then + local vehicle = GetVehiclePedIsIn(ped, false) + if GetPedInVehicleSeat(vehicle, -1) == ped and not IsVehicleBlacklisted(vehicle) then + if currentVehicle == nil or currentVehicle ~= vehicle then + currentVehicle = vehicle + currentVehicleFuelType = getVehicleFuelTypeFromServer(vehicle) + fuelSynced = false + end + HandleFuelConsumption(vehicle, currentVehicleFuelType) + end + else + if isFuelConsumptionChartOpen then + closeFuelConsumptionChartUI() + end + currentVehicleFuelType = "default" + currentVehicle = nil + fuelSynced = false + end + end + end) +end + +function HandleFuelConsumption(vehicle, fuelType) + -- If no decorator exists, set a random fuel % + if not DecorExistOn(vehicle, fuelDecor) then + SetFuel(vehicle, math.random(200, 800) / 10) + elseif not fuelSynced then + -- Sync once + SetFuel(vehicle, GetFuel(vehicle)) + fuelSynced = true + end + + -- If engine is off, do nothing + if not GetIsVehicleEngineRunning(vehicle) then + return + end + + -- Turns engine off if have no fuel + local currentFuelLevel = GetVehicleFuelLevel(vehicle) + if currentFuelLevel <= 0.0 then + SetVehicleEngineOn(vehicle, false, true, false) + return + end + + -- Get the vehicle's actual tank size in liters + local tankSize = getVehicleTankSize(vehicle) + + -- Base consumption from config and RPM + currentConsumption = Config.FuelUsage[Utils.Math.round(GetVehicleCurrentRpm(vehicle), 1)] * (Config.FuelConsumptionPerClass[GetVehicleClass(vehicle)] or 1.0) * (Config.FuelConsumptionPerFuelType[fuelType] or 1.0) / 10 + + -- Scale consumption according to tank size. + -- A smaller tank will lose percentage faster; a bigger tank slower. + local scaledConsumption = currentConsumption * (100 / tankSize) + + -- Subtract from the current fuel % + local newFuelLevel = currentFuelLevel - scaledConsumption + + -- Write the new fuel % back + SetFuel(vehicle, newFuelLevel) + + -- Store data for chart + if Config.FuelConsumptionChart.enabled then + storeDataForChart(vehicle, newFuelLevel, currentConsumption) + end + + validateDieselFuelMismatch(vehicle, fuelType) +end + +function validateDieselFuelMismatch(vehicle, fuelType) + if (fuelType == "diesel" and not IsVehicleDiesel(vehicle)) or (fuelType ~= "diesel" and IsVehicleDiesel(vehicle)) then + SetTimeout(5000, function() + if IsVehicleDriveable(vehicle, false) then + SetVehicleEngineHealth(vehicle, 0.0) + SetVehicleUndriveable(vehicle, true) + exports['lc_utils']:notify("error", Utils.translate("vehicle_wrong_fuel")) + end + end) + end +end + +function createDebugNozzleOffsetThread() + CreateThread(function() + while true do + local vehicle = GetVehiclePlayerIsLookingAt() + if not vehicle or not DoesEntityExist(vehicle) then + Wait(2) + goto continue -- Skip if no valid vehicle found + end + + local hit, hitCoords = GetLookAtHitCoords() + if hit and DoesEntityExist(vehicle) then + local offset = GetNozzleOffset(vehicle, hitCoords) + + if offset then + local vehCoords = GetEntityCoords(vehicle) + + -- Get vehicle's axis vectors + local _, rightVector, _ = GetEntityMatrix(vehicle) + + -- Direction from vehicle to hit point + local directionToHit = hitCoords - vehCoords + directionToHit = directionToHit / #(directionToHit) -- normalize + + -- Dot product with right vector + local side = rightVector.x * directionToHit.x + rightVector.y * directionToHit.y + rightVector.z * directionToHit.z + + -- Adjust offset.right + if side > 0 then + -- hit is on the right side + offset.right = offset.right + 0.07 + else + -- hit is on the left side + offset.right = offset.right - 0.07 + end + + -- Final world position of the nozzle point + local finalWorldPos = getWorldPosFromOffset(vehicle, offset) + + -- Draw a small marker at the computed position + DrawMarker(28, finalWorldPos.x, finalWorldPos.y, finalWorldPos.z + 0.05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.03, 0.03, 0.03, 255, 0, 0, 180, false, true, 2, nil, nil, false) + + if IsControlJustPressed(0,38) then + local model = GetEntityModel(vehicle) + local modelName = GetDisplayNameFromVehicleModel(model):lower() + + local zRotation = side > 0 and 180 or 0 + + local clipboardText = string.format( + '["%s"] = { distance = 1.3, nozzleOffset = { forward = %.2f, right = %.2f, up = %.2f }, nozzleRotation = { x = 0, y = 0, z = %d } },', + modelName, + offset.forward, offset.right, offset.up, + zRotation + ) + + SendNUIMessage({ + type = "copyToClipboard", + text = clipboardText + }) + + exports['lc_utils']:notify("success", "Copied to clipboard: " .. clipboardText) + exports['lc_utils']:notify("warning", "Make sure the vehicle spawn name is correct. You may need to manually fine-tune the offset and rotation.") + end + end + end + Wait(2) + ::continue:: + end + end) +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- UI +----------------------------------------------------------------------------------------------------------------------------------------- + +function clientOpenUI(pump, pumpModel, isElectricPump) + currentPump = pump + local ped = PlayerPedId() + local playerCoords = GetEntityCoords(ped) + + closestVehicleToPump = GetClosestVehicle(playerCoords) + local isElectricVehicle = IsVehicleElectric(closestVehicleToPump) + if not (isElectricVehicle and isElectricPump) and not (not isElectricVehicle and not isElectricPump) then + -- Reset the near vehicle to 0 when it does not match the pump type (only allows electric vehicle in electric chargers and gas vehicles in gas pumps) + closestVehicleToPump = 0 + end + + pumpLocation = nil + + if closestVehicleToPump and #(playerCoords - GetEntityCoords(closestVehicleToPump)) < 5 then + -- Load the nearest vehicle fuel and plate (fuel type) + local vehicleFuel = GetFuel(closestVehicleToPump) + local vehiclePlate = GetVehicleNumberPlateText(closestVehicleToPump) + local vehicleTankSize = getVehicleTankSize(closestVehicleToPump) + local vehicleDisplayFuelAmount = getVehicleDisplayFuelAmount(vehicleFuel, vehicleTankSize) + TriggerServerEvent("lc_fuel:serverOpenUI", isElectricPump, pumpModel, vehicleDisplayFuelAmount, vehicleTankSize, vehiclePlate) + else + -- Allow the user to open the UI even without vehicles nearby + TriggerServerEvent("lc_fuel:serverOpenUI", isElectricPump, pumpModel) + end +end + +function loadNuiVariables() + if isNuiVariablesLoaded then + return + end + + -- Load NUI variables + SendNUIMessage({ + utils = { config = Utils.Config, lang = Utils.Lang }, + resourceName = GetCurrentResourceName() + }) + + local maxIterations = 100 -- Maximum number of iterations (100 * 100ms = 10 seconds) + local iterations = 0 + + while not isNuiVariablesLoaded do + Wait(100) + iterations = iterations + 1 + + if iterations >= maxIterations then + print("Error: Timeout while loading NUI variables after " .. iterations .. " attempts.") + return + end + end +end + + +RegisterNetEvent('lc_fuel:clientOpenUI') +AddEventHandler('lc_fuel:clientOpenUI', function(data) + loadNuiVariables() + data.currentFuelType = dealWithDefaultFuelType(closestVehicleToPump, data.currentFuelType) + SendNUIMessage({ + openMainUI = true, + data = data + }) + mainUiOpen = true + TriggerScreenblurFadeIn(1000) + FreezeEntityPosition(PlayerPedId(), true) + SetNuiFocus(true,true) +end) + +RegisterNUICallback('post', function(body, cb) + if cooldown == nil then + cooldown = true + + if body.event == "close" then + closeUI() + elseif body.event == "closeFuelConsumptionChartUI" then + closeFuelConsumptionChartUI() + elseif body.event == "removeFocusFuelConsumptionChartUI" then + SetNuiFocus(false,false) + elseif body.event == "startRecordingGraph" then + isRecording = true + elseif body.event == "stopRecordingGraph" then + isRecording = false + elseif body.event == "notify" then + exports['lc_utils']:notify(body.data.type,body.data.msg) + elseif body.event == "changeVehicleFuelType" then + changeVehicleFuelType(closestVehicleToPump, body.data.selectedFuelType) + else + TriggerServerEvent('lc_fuel:'..body.event,body.data) + end + cb(200) + + SetTimeout(5,function() + cooldown = nil + end) + end +end) + +RegisterNUICallback('setNuiVariablesLoaded', function(body, cb) + isNuiVariablesLoaded = true + cb(200) +end) + +function closeUI() + mainUiOpen = false + FreezeEntityPosition(PlayerPedId(), false) + TriggerScreenblurFadeOut(1000) + SetNuiFocus(false,false) + SendNUIMessage({ hideMainUI = true }) +end + +RegisterNetEvent('lc_fuel:closeUI') +AddEventHandler('lc_fuel:closeUI', function() + closeUI() +end) + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Exports +----------------------------------------------------------------------------------------------------------------------------------------- + +function GetFuel(vehicle) + if not DoesEntityExist(vehicle) then + warn(("[GetFuel] Vehicle entity does not exist. Received: %s. This is usually caused by a misconfiguration in the export."):format(tostring(vehicle))) + return 0 + end + return DecorGetFloat(vehicle, fuelDecor) +end +exports('GetFuel', GetFuel) + +function SetFuel(vehicle, fuel) + if not DoesEntityExist(vehicle) then + warn(("[SetFuel] Vehicle entity does not exist. Received: %s. This is usually caused by a misconfiguration in the export."):format(tostring(vehicle))) + return + end + + if type(fuel) ~= "number" then + warn(("[SetFuel] Invalid fuel value received: %s. Fuel must be a number between 0 and 100."):format(tostring(fuel))) + return + end + + -- Normalize the fuel values if received negative values or higher than 100 + fuel = math.max(0.0, math.min(fuel, 100.0)) + + SetVehicleFuelLevel(vehicle, fuel + 0.0) + DecorSetFloat(vehicle, fuelDecor, GetVehicleFuelLevel(vehicle)) +end +exports('SetFuel', SetFuel) + +-- Just another way to call the exports in case someone does it like this... +function getFuel(vehicle) + return GetFuel(vehicle) +end +exports('getFuel', getFuel) + +function setFuel(vehicle, fuel) + SetFuel(vehicle, fuel) +end +exports('setFuel', setFuel) + +-- Alias LegacyFuel's exports to point to our lc_fuel functions: +AddEventHandler('__cfx_export_LegacyFuel_SetFuel', function(setCB) + -- Redirect LegacyFuel:SetFuel to use lc_fuel's SetFuel function + setCB(SetFuel) +end) + +AddEventHandler('__cfx_export_LegacyFuel_GetFuel', function(setCB) + -- Redirect LegacyFuel:GetFuel to use lc_fuel's GetFuel function + setCB(GetFuel) +end) + +function SetFuelType(vehicle, fuelType) + if not DoesEntityExist(vehicle) then + warn(("[SetFuelType] Vehicle entity does not exist. Received: %s. This is usually caused by a misconfiguration in the export."):format(tostring(vehicle))) + return + end + if not fuelType or fuelType == "nil" or fuelType == "" then + fuelType = dealWithDefaultFuelType(vehicle, "default") + end + TriggerServerEvent("lc_fuel:setVehicleFuelType", GetVehicleNumberPlateText(vehicle), fuelType) +end +exports('SetFuelType', SetFuelType) + +function setFuelType(vehicle, fuelType) + return SetFuelType(vehicle, fuelType) +end +exports('setFuelType', setFuelType) + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Utils +----------------------------------------------------------------------------------------------------------------------------------------- + +function getVehicleFuelTypeFromServer(vehicle) + local returnFuelType = nil + + Utils.Callback.TriggerServerCallback('lc_fuel:getVehicleFuelType', function(fuelType) + returnFuelType = dealWithDefaultFuelType(vehicle, fuelType) + end, GetVehicleNumberPlateText(vehicle)) + + while returnFuelType == nil do + Wait(10) + end + return returnFuelType +end + +function dealWithDefaultFuelType(vehicle, fuelType) + -- Define if the vehicle is diesel or gasoline for the ones that have never been refueled + if fuelType == "default" then + if IsVehicleDiesel(vehicle) then + fuelType = "diesel" + elseif IsVehicleElectric(vehicle) then + fuelType = "electric" + else + fuelType = "regular" + end + end + return fuelType +end + +function changeVehicleFuelType(vehicle, fuelType) + local ped = PlayerPedId() + local playerCoords = GetEntityCoords(ped) + if vehicle and #(playerCoords - GetEntityCoords(vehicle)) < 5 then + SetFuel(vehicle, 0.0) + exports['lc_utils']:notify("info",Utils.translate("vehicle_tank_emptied")) + TriggerServerEvent("lc_fuel:setVehicleFuelType", GetVehicleNumberPlateText(vehicle), fuelType) + else + exports['lc_utils']:notify("error",Utils.translate("vehicle_not_found")) + end +end + +function getVehicleDisplayFuelAmount(currentFuel, tankSize) + local displayFuelAmount = currentFuel * tankSize / 100 + return displayFuelAmount +end + +function getVehicleTankSize(vehicle) + local vehicleHash = GetEntityModel(vehicle) + if Config.FuelTankSize.perVehicleHash[vehicleHash] then + return Config.FuelTankSize.perVehicleHash[vehicleHash] + end + return Config.FuelTankSize.perClass[GetVehicleClass(vehicle)] or 100 +end + +function GetPumpOffset(pump) + local heightOffset = { forward = 0.0, right = 0.0, up = 2.1 } + local pumpModel = GetEntityModel(pump) + + for _, v in pairs(Config.Electric.chargersProps) do + if pumpModel == joaat(v.prop) then + heightOffset = v.ropeOffset + end + end + + for _, v in pairs(Config.GasPumpProps) do + if pumpModel == joaat(v.prop) then + heightOffset = v.ropeOffset + end + end + + return heightOffset +end + +function CreateRopeToPump(pumpCoords) + local offset = GetPumpOffset(currentPump) + + RopeLoadTextures() + while not RopeAreTexturesLoaded() do + Wait(0) + end + + local forwardVector, rightVector, upVector, _ = GetEntityMatrix(currentPump) + + -- Adjust the offsets + local forwardOffset = forwardVector * offset.forward + local rightoffset = rightVector * offset.right + local upOffset = upVector * offset.up + local finalOffset = forwardOffset + rightoffset + upOffset + + local ropeObj = AddRope(pumpCoords.x + finalOffset.x, pumpCoords.y + finalOffset.y, pumpCoords.z + finalOffset.z, 0.0, 0.0, 0.0, 4.0, 1, 10.0, 0.0, 1.0, false, false, false, 1.0, true) + while not ropeObj do + Wait(0) + end + ActivatePhysics(ropeObj) + Wait(100) + + local nozzlePos = GetOffsetFromEntityInWorldCoords(fuelNozzle, 0.0, -0.033, -0.195) + ---@diagnostic disable-next-line: param-type-mismatch + AttachEntitiesToRope(ropeObj, currentPump, fuelNozzle, pumpCoords.x + finalOffset.x, pumpCoords.y + finalOffset.y, pumpCoords.z + finalOffset.z, nozzlePos.x, nozzlePos.y, nozzlePos.z, 30.0, false, false, nil, nil) + return ropeObj +end + +function GetVehicles() + return GetGamePool('CVehicle') +end + +function GetClosestVehicle(coords, modelFilter) + return GetClosestEntity(GetVehicles(), false, coords, modelFilter) +end + +function GetClosestEntity(entities, isPlayerEntities, coords, modelFilter) + local closestEntity, closestEntityDistance, filteredEntities = -1, -1, nil + + if coords then + coords = vector3(coords.x, coords.y, coords.z) + else + local playerPed = PlayerPedId() + coords = GetEntityCoords(playerPed) + end + + if modelFilter then + filteredEntities = {} + + for _, entity in pairs(entities) do + if modelFilter[GetEntityModel(entity)] then + filteredEntities[#filteredEntities + 1] = entity + end + end + end + + for k, entity in pairs(filteredEntities or entities) do + local distance = #(coords - GetEntityCoords(entity)) + + if closestEntityDistance == -1 or distance < closestEntityDistance then + closestEntity, closestEntityDistance = isPlayerEntities and k or entity, distance + end + end + + return closestEntity, closestEntityDistance +end + +function GetVehicleCapPos(vehicle) + local closestCapPos + local tanks = vehicleCapBoneList() + for _, v in pairs(tanks) do + local vehicleTank = GetEntityBoneIndexByName(vehicle, v) + if vehicleTank ~= -1 then + closestCapPos = GetWorldPositionOfEntityBone(vehicle, vehicleTank) + break + end + end + return closestCapPos +end + +function Round(num, numDecimalPlaces) + error("Do not use this") +end + +function IsVehicleBlacklisted(vehicle) + if vehicle and vehicle ~= 0 then + local vehicleHash = GetEntityModel(vehicle) + -- Blacklist electric vehicles if electric recharge is disabled + if not Config.Electric.enabled and IsVehicleElectric(vehicle) then + return true + end + + -- Check if the vehicle is in the blacklist + if Config.BlacklistedVehiclesHash[vehicleHash] then + return true + end + return false + end + return true +end + +function IsVehicleDiesel(vehicle) + if vehicle and vehicle ~= 0 then + local vehicleHash = GetEntityModel(vehicle) + -- Check if the vehicle is in the diesel list + if Config.DieselVehiclesHash[vehicleHash] then + return true + end + end + return false +end + +function IsVehicleElectric(vehicle) + if vehicle and vehicle ~= 0 then + local vehicleHash = GetEntityModel(vehicle) + -- Check if the vehicle is in the diesel list + if Config.Electric.vehiclesListHash[vehicleHash] then + return true + end + end + return false +end + +function GetClosestPump(coords, isElectric) + local pump = nil + local currentPumpModel = nil + local pumpList = isElectric and Config.Electric.chargersProps or Config.GasPumpProps + + for i = 1, #pumpList do + currentPumpModel = pumpList[i].prop + pump = GetClosestObjectOfType(coords.x, coords.y, coords.z, 2.5, joaat(currentPumpModel), true, true, true) + if pump ~= 0 then return pump, currentPumpModel end + end + + return nil, nil +end + +function createBlips() + if Config.Blips.onlyShowNearestBlip then + Citizen.CreateThread(function() + local currentBlip = 0 + local currentBlipIndex = 0 + + while true do + local coords = GetEntityCoords(PlayerPedId()) + local closestBlipDistance = math.maxinteger + local closestBlipCoords + local closestBlipId + + for blipId, blipCoord in pairs(Config.Blips.locations) do + local distanceToBlip = #(coords - blipCoord) + + if distanceToBlip < closestBlipDistance then + closestBlipDistance = distanceToBlip + closestBlipCoords = blipCoord + closestBlipId = blipId + end + end + + if currentBlipIndex ~= closestBlipId then + if DoesBlipExist(currentBlip) then + Utils.Blips.removeBlip(currentBlip) + end + + if closestBlipCoords then + currentBlip = Utils.Blips.createBlipForCoords(closestBlipCoords.x,closestBlipCoords.y,closestBlipCoords.z,Config.Blips.blipId,Config.Blips.color,Utils.translate('blip_text'),Config.Blips.scale,false) + end + + currentBlipIndex = closestBlipId + end + + Citizen.Wait(5000) + end + end) + else + local text = Utils.translate('blip_text') + for _, blipCoords in pairs(Config.Blips.locations) do + Utils.Blips.createBlipForCoords(blipCoords.x,blipCoords.y,blipCoords.z,Config.Blips.blipId,Config.Blips.color,text,Config.Blips.scale,false) + end + end +end + +function convertConfigVehiclesDisplayNameToHash() + Config.BlacklistedVehiclesHash = {} + for _, value in pairs(Config.BlacklistedVehicles) do + Config.BlacklistedVehiclesHash[joaat(value)] = true + end + Config.Electric.vehiclesListHash = {} + for _, value in pairs(Config.Electric.vehiclesList) do + Config.Electric.vehiclesListHash[joaat(value)] = true + end + Config.DieselVehiclesHash = {} + for _, value in pairs(Config.DieselVehicles) do + Config.DieselVehiclesHash[joaat(value)] = true + end + Config.FuelTankSize.perVehicleHash = {} + for key, value in pairs(Config.FuelTankSize.perVehicle) do + Config.FuelTankSize.perVehicleHash[joaat(key)] = value + end + Config.CustomVehicleParametersHash = {} + Utils.Table.deepMerge(Config.HiddenCustomVehicleParameters, Config.CustomVehicleParameters) + for key, value in pairs(Config.HiddenCustomVehicleParameters) do + Config.CustomVehicleParametersHash[joaat(key)] = value + end + Config.CustomVehicleParametersHash.default = Config.CustomVehicleParameters.default -- Adds back the default +end + +RegisterNetEvent('lc_fuel:Notify') +AddEventHandler('lc_fuel:Notify', function(type,message) + exports['lc_utils']:notify(type,message) +end) + +Citizen.CreateThread(function() + Wait(1000) + SetNuiFocus(false,false) + SetNuiFocusKeepInput(false) + FreezeEntityPosition(PlayerPedId(), false) + + Utils.loadLanguageFile(Lang) + + cachedTranslations = { + open_refuel = Utils.translate('markers.open_refuel'), + open_recharge = Utils.translate('markers.open_recharge'), + interact_with_vehicle = Utils.translate('markers.interact_with_vehicle'), + return_nozzle = Utils.translate('markers.return_nozzle'), + } + + convertConfigVehiclesDisplayNameToHash() + + if Config.Blips and Config.Blips.enabled then + createBlips() + end + + -- Gas + if Utils.Config.custom_scripts_compatibility.target == "disabled" then + createGasMarkersThread() + else + createGasTargetsThread() + end + createCustomPumpModelsThread() + + -- Electrics + if Config.Electric.enabled then + CreateThread(function() + createElectricZones() + + if Utils.Config.custom_scripts_compatibility.target == "disabled" then + createElectricMarkersThread() + else + createElectricTargetsThread() + end + end) + end + + -- Refuel + if Utils.Config.custom_scripts_compatibility.target ~= "disabled" then + createTargetForVehicleIteraction() + end + + -- Other threads + createFuelConsumptionThread() + if Config.JerryCan.enabled and Utils.Config.custom_scripts_compatibility.target == "disabled" then + createJerryCanThread() + end + + if Config.DebugNozzleOffset then + createDebugNozzleOffsetThread() + end +end) + +-- Debug nozzle offset functions +function GetVehiclePlayerIsLookingAt() + local playerPed = PlayerPedId() + local camCoords = GetGameplayCamCoord() + local camRot = GetGameplayCamRot(2) + local direction = RotationToDirection(camRot) + local maxDistance = 10.0 + + local destCoords = camCoords + direction * maxDistance + local rayHandle = StartShapeTestRay(camCoords.x, camCoords.y, camCoords.z, destCoords.x, destCoords.y, destCoords.z, 10, playerPed, 0) + local _, hit, endCoords, _, entityHit = GetShapeTestResult(rayHandle) + + if hit == 1 and IsEntityAVehicle(entityHit) then + return entityHit + end + return nil +end + +function GetLookAtHitCoords() + local playerPed = PlayerPedId() + local camCoords = GetGameplayCamCoord() + local camRot = GetGameplayCamRot(2) + local direction = RotationToDirection(camRot) + + local endCoords = vec3( + camCoords.x + direction.x * 10.0, + camCoords.y + direction.y * 10.0, + camCoords.z + direction.z * 10.0 + ) + + local rayHandle = StartShapeTestRay(camCoords.x, camCoords.y, camCoords.z, endCoords.x, endCoords.y, endCoords.z, -1, playerPed, 0) + local _, hit, hitCoords = GetShapeTestResult(rayHandle) + return hit, hitCoords +end + +function RotationToDirection(rot) + local z = math.rad(rot.z) + local x = math.rad(rot.x) + local cosX = math.cos(x) + + return vec3( + -math.sin(z) * cosX, + math.cos(z) * cosX, + math.sin(x) + ) +end + +-- Gets the offset from 'petrolcap' to the point you're looking at +function GetNozzleOffset(vehicle, hitCoords) + -- World position of the bone + local boneWorldPos = GetVehicleCapPos(vehicle) + + -- Directional vector from bone to hit point + local direction = { + x = hitCoords.x - boneWorldPos.x, + y = hitCoords.y - boneWorldPos.y, + z = hitCoords.z - boneWorldPos.z + } + + -- Convert direction vector to vehicle-local offset + local forwardVector, rightVector, upVector, _ = GetEntityMatrix(vehicle) + + local offset = { + right = direction.x * rightVector.x + direction.y * rightVector.y + direction.z * rightVector.z, + forward = direction.x * forwardVector.x + direction.y * forwardVector.y + direction.z * forwardVector.z, + up = direction.x * upVector.x + direction.y * upVector.y + direction.z * upVector.z + } + + return offset +end + +if Config.EnableHUD then + local function DrawAdvancedText(x,y ,w,h,sc, text, r,g,b,a,font,jus) + SetTextFont(font) + SetTextProportional(false) + SetTextScale(sc, sc) + SetTextJustification(jus) + SetTextColour(r, g, b, a) + SetTextDropShadow() + SetTextEdge(1, 0, 0, 0, 255) + SetTextDropShadow() + SetTextOutline() + SetTextEntry("STRING") + AddTextComponentString(text) + DrawText(x - 0.1+w, y - 0.02+h) + end + + local mph = "0" + local kmh = "0" + local fuel = "0" + local displayHud = false + + local x = 0.01135 + local y = 0.002 + + Citizen.CreateThread(function() + while true do + local ped = PlayerPedId() + + if IsPedInAnyVehicle(ped, false) then + local vehicle = GetVehiclePedIsIn(ped, false) + local speed = GetEntitySpeed(vehicle) + + mph = tostring(math.ceil(speed * 2.236936)) + kmh = tostring(math.ceil(speed * 3.6)) + fuel = tostring(Utils.Math.round(GetVehicleFuelLevel(vehicle),2)) + + displayHud = true + else + displayHud = false + + Citizen.Wait(500) + end + + Citizen.Wait(50) + end + end) + + Citizen.CreateThread(function() + while true do + if displayHud then + DrawAdvancedText(0.130 - x, 0.77 - y, 0.005, 0.0028, 0.6, mph, 255, 255, 255, 255, 6, 1) + DrawAdvancedText(0.174 - x, 0.77 - y, 0.005, 0.0028, 0.6, kmh, 255, 255, 255, 255, 6, 1) + DrawAdvancedText(0.2155 - x, 0.77 - y, 0.005, 0.0028, 0.6, fuel, 255, 255, 255, 255, 6, 1) + DrawAdvancedText(0.2615 - x, 0.77 - y, 0.005, 0.0028, 0.6, tostring(currentConsumption), 255, 255, 255, 255, 6, 1) + DrawAdvancedText(0.145 - x, 0.7765 - y, 0.005, 0.0028, 0.4, "mp/h km/h Fuel Consumption", 255, 255, 255, 255, 6, 1) + else + Citizen.Wait(50) + end + + Citizen.Wait(0) + end + end) +end + +AddEventHandler('onResourceStop', function(resourceName) + if GetCurrentResourceName() ~= resourceName then return end + + deleteRopeAndNozzleProp() +end) + +function deleteRopeAndNozzleProp() + if DoesRopeExist(fuelRope) then + RopeUnloadTextures() + DeleteRope(fuelRope) + end + if DoesEntityExist(fuelNozzle) then + DeleteEntity(fuelNozzle) + end +end + +function vehicleCapBoneList() + return { "petrolcap", "petroltank", "petroltank_l", "petroltank_r", "wheel_lr", "wheel_lf", "engine", "chassis_dummy" } +end + +-- Do not change this, use the Config.CustomVehicleParameters in config.lua +Config.HiddenCustomVehicleParameters = { + -- Cars + ["asbo"] = { distance = 1.2, nozzleOffset = { forward = 0.0, right = -0.21, up = 0.50} }, + ["blista"] = { distance = 1.2, nozzleOffset = { forward = 0.0, right = -0.21, up = 0.50} }, + ["brioso"] = { distance = 1.2, nozzleOffset = { forward = 0.0, right = -0.10, up = 0.60} }, + ["club"] = { distance = 1.2, nozzleOffset = { forward = -0.2, right = -0.13, up = 0.50} }, + ["kanjo"] = { distance = 1.2, nozzleOffset = { forward = -0.2, right = -0.17, up = 0.50} }, + ["issi2"] = { distance = 1.2, nozzleOffset = { forward = -0.2, right = -0.15, up = 0.50} }, + ["issi3"] = { distance = 1.2, nozzleOffset = { forward = -0.27, right = -0.13, up = 0.54} }, + ["issi4"] = { distance = 1.2, nozzleOffset = { forward = -0.27, right = -0.13, up = 0.70} }, + ["issi5"] = { distance = 1.2, nozzleOffset = { forward = -0.27, right = -0.13, up = 0.70} }, + ["issi6"] = { distance = 1.2, nozzleOffset = { forward = -0.27, right = -0.13, up = 0.70} }, + ["panto"] = { distance = 1.2, nozzleOffset = { forward = -0.10, right = -0.15, up = 0.65} }, + ["prairie"] = { distance = 1.2, nozzleOffset = { forward = -0.20, right = -0.20, up = 0.45} }, + ["rhapsody"] = { distance = 1.2, nozzleOffset = { forward = -0.20, right = -0.20, up = 0.45} }, + ["brioso2"] = { distance = 1.2, nozzleOffset = { forward = -0.25, right = -0.13, up = 0.40} }, + ["weevil"] = { distance = 1.2, nozzleOffset = { forward = -0.02, right = -0.03, up = 0.63} }, + ["issi7"] = { distance = 1.2, nozzleOffset = { forward = -0.03, right = -0.12, up = 0.57} }, + ["blista2"] = { distance = 1.2, nozzleOffset = { forward = -0.25, right = -0.23, up = 0.50} }, + ["blista3"] = { distance = 1.2, nozzleOffset = { forward = -0.25, right = -0.23, up = 0.50} }, + ["brioso3"] = { distance = 1.2, nozzleOffset = { forward = -0.25, right = -0.06, up = 0.40} }, + ["boor"] = { distance = 1.2, nozzleOffset = { forward = 0.0, right = -0.18, up = 0.50} }, + ["asea"] = { distance = 1.2, nozzleOffset = { forward = -0.28, right = -0.21, up = 0.50} }, + ["asterope"] = { distance = 1.2, nozzleOffset = { forward = -0.28, right = -0.16, up = 0.50} }, + ["cog55"] = { distance = 1.2, nozzleOffset = { forward = -0.44, right = -0.21, up = 0.45} }, + ["cognoscenti"] = { distance = 1.2, nozzleOffset = { forward = -0.44, right = -0.21, up = 0.45} }, + ["emperor"] = { distance = 1.2, nozzleOffset = { forward = -0.44, right = -0.22, up = 0.40} }, + ["fugitive"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.15, up = 0.40} }, + ["glendale"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.22, up = 0.40} }, + ["glendale2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.22, up = 0.30} }, + ["ingot"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.23, up = 0.45} }, + ["intruder"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.23, up = 0.40} }, + ["premier"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.16, up = 0.52} }, + ["primo"] = { distance = 1.2, nozzleOffset = { forward = -0.52, right = -0.18, up = 0.40} }, + ["primo2"] = { distance = 1.2, nozzleOffset = { forward = -0.52, right = -0.20, up = 0.35} }, + ["regina"] = { distance = 1.2, nozzleOffset = { forward = -0.52, right = -0.24, up = 0.40} }, + ["stafford"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.17, up = 0.50} }, + ["stanier"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.21, up = 0.40} }, + ["stratum"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.35} }, + ["stretch"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.21, up = 0.35} }, + ["superd"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.40} }, + ["tailgater"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.45} }, + ["warrener"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.45} }, + ["washington"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.22, up = 0.45} }, + ["tailgater2"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.14, up = 0.45} }, + ["cinquemila"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.21, up = 0.55} }, + ["astron"] = { distance = 1.2, nozzleOffset = { forward = -0.20, right = -0.22, up = 0.55} }, + ["baller7"] = { distance = 1.2, nozzleOffset = { forward = -0.62, right = -0.16, up = 0.60} }, + ["comet7"] = { distance = 1.2, nozzleOffset = { forward = -0.37, right = -0.19, up = 0.45} }, + ["deity"] = { distance = 1.2, nozzleOffset = { forward = -0.37, right = -0.21, up = 0.50} }, + ["jubilee"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.16, up = 0.60} }, + ["oracle"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.40} }, + ["schafter2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.45} }, + ["warrener2"] = { distance = 1.2, nozzleOffset = { forward = -0.02, right = -0.20, up = 0.40} }, + ["rhinehart"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.15, up = 0.50} }, + ["eudora"] = { distance = 1.2, nozzleOffset = { forward = 0.29, right = -0.38, up = 0.22} }, + + ["rebla"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.19, up = 0.60} }, + ["baller"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.23, up = 0.60} }, + ["baller2"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.60} }, + ["baller3"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.60} }, + ["baller4"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.60} }, + ["baller5"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.60} }, + ["baller6"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.60} }, + ["bjxl"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.21, up = 0.60} }, + ["cavalcade"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.21, up = 0.65} }, + ["cavalcade2"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.21, up = 0.65} }, + ["contender"] = { distance = 1.2, nozzleOffset = { forward = 0.75, right = -0.17, up = 0.50} }, + ["dubsta"] = { distance = 1.2, nozzleOffset = { forward = 0.25, right = -0.17, up = 0.70} }, + ["dubsta2"] = { distance = 1.2, nozzleOffset = { forward = 0.25, right = -0.17, up = 0.70} }, + ["fq2"] = { distance = 1.2, nozzleOffset = { forward = -0.32, right = -0.23, up = 0.53} }, + ["granger"] = { distance = 1.2, nozzleOffset = { forward = 0.65, right = -0.27, up = 0.60} }, + ["granger2"] = { distance = 1.2, nozzleOffset = { forward = 0.45, right = -0.26, up = 0.60} }, + ["gresley"] = { distance = 1.2, nozzleOffset = { forward = 0.05, right = -0.17, up = 0.66} }, + ["habanero"] = { distance = 1.2, nozzleOffset = { forward = -0.47, right = -0.17, up = 0.50} }, + ["huntley"] = { distance = 1.2, nozzleOffset = { forward = 0.07, right = -0.24, up = 0.65} }, + ["landstalker"] = { distance = 1.2, nozzleOffset = { forward = 0.40, right = -0.23, up = 0.60} }, + ["landstalker2"] = { distance = 1.2, nozzleOffset = { forward = 0.25, right = -0.24, up = 0.60} }, + ["novak"] = { distance = 1.2, nozzleOffset = { forward = -0.25, right = -0.21, up = 0.60} }, + ["patriot"] = { distance = 1.2, nozzleOffset = { forward = 0.2, right = -0.22, up = 0.75} }, + ["patriot2"] = { distance = 1.2, nozzleOffset = { forward = 0.2, right = -0.22, up = 0.75} }, + ["patriot3"] = { distance = 1.2, nozzleOffset = { forward = 0.50, right = -0.29, up = 0.65} }, + ["radi"] = { distance = 1.2, nozzleOffset = { forward = -0.30, right = -0.17, up = 0.60} }, + ["rocoto"] = { distance = 1.2, nozzleOffset = { forward = -0.30, right = -0.20, up = 0.60} }, + ["seminole"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.20, up = 0.65} }, + ["seminole2"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.20, up = 0.55} }, + ["serrano"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.19, up = 0.60} }, + ["toros"] = { distance = 1.2, nozzleOffset = { forward = -0.26, right = -0.26, up = 0.68} }, + ["xls"] = { distance = 1.2, nozzleOffset = { forward = -0.0, right = -0.20, up = 0.65} }, + + ["cogcabrio"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.50} }, + ["exemplar"] = { distance = 1.2, nozzleOffset = { forward = -0.27, right = -0.19, up = 0.45} }, + ["f620"] = { distance = 1.2, nozzleOffset = { forward = -0.29, right = -0.25, up = 0.40} }, + ["felon"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.18, up = 0.40} }, + ["felon2"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.18, up = 0.40} }, + ["jackal"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.11, up = 0.50} }, + ["oracle2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.15, up = 0.50} }, + ["sentinel"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.11, up = 0.50} }, + ["sentinel2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.11, up = 0.50} }, + ["windsor"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.15, up = 0.50} }, + ["windsor2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.15, up = 0.50} }, + ["zion"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.17, up = 0.50} }, + ["zion2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.17, up = 0.50} }, + ["previon"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.21, up = 0.50} }, + ["champion"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.11, up = 0.40} }, + ["futo"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.15, up = 0.40} }, + ["sentinel3"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.22, up = 0.30} }, + ["kanjosj"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.45} }, + ["postlude"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.15, up = 0.45} }, + ["tahoma"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.35} }, + ["broadway"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.35} }, + + ["blade"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.31, up = 0.40} }, + ["buccaneer"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.28, up = 0.40} }, + ["chino"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.28, up = 0.35} }, + ["chino2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.25} }, + ["clique"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.21, up = 0.25} }, + ["coquette3"] = { distance = 1.2, nozzleOffset = { forward = 0.43, right = -0.31, up = 0.25} }, + ["deviant"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, + ["dominator"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, + ["dominator2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, + ["dominator3"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.40} }, + ["dominator4"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.21, up = 0.40} }, + ["dominator7"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.40} }, + ["dominator8"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.40} }, + ["dukes"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.31, up = 0.40} }, + ["dukes2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.31, up = 0.40} }, + ["dukes3"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.25, up = 0.40} }, + ["faction"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.17, up = 0.40} }, + ["faction2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.16, up = 0.30} }, + ["faction3"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.16, up = 0.70} }, + ["ellie"] = { distance = 1.2, nozzleOffset = { forward = -0.30, right = -0.05, up = 0.67} }, + ["gauntlet"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.40} }, + ["gauntlet2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.40} }, + ["gauntlet3"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.25, up = 0.50} }, + ["gauntlet4"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.18, up = 0.45} }, + ["gauntlet5"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.25, up = 0.50} }, + ["hermes"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.31, up = 0.20} }, + ["hotknife"] = { distance = 1.2, nozzleOffset = { forward = 0.40, right = -0.00, up = 0.30} }, + ["hustler"] = { distance = 1.2, nozzleOffset = { forward = -0.62, right = 0.05, up = 0.20} }, + ["impaler"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.27, up = 0.35} }, + ["impaler2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.24, up = 0.35} }, + ["impaler3"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.22, up = 0.45} }, + ["impaler4"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.35} }, + ["imperator"] = { distance = 1.2, nozzleOffset = { forward = -0.05, right = -0.15, up = 0.65} }, + ["imperator2"] = { distance = 1.2, nozzleOffset = { forward = -0.05, right = -0.15, up = 0.65} }, + ["imperator3"] = { distance = 1.2, nozzleOffset = { forward = -0.05, right = -0.15, up = 0.65} }, + ["lurcher"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.30, up = 0.35} }, + ["nightshade"] = { distance = 1.2, nozzleOffset = { forward = -0.60, right = -0.07, up = 0.35} }, + ["phoenix"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.23, up = 0.35} }, + ["picador"] = { distance = 1.2, nozzleOffset = { forward = 0.75, right = -0.23, up = 0.45} }, + ["ratloader2"] = { distance = 1.2, nozzleOffset = { forward = 1.05, right = -0.07, up = 0.35} }, + ["ruiner"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.35} }, + ["ruiner2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.35} }, + ["sabregt"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.20, up = 0.35} }, + ["sabregt2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.20, up = 0.30} }, + ["slamvan"] = { distance = 1.2, nozzleOffset = { forward = 0.90, right = 0.03, up = 0.25} }, + ["slamvan2"] = { distance = 1.2, nozzleOffset = { forward = 0.90, right = -0.18, up = 0.30} }, + ["slamvan3"] = { distance = 1.2, nozzleOffset = { forward = 0.85, right = -0.03, up = 0.10} }, + ["stalion"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.35} }, + ["stalion2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.35} }, + ["tampa"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.20, up = 0.35} }, + ["tulip"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.23, up = 0.35} }, + ["vamos"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.25, up = 0.39} }, + ["vigero"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.22, up = 0.39} }, + ["virgo"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.22, up = 0.39} }, + ["virgo2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.28, up = 0.30} }, + ["virgo3"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.25, up = 0.30} }, + ["voodoo"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.29, up = 0.42} }, + ["yosemite"] = { distance = 1.2, nozzleOffset = { forward = 1.20, right = -0.29, up = 0.25} }, + ["yosemite2"] = { distance = 1.2, nozzleOffset = { forward = 1.22, right = -0.13, up = 0.35} }, + ["buffalo4"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.22, up = 0.50} }, + ["manana"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.25, up = 0.30} }, + ["manana2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.25, up = 0.30} }, + ["tampa2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.10, up = 0.30} }, + ["ruiner4"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.19, up = 0.35} }, + ["vigero2"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.50} }, + ["weevil2"] = { distance = 1.2, nozzleOffset = { forward = 1.90, right = 0.15, up = 0.25} }, + ["buffalo5"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.50} }, + ["tulip2"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.24, up = 0.35} }, + ["clique2"] = { distance = 1.2, nozzleOffset = { forward = 0.05, right = -0.26, up = 0.60} }, + ["brigham"] = { distance = 1.2, nozzleOffset = { forward = 0.15, right = -0.30, up = 0.40} }, + ["greenwood"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.21, up = 0.50} }, + + ["ardent"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.19, up = 0.35} }, + ["btype"] = { distance = 1.2, nozzleOffset = { forward = 0.25, right = -0.05, up = 0.78} }, + ["btype2"] = { distance = 1.2, nozzleOffset = { forward = 0.36, right = 0.07, up = 0.55} }, + ["btype3"] = { distance = 1.2, nozzleOffset = { forward = 0.25, right = -0.05, up = 0.78} }, + ["casco"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.21, up = 0.30} }, + ["deluxo"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.13, up = 0.40} }, + ["dynasty"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.21, up = 0.40} }, + ["fagaloa"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.21, up = 0.35} }, + ["feltzer3"] = { distance = 1.2, nozzleOffset = { forward = -0.31, right = -0.13, up = 0.55} }, + ["gt500"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.19, up = 0.25} }, + ["infernus2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.27, up = 0.35} }, + ["jb700"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.21, up = 0.35} }, + ["jb7002"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.21, up = 0.35} }, + ["mamba"] = { distance = 1.2, nozzleOffset = { forward = -0.30, right = -0.13, up = 0.50} }, + ["michelli"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.18, up = 0.30} }, + ["monroe"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.21, up = 0.30} }, + ["nebula"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.30} }, + ["peyote"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.26, up = 0.30} }, + ["peyote3"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.26, up = 0.30} }, + ["pigalle"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.30} }, + ["rapidgt3"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.30} }, + ["retinue"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.30} }, + ["retinue2"] = { distance = 1.2, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.30} }, + ["savestra"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.15, up = 0.40} }, + ["stinger"] = { distance = 1.2, nozzleOffset = { forward = -0.02, right = -0.13, up = 0.65} }, + ["stingergt"] = { distance = 1.2, nozzleOffset = { forward = -0.55, right = -0.20, up = 0.30} }, + ["stromberg"] = { distance = 1.2, nozzleOffset = { forward = -0.35, right = -0.23, up = 0.35} }, + ["swinger"] = { distance = 1.2, nozzleOffset = { forward = 0.45, right = -0.28, up = 0.25} }, + ["torero"] = { distance = 1.2, nozzleOffset = { forward = 0.75, right = -0.21, up = 0.35} }, + ["tornado"] = { distance = 1.2, nozzleOffset = { forward = 0.45, right = -0.28, up = 0.25} }, + ["tornado2"] = { distance = 1.2, nozzleOffset = { forward = 0.45, right = -0.28, up = 0.25} }, + ["tornado5"] = { distance = 1.2, nozzleOffset = { forward = 0.45, right = -0.28, up = 0.25} }, + ["turismo2"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.23, up = 0.40} }, + ["viseris"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.18, up = 0.40} }, + ["z190"] = { distance = 1.2, nozzleOffset = { forward = -0.68, right = -0.10, up = 0.47} }, + ["ztype"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.23, up = 0.30} }, + ["zion3"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.23, up = 0.30} }, + ["cheburek"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.20, up = 0.30} }, + ["toreador"] = { distance = 1.2, nozzleOffset = { forward = -0.40, right = -0.22, up = 0.35} }, + ["peyote2"] = { distance = 1.2, nozzleOffset = { forward = -0.50, right = -0.28, up = 0.30} }, + ["coquette2"] = { distance = 1.2, nozzleOffset = { forward = 0.43, right = -0.24, up = 0.25} }, + + ["alpha"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.21, up = 0.40} }, + ["banshee"] = { distance = 1.3, nozzleOffset = { forward = -0.55, right = -0.09, up = 0.40} }, + ["bestiagts"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.25, up = 0.45} }, + ["buffalo"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.35} }, + ["buffalo2"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.35} }, + ["carbonizzare"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.27, up = 0.50} }, + ["comet2"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.35} }, + ["comet3"] = { distance = 1.3, nozzleOffset = { forward = -0.52, right = -0.07, up = 0.20} }, + ["comet4"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.20, up = 0.35} }, + ["comet5"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.35} }, + ["coquette"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.28, up = 0.25} }, + ["coquette4"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.28, up = 0.25} }, + ["drafter"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.18, up = 0.45} }, + ["elegy"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.28, up = 0.30} }, + ["elegy2"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.18, up = 0.50} }, + ["feltzer2"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.15, up = 0.45} }, + ["flashgt"] = { distance = 1.3, nozzleOffset = { forward = -0.31, right = -0.26, up = 0.50} }, + ["furoregt"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.50} }, + ["gb200"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.20, up = 0.40} }, + ["komoda"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, + ["italigto"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.27, up = 0.40} }, + ["jugular"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.16, up = 0.55} }, + ["jester"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.23, up = 0.35} }, + ["jester2"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.23, up = 0.35} }, + ["jester3"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.16, up = 0.40} }, + ["kuruma"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.19, up = 0.40} }, + ["kuruma2"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.19, up = 0.40} }, + ["locust"] = { distance = 1.3, nozzleOffset = { forward = 0.44, right = 0.00, up = 0.61} }, + ["lynx"] = { distance = 1.3, nozzleOffset = { forward = 0.43, right = -0.24, up = 0.40} }, + ["massacro"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.21, up = 0.42} }, + ["massacro2"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.21, up = 0.42} }, + ["neo"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.23, up = 0.42} }, + ["ninef"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.24, up = 0.35} }, + ["ninef2"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.24, up = 0.35} }, + ["omnis"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.27, up = 0.31} }, + ["paragon"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.25, up = 0.40} }, + ["pariah"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.19, up = 0.45} }, + ["penumbra"] = { distance = 1.3, nozzleOffset = { forward = -0.25, right = -0.19, up = 0.50} }, + ["penumbra2"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.40} }, + ["rapidgt"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.17, up = 0.40} }, + ["rapidgt2"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.17, up = 0.40} }, + ["raptor"] = { distance = 1.3, nozzleOffset = { forward = -0.20, right = -0.40, up = 0.25} }, + ["revolter"] = { distance = 1.3, nozzleOffset = { forward = -0.55, right = -0.17, up = 0.45} }, + ["ruston"] = { distance = 1.3, nozzleOffset = { forward = 0.53, right = -0.00, up = 0.53} }, + ["schafter3"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.40} }, + ["schafter4"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.40} }, + ["schlagen"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.40} }, + ["seven70"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.23, up = 0.30} }, + ["specter"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.23, up = 0.30} }, + ["streiter"] = { distance = 1.3, nozzleOffset = { forward = -0.55, right = -0.08, up = 0.50} }, + ["sugoi"] = { distance = 1.3, nozzleOffset = { forward = -0.25, right = -0.15, up = 0.50} }, + ["sultan"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, + ["sultan2"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, + ["surano"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.26, up = 0.40} }, + ["tropos"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.08, up = 0.40} }, + ["verlierer2"] = { distance = 1.3, nozzleOffset = { forward = 1.60, right = -0.19, up = 0.30} }, + ["vstr"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.17, up = 0.55} }, + ["zr350"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.21, up = 0.35} }, + ["calico"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.16, up = 0.45} }, + ["futo2"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.19, up = 0.35} }, + ["euros"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.15, up = 0.55} }, + ["remus"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.16, up = 0.45} }, + ["comet6"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.35} }, + ["growler"] = { distance = 1.3, nozzleOffset = { forward = -0.20, right = -0.23, up = 0.45} }, + ["vectre"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.17, up = 0.45} }, + ["cypher"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.21, up = 0.45} }, + ["sultan3"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.40} }, + ["rt3000"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.17, up = 0.45} }, + ["sultanrs"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.13, up = 0.40} }, + ["visione"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.10, up = 0.40} }, + ["cheetah2"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.20, up = 0.35} }, + ["stingertt"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.25, up = 0.35} }, + ["sentinel4"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.35} }, + ["sm722"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.35} }, + ["tenf"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.23, up = 0.43} }, + ["tenf2"] = { distance = 1.3, nozzleOffset = { forward = 0.43, right = -0.15, up = 0.43} }, + ["everon2"] = { distance = 1.3, nozzleOffset = { forward = -1.03, right = -0.24, up = 0.50} }, + ["issi8"] = { distance = 1.3, nozzleOffset = { forward = -0.20, right = -0.21, up = 0.55} }, + ["corsita"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.24, up = 0.30} }, + ["gauntlet6"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.21, up = 0.31} }, + ["coureur"] = { distance = 1.3, nozzleOffset = { forward = -0.10, right = -0.22, up = 0.45} }, + ["r300"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.26, up = 0.40} }, + ["panthere"] = { distance = 1.3, nozzleOffset = { forward = -0.30, right = -0.14, up = 0.40} }, + + ["adder"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.19, up = 0.50} }, + ["autarch"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.23, up = 0.30} }, + ["banshee2"] = { distance = 1.3, nozzleOffset = { forward = -0.55, right = -0.09, up = 0.40} }, + ["bullet"] = { distance = 1.3, nozzleOffset = { forward = -0.00, right = -0.30, up = 0.05} }, + ["cheetah"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.31, up = 0.35} }, + ["entity2"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.27, up = 0.35} }, + ["entityxf"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.31, up = 0.35} }, + ["emerus"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.28, up = 0.35} }, + ["fmj"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.23, up = 0.30} }, + ["furia"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.26, up = 0.35} }, + ["gp1"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.27, up = 0.30} }, + ["infernus"] = { distance = 1.3, nozzleOffset = { forward = 0.91, right = -0.14, up = 0.65} }, + ["italigtb"] = { distance = 1.3, nozzleOffset = { forward = 0.25, right = -0.20, up = 0.35} }, + ["italigtb2"] = { distance = 1.3, nozzleOffset = { forward = 0.25, right = -0.20, up = 0.40} }, + ["krieger"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.21, up = 0.25} }, + ["le7b"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.35, up = 0.25} }, + ["nero"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.29, up = 0.25} }, + ["nero2"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.29, up = 0.25} }, + ["osiris"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.27, up = 0.25} }, + ["penetrator"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.25, up = 0.25} }, + ["pfister811"] = { distance = 1.3, nozzleOffset = { forward = 0.75, right = -0.28, up = 0.35} }, + ["prototipo"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.27, up = 0.35} }, + ["reaper"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.15, up = 0.35} }, + ["s80"] = { distance = 1.3, nozzleOffset = { forward = 0.40, right = -0.31, up = 0.30} }, + ["sc1"] = { distance = 1.3, nozzleOffset = { forward = 0.40, right = -0.25, up = 0.30} }, + ["sheava"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.17, up = 0.35} }, + ["t20"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.27, up = 0.30} }, + ["taipan"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.25, up = 0.30} }, + ["tempesta"] = { distance = 1.3, nozzleOffset = { forward = 0.25, right = -0.10, up = 0.60} }, + ["thrax"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.22, up = 0.30} }, + ["tigon"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.27, up = 0.30} }, + ["turismor"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.28, up = 0.30} }, + ["tyrant"] = { distance = 1.3, nozzleOffset = { forward = 0.30, right = -0.29, up = 0.50} }, + ["tyrus"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.26, up = 0.30} }, + ["vacca"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.32, up = 0.35} }, + ["vagner"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.29, up = 0.35} }, + ["xa21"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.27, up = 0.35} }, + ["zentorno"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.29, up = 0.35} }, + ["zorrusso"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.27, up = 0.35} }, + ["ignus"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.32, up = 0.35} }, + ["zeno"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.24, up = 0.35} }, + ["deveste"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.21, up = 0.25} }, + ["lm87"] = { distance = 1.3, nozzleOffset = { forward = 0.40, right = -0.34, up = 0.25} }, + ["torero2"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.28, up = 0.35} }, + ["entity3"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.27, up = 0.25} }, + ["virtue"] = { distance = 1.3, nozzleOffset = { forward = 0.45, right = -0.19, up = 0.35} }, + + ["bfinjection"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = 0.02, up = 0.35} }, + ["bifta"] = { distance = 1.3, nozzleOffset = { forward = 0.03, right = -0.65, up = 0.10} }, + + -- offroad + ["blazer"] = { distance = 1.3, nozzleOffset = { forward = -0.08, right = -0.29, up = 0.20} }, + ["blazer2"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.25, up = 0.25} }, + ["blazer3"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.25, up = 0.15} }, + ["blazer4"] = { distance = 1.3, nozzleOffset = { forward = -0.00, right = -0.22, up = 0.12} }, + ["blazer5"] = { distance = 1.3, nozzleOffset = { forward = -0.10, right = -0.55, up = 0.25} }, + ["brawler"] = { distance = 1.3, nozzleOffset = { forward = -0.16, right = -0.13, up = 0.90} }, + ["caracara"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.16, up = 0.80} }, + ["caracara2"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.16, up = 0.80} }, + ["dubsta3"] = { distance = 1.3, nozzleOffset = { forward = -0.75, right = -0.97, up = 0.40} }, + ["dune"] = { distance = 1.3, nozzleOffset = { forward = 0.05, right = -0.65, up = 0.05} }, + ["everon"] = { distance = 1.3, nozzleOffset = { forward = -0.80, right = -0.04, up = 0.80} }, + ["freecrawler"] = { distance = 1.3, nozzleOffset = { forward = -0.10, right = -0.20, up = 1.00} }, + ["hellion"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.17, up = 0.40} }, + ["kalahari"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.17, up = 0.40} }, + ["kamacho"] = { distance = 1.3, nozzleOffset = { forward = 1.05, right = -0.02, up = 0.60} }, + ["mesa3"] = { distance = 1.3, nozzleOffset = { forward = 0.25, right = 0.02, up = 0.85} }, + ["outlaw"] = { distance = 1.3, nozzleOffset = { forward = 0.60, right = -0.13, up = 0.70} }, + ["rancherxl"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.23, up = 0.50} }, + ["rebel2"] = { distance = 1.3, nozzleOffset = { forward = 1.10, right = -0.09, up = 0.55} }, + ["riata"] = { distance = 1.3, nozzleOffset = { forward = 0.90, right = -0.02, up = 0.80} }, + ["sandking"] = { distance = 1.3, nozzleOffset = { forward = 0.90, right = -0.17, up = 0.70} }, + ["sandking2"] = { distance = 1.3, nozzleOffset = { forward = 0.90, right = -0.17, up = 0.70} }, + ["trophytruck"] = { distance = 1.3, nozzleOffset = { forward = 0.90, right = -0.10, up = 0.70} }, + ["trophytruck2"] = { distance = 1.3, nozzleOffset = { forward = 0.90, right = -0.10, up = 0.70} }, + ["vagrant"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = 0.15, up = 0.25} }, + ["verus"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.30, up = 0.08} }, + ["winky"] = { distance = 1.3, nozzleOffset = { forward = -1.00, right = -0.19, up = 0.50} }, + ["yosemite3"] = { distance = 1.3, nozzleOffset = { forward = 0.83, right = -0.19, up = 0.50} }, + ["mesa"] = { distance = 1.3, nozzleOffset = { forward = 0.30, right = -0.11, up = 0.66} }, + ["ratel"] = { distance = 1.3, nozzleOffset = { forward = 0.61, right = 0.16, up = 1.11} }, + ["l35"] = { distance = 1.3, nozzleOffset = { forward = 0.80, right = -0.17, up = 0.55} }, + ["monstrociti"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.23, up = 0.45} }, + ["draugur"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.16, up = 0.80} }, + + -- truck + ["guardian"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.16, up = 0.40} }, + ["mixer2"] = { distance = 1.3, nozzleOffset = { forward = 0.0, right = 0.11, up = -0.06} , nozzleRotation = { x = 0, y = 0, z = 180} }, + ["tiptruck2"] = { distance = 3.5, nozzleOffset = { forward = 2.00, right = -2.25, up = -0.24} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["tiptruck"] = { distance = 1.3, nozzleOffset = { forward = 0.01, right = -0.19, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["rubble"] = { distance = 1.3, nozzleOffset = { forward = 0.01, right = -0.19, up = 0.04} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["mixer"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.23, up = 0.04} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["flatbed"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.23, up = 0.04} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["dump"] = { distance = 1.3, nozzleOffset = { forward = 0.27, right = -0.57, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["bulldozer"] = { distance = 1.3, nozzleOffset = { forward = 0.70, right = -0.25, up = 0.80} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["handler"] = { distance = 1.3, nozzleOffset = { forward = 0.88, right = -0.52, up = 0.88} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["cutter"] = { distance = 1.3, nozzleOffset = { forward = 0.95, right = -0.42, up = 0.30} , nozzleRotation = { x = 0, y = 0, z = 0} }, + + -- utillity + ["slamtruck"] = { distance = 1.3, nozzleOffset = { forward = 0.70, right = -0.28, up = 0.26} }, + ["utillitruck"] = { distance = 2.0, nozzleOffset = { forward = -0.80, right = -1.25, up = 0.50} }, + ["utillitruck2"] = { distance = 2.0, nozzleOffset = { forward = -0.80, right = -1.25, up = 0.50} }, + ["utillitruck3"] = { distance = 2.0, nozzleOffset = { forward = -0.40, right = -0.30, up = 0.50} }, + ["tractor"] = { distance = 2.0, nozzleOffset = { forward = 1.50, right = 0.27, up = 0.30} }, + ["tractor2"] = { distance = 2.0, nozzleOffset = { forward = 1.60, right = 0.05, up = 0.20} }, + ["tractor3"] = { distance = 2.0, nozzleOffset = { forward = 1.60, right = 0.05, up = 0.20} }, + ["towtruck"] = { distance = 2.0, nozzleOffset = { forward = -0.45, right = -0.30, up = 0.10} }, + ["towtruck2"] = { distance = 2.0, nozzleOffset = { forward = 0.85, right = 0.05, up = 0.50} }, + ["scrap"] = { distance = 2.0, nozzleOffset = { forward = -0.52, right = -0.05, up = -0.05} }, + ["sadler"] = { distance = 1.3, nozzleOffset = { forward = 1.14, right = -0.22, up = 0.70} }, + ["ripley"] = { distance = 2.0, nozzleOffset = { forward = -0.95, right = -0.48, up = 0.40} }, + ["mower"] = { distance = 2.0, nozzleOffset = { forward = 1.00, right = 0.10, up = 0.63} }, + ["forklift"] = { distance = 1.3, nozzleOffset = { forward = 0.05, right = -0.27, up = -0.40} }, + ["docktug"] = { distance = 2.5, nozzleOffset = { forward = 0.0, right = -0.25, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, + + -- van + ["bison"] = { distance = 1.3, nozzleOffset = { forward = 0.70, right = -0.28, up = 0.26} }, + ["bobcatxl"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.28, up = 0.35} }, + ["burrito3"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.25, up = 0.35} }, + ["gburrito2"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.22, up = 0.35} }, + ["rumpo"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.25, up = 0.35} }, + ["journey"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.34, up = 0.45} }, + ["minivan"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.20, up = 0.45} }, + ["minivan2"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.20, up = 0.45} }, + ["paradise"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.23, up = 0.45} }, + ["rumpo3"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.23, up = 0.45} }, + ["speedo"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.24, up = 0.45} }, + ["speedo4"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.24, up = 0.45} }, + ["surfer"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.29, up = 0.45} }, + ["youga3"] = { distance = 1.3, nozzleOffset = { forward = 0.55, right = -0.18, up = 0.45} }, + ["youga"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.23, up = 0.45} }, + ["youga2"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.25, up = 0.40} }, + ["youga4"] = { distance = 1.3, nozzleOffset = { forward = 0.35, right = -0.30, up = 0.40} }, + ["moonbeam"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.28, up = 0.40} }, + ["moonbeam2"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.24, up = 0.40} }, + ["boxville"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.24, up = 0.40} }, + ["boxville2"] = { distance = 1.3, nozzleOffset = { forward = -2.20, right = -0.30, up = 0.05} }, + ["boxville3"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.24, up = 0.40} }, + ["boxville4"] = { distance = 1.3, nozzleOffset = { forward = -2.20, right = -0.30, up = 0.05} }, + ["boxville5"] = { distance = 1.3, nozzleOffset = { forward = -2.20, right = -0.30, up = 0.05} }, + ["pony"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.24, up = 0.40} }, + ["pony2"] = { distance = 1.3, nozzleOffset = { forward = 0.40, right = -0.26, up = 0.40} }, + ["journey2"] = { distance = 1.3, nozzleOffset = { forward = -0.65, right = -0.34, up = 0.45} }, + ["surfer3"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.29, up = 0.45} }, + ["speedo5"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.24, up = 0.45} }, + ["mule2"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.35, up = 0.75} }, + ["taco"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.24, up = 0.45} }, + + + -- Lspd, ems .... + ["riot"] = { distance = 2.5, nozzleOffset = { forward = -0.80, right = -1.30, up = 0.30} }, + ["riot2"] = { distance = 1.3, nozzleOffset = { forward = -0.70, right = -0.09, up = 0.65} }, + ["pbus"] = { distance = 1.3, nozzleOffset = { forward = -0.70, right = -0.30, up = 0.65} }, + ["police"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.17, up = 0.50} }, + ["police2"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.24, up = 0.50} }, + ["police3"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.17, up = 0.50} }, + ["police4"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.17, up = 0.50} }, + ["sheriff"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.17, up = 0.50} }, + ["sheriff2"] = { distance = 1.3, nozzleOffset = { forward = 0.70, right = -0.28, up = 0.60} }, + ["policeold1"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.24, up = 0.60} }, + ["policeold2"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.16, up = 0.40} }, + ["policet"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.26, up = 0.60} }, + ["policeb"] = { distance = 1.3, nozzleOffset = { forward = -0.18, right = -0.18, up = 0.10}, nozzleRotation = { x = 0, y = 0, z = 60} }, + ["polmav"] = { distance = 3.0, nozzleOffset = { forward = 0.12, right = -0.60, up = -0.45}, nozzleRotation = { x = 0, y = 0, z = 20} }, + ["ambulance"] = { distance = 1.3, nozzleOffset = { forward = 2.95, right = -0.08, up = 0.50} }, + ["firetruk"] = { distance = 1.3, nozzleOffset = { forward = 0.50, right = -0.32, up = 0.70} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["lguard"] = { distance = 1.3, nozzleOffset = { forward = 0.67, right = -0.27, up = 0.57} }, + ["pranger"] = { distance = 1.3, nozzleOffset = { forward = 0.67, right = -0.27, up = 0.57} }, + ["fbi"] = { distance = 1.3, nozzleOffset = { forward = -0.45, right = -0.24, up = 0.40} }, + ["fbi2"] = { distance = 1.3, nozzleOffset = { forward = 0.67, right = -0.27, up = 0.57} }, + ["predator"] = { distance = 3.5, nozzleOffset = { forward = 1.80, right = 1.58, up = 0.17}, nozzleRotation = { x = 0, y = 0, z = 180} }, + + -- Military + ["apc"] = { distance = 2.5, nozzleOffset = { forward = -0.80, right = -1.00, up = 1.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["barracks"] = { distance = 3.5, nozzleOffset = { forward = 0.00, right = -0.25, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["barracks2"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.28, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["barracks3"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.16, up = -0.03} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["chernobog"] = { distance = 2.5, nozzleOffset = { forward = 3.70, right = -0.20, up = 0.22} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["crusader"] = { distance = 1.5, nozzleOffset = { forward = 0.25, right = -0.11, up = 0.65} }, + ["halftrack"] = { distance = 1.5, nozzleOffset = { forward = -0.60, right = -0.30, up = 0.90} }, + ["khanjali"] = { distance = 1.5, nozzleOffset = { forward = -0.55, right = -0.80, up = 0.95} }, + ["rhino"] = { distance = 1.5, nozzleOffset = { forward = -0.05, right = -0.52, up = 1.00} }, + ["scarab"] = { distance = 1.5, nozzleOffset = { forward = 0.60, right = 0.15, up = 1.08} }, + ["terbyte"] = { distance = 1.5, nozzleOffset = { forward = -0.700, right = -0.47, up = 0.65} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["vetir"] = { distance = 3.0, nozzleOffset = { forward = 1.25, right = 1.95, up = 0.40} , nozzleRotation = { x = 0, y = 0, z = 180} }, + + ["thruster"] = { distance = 1.5, nozzleOffset = { forward = -0.10, right = 0.40, up = 1.25} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["minitank"] = { distance = 1.5, nozzleOffset = { forward = 0.05, right = -0.05, up = 0.49} }, + + -- Electric cars + ["voltic"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.14, up = 0.45} }, + ["voltic2"] = { distance = 1.3, nozzleOffset = { forward = -0.12, right = 0.12, up = 0.57} }, + ["caddy"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.09, up = 0.53} }, + ["caddy2"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.09, up = 0.35} }, + ["caddy3"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.09, up = 0.35} }, + ["surge"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.14, up = 0.45} }, + ["iwagen"] = { distance = 1.3, nozzleOffset = { forward = -0.40, right = -0.16, up = 0.50} }, + ["raiden"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.13, up = 0.50} }, + ["airtug"] = { distance = 1.3, nozzleOffset = { forward = -0.20, right = -0.18, up = 0.47} }, + ["neon"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.16, up = 0.50} }, + ["omnisegt"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.16, up = 0.40} }, + ["cyclone"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.16, up = 0.45} }, + ["tezeract"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.16, up = 0.48} }, + ["imorgon"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.16, up = 0.48} }, + ["dilettante"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.14, up = 0.48} }, + ["dilettante2"] = { distance = 1.3, nozzleOffset = { forward = -0.05, right = -0.14, up = 0.48} }, + ["khamelion"] = { distance = 1.3, nozzleOffset = { forward = -0.35, right = -0.20, up = 0.48} }, + + ["rcbandito"] = { distance = 1.3, nozzleOffset = { forward = -0.12, right = 0.12, up = 0.22} }, + + + -- Motorcycles + ["akuma"] = { distance = 1.1, nozzleOffset = { forward = 0.01, right = -0.20, up = 0.20} }, + ["avarus"] = { distance = 1.1, nozzleOffset = { forward = -0.22, right = 0.03, up = 0.11} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["bagger"] = { distance = 1.1, nozzleOffset = { forward = -0.26, right = 0.03, up = 0.11} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["bati"] = { distance = 1.1, nozzleOffset = { forward = -0.15, right = -0.25, up = 0.20} }, + ["bati2"] = { distance = 1.1, nozzleOffset = { forward = -0.15, right = -0.24, up = 0.20} }, + ["bf400"] = { distance = 1.1, nozzleOffset = { forward = -0.05, right = -0.24, up = 0.29} }, + ["carbonrs"] = { distance = 1.1, nozzleOffset = { forward = 0.01, right = -0.22, up = 0.20} }, + ["chimera"] = { distance = 1.1, nozzleOffset = { forward = -0.22, right = 0.00, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["cliffhanger"] = { distance = 1.1, nozzleOffset = { forward = -0.33, right = 0.05, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["daemon"] = { distance = 1.1, nozzleOffset = { forward = -0.17, right = 0.03, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["daemon2"] = { distance = 1.1, nozzleOffset = { forward = -0.17, right = 0.03, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["defiler"] = { distance = 1.1, nozzleOffset = { forward = 0.09, right = -0.23, up = 0.20} }, + ["deathbike"] = { distance = 1.1, nozzleOffset = { forward = -0.17, right = 0.03, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["deathbike2"] = { distance = 1.1, nozzleOffset = { forward = -0.25, right = 0.03, up = 0.07} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["deathbike3"] = { distance = 1.1, nozzleOffset = { forward = -0.17, right = 0.03, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["diablous"] = { distance = 1.1, nozzleOffset = { forward = -0.27, right = 0.06, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["diablous2"] = { distance = 1.1, nozzleOffset = { forward = -0.27, right = 0.03, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["double"] = { distance = 1.1, nozzleOffset = { forward = 0.01, right = -0.22, up = 0.20} }, + ["enduro"] = { distance = 1.1, nozzleOffset = { forward = -0.05, right = -0.17, up = 0.25} }, + ["esskey"] = { distance = 1.1, nozzleOffset = { forward = -0.05, right = -0.20, up = 0.20} }, + ["faggio"] = { distance = 1.1, nozzleOffset = { forward = 0.20, right = -0.28, up = 0.30} }, + ["faggio2"] = { distance = 1.1, nozzleOffset = { forward = 0.20, right = 0.25, up = -0.10} , nozzleRotation = { x = 0, y = 0, z = 180} }, + ["faggio3"] = { distance = 1.1, nozzleOffset = { forward = 0.20, right = 0.25, up = -0.10} , nozzleRotation = { x = 0, y = 0, z = 180} }, + ["fcr"] = { distance = 1.1, nozzleOffset = { forward = -0.03, right = -0.21, up = 0.10} }, + ["gargoyle"] = { distance = 1.1, nozzleOffset = { forward = -0.26, right = 0.03, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["hakuchou"] = { distance = 1.1, nozzleOffset = { forward = 0.05, right = -0.17, up = 0.10} }, + ["hakuchou2"] = { distance = 1.1, nozzleOffset = { forward = 0.00, right = -0.19, up = 0.10} }, + ["hexer"] = { distance = 1.1, nozzleOffset = { forward = -0.17, right = 0.04, up = 0.20} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["innovation"] = { distance = 1.1, nozzleOffset = { forward = -0.23, right = 0.02, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["lectro"] = { distance = 1.1, nozzleOffset = { forward = -0.12, right = -0.20, up = 0.20} }, + ["manchez"] = { distance = 1.1, nozzleOffset = { forward = -0.04, right = -0.20, up = 0.10} }, + ["nemesis"] = { distance = 1.1, nozzleOffset = { forward = -0.03, right = -0.17, up = 0.10} }, + ["nightblade"] = { distance = 1.1, nozzleOffset = { forward = -0.27, right = 0.05, up = 0.14} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["oppressor"] = { distance = 1.1, nozzleOffset = { forward = -0.27, right = 0.05, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["pcj"] = { distance = 1.1, nozzleOffset = { forward = 0.04, right = -0.20, up = 0.20} }, + ["ratbike"] = { distance = 1.1, nozzleOffset = { forward = -0.22, right = 0.03, up = 0.11} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["ruffian"] = { distance = 1.1, nozzleOffset = { forward = 0.04, right = -0.19, up = 0.20} }, + ["sanchez"] = { distance = 1.1, nozzleOffset = { forward = -0.05, right = -0.22, up = 0.25} }, + ["sanchez2"] = { distance = 1.1, nozzleOffset = { forward = -0.05, right = -0.22, up = 0.25} }, + ["sanctus"] = { distance = 1.1, nozzleOffset = { forward = -0.22, right = 0.03, up = 0.08} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["shotaro"] = { distance = 1.1, nozzleOffset = { forward = 0.06, right = -0.25, up = 0.20} }, + ["sovereign"] = { distance = 1.1, nozzleOffset = { forward = -0.07, right = -0.23, up = 0.15} }, + ["stryder"] = { distance = 1.1, nozzleOffset = { forward = 0.06, right = -0.20, up = 0.15} }, + ["thrust"] = { distance = 1.1, nozzleOffset = { forward = -0.02, right = -0.25, up = 0.15} }, + ["vader"] = { distance = 1.1, nozzleOffset = { forward = 0.10, right = -0.25, up = 0.20} }, + ["vindicator"] = { distance = 1.1, nozzleOffset = { forward = -0.02, right = -0.25, up = 0.15} }, + ["vortex"] = { distance = 1.1, nozzleOffset = { forward = -0.02, right = -0.20, up = 0.12} }, + ["wolfsbane"] = { distance = 1.1, nozzleOffset = { forward = -0.22, right = 0.03, up = 0.11} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["zombiea"] = { distance = 1.1, nozzleOffset = { forward = -0.14, right = 0.03, up = 0.11} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["zombieb"] = { distance = 1.1, nozzleOffset = { forward = -0.21, right = 0.03, up = 0.15} , nozzleRotation = { x = 0, y = 0, z = 90} }, + ["manchez2"] = { distance = 1.1, nozzleOffset = { forward = -0.04, right = -0.22, up = 0.10} }, + ["shinobi"] = { distance = 1.1, nozzleOffset = { forward = -0.04, right = -0.22, up = 0.20} }, + ["reever"] = { distance = 1.1, nozzleOffset = { forward = 0.09, right = -0.20, up = 0.10} }, + ["manchez3"] = { distance = 1.1, nozzleOffset = { forward = -0.04, right = -0.22, up = 0.10} }, + + -- truck 2 + ["pounder2"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.12, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["mule4"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.35, up = 0.75} }, + ["phantom3"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["hauler2"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.17, up = 0.02} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["phantom2"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["mule5"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.35, up = 0.75} }, + ["stockade"] = { distance = 3.0, nozzleOffset = { forward = -0.50, right = -1.36, up = 0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["pounder"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.12, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["phantom"] = { distance = 1.3, nozzleOffset = { forward = -0.60, right = -0.17, up = 0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["packer"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.17, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["mule"] = { distance = 1.3, nozzleOffset = { forward = -0.50, right = -0.35, up = 0.75} }, + ["hauler"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = -0.17, up = -0.02} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["biff"] = { distance = 1.3, nozzleOffset = { forward = 0.0, right = 0.11, up = -0.06} , nozzleRotation = { x = 0, y = 0, z = 180} }, + ["benson"] = { distance = 1.3, nozzleOffset = { forward = 0.32, right = 0.40, up = 0.21} , nozzleRotation = { x = 0, y = 0, z = 180} }, + + --helicopters + ["conada2"] = { distance = 3.5, nozzleOffset = { forward = -0.10, right = -0.90, up = 0.20}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["akula"] = { distance = 3.5, nozzleOffset = { forward = -1.50, right = -0.65, up = -0.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["annihilator2"] = { distance = 3.5, nozzleOffset = { forward = -1.10, right = -1.25, up = -1.70}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["annihilator"] = { distance = 3.5, nozzleOffset = { forward = -1.20, right = -1.05, up = -1.70}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["havok"] = { distance = 1.5, nozzleOffset = { forward = -0.10, right = -0.49, up = 0.00}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["hunter"] = { distance = 3.5, nozzleOffset = { forward = -1.50, right = -0.69, up = -0.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["seasparrow"] = { distance = 1.5, nozzleOffset = { forward = -0.10, right = -0.70, up = 0.00}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["seasparrow2"] = { distance = 3.2, nozzleOffset = { forward = -0.10, right = -0.70, up = 0.00}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["supervolito"] = { distance = 2.0, nozzleOffset = { forward = -0.50, right = -0.87, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["supervolito2"] = { distance = 2.0, nozzleOffset = { forward = -0.50, right = -0.87, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["swift"] = { distance = 2.5, nozzleOffset = { forward = -1.10, right = -0.90, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["swift2"] = { distance = 2.5, nozzleOffset = { forward = -1.10, right = -0.90, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["valkyrie"] = { distance = 2.5, nozzleOffset = { forward = 1.60, right = -1.21, up = -1.80}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["valkyrie2"] = { distance = 2.5, nozzleOffset = { forward = 1.60, right = -1.21, up = -1.80}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["volatus"] = { distance = 2.5, nozzleOffset = { forward = -1.10, right = -0.86, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["savage"] = { distance = 3.5, nozzleOffset = { forward = -0.10, right = -1.09, up = -2.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["buzzard2"] = { distance = 2.2, nozzleOffset = { forward = -1.10, right = -0.76, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 10} }, + ["buzzard"] = { distance = 2.5, nozzleOffset = { forward = -1.10, right = -0.76, up = -1.30}, nozzleRotation = { x = 0, y = 0, z = 10} }, + ["cargobob"] = { distance = 4.0, nozzleOffset = { forward = -2.20, right = -1.29, up = -2.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["cargobob2"] = { distance = 4.0, nozzleOffset = { forward = -2.20, right = -1.29, up = -2.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["cargobob3"] = { distance = 4.0, nozzleOffset = { forward = -2.20, right = -1.29, up = -2.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["cargobob4"] = { distance = 4.0, nozzleOffset = { forward = -2.20, right = -1.29, up = -2.30}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["conada"] = { distance = 2.0, nozzleOffset = { forward = -0.09, right = -0.90, up = 0.20}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["frogger"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = -0.92, up = -0.90}, nozzleRotation = { x = 0, y = 0, z = 20} }, + ["frogger2"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = -0.92, up = -0.90}, nozzleRotation = { x = 0, y = 0, z = 20} }, + ["seasparrow3"] = { distance = 1.5, nozzleOffset = { forward = -0.10, right = -0.70, up = 0.00}, nozzleRotation = { x = 0, y = 0, z = 0} }, + ["skylift"] = { distance = 4.0, nozzleOffset = { forward = 4.95, right = 0.00, up = -3.50}, nozzleRotation = { x = 0, y = 0, z = 90} }, + ["maverick"] = { distance = 3.5, nozzleOffset = { forward = 0.00, right = -0.90, up = -1.50}, nozzleRotation = { x = 0, y = 0, z = 20} }, + + -- Boats + ["toro"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.75, up = 0.48 } , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["toro2"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.75, up = 0.48 } , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["patrolboat"] = { distance = 1.0, nozzleOffset = { forward = -0.30, right = 0.25, up = 0.20 } , nozzleRotation = { x = 0, y = 0, z = 180} }, + ["longfin"] = { distance = 1.5, nozzleOffset = { forward = 0.90 , right = -1.40, up = 0.68} , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["speeder"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.95, up = 0.65 } , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["speeder2"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.95, up = 0.65 } , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["tropic"] = { distance = 2.0, nozzleOffset = { forward = 1.00, right = -1.15, up = 0.71} , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["tropic2"] = { distance = 2.0, nozzleOffset = { forward = 1.00, right = -1.15, up = 0.71} , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["suntrap"] = { distance = 2.0, nozzleOffset = { forward = 0.55, right = -0.95, up = 0.93} , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["squalo"] = { distance = 1.5, nozzleOffset = { forward = 0.0, right = -0.95, up = 0.57 } , nozzleRotation = { x = -60, y = 0, z = 90} }, + ["marquis"] = { distance = 1.5, nozzleOffset = { forward = 0.50, right = -1.35, up = 1.36 } , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["jetmax"] = { distance = 2.0, nozzleOffset = { forward = 0.50, right = -0.90, up = 0.54 } , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["dinghy"] = { distance = 1.0, nozzleOffset = { forward = -0.20, right = -0.69, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["dinghy2"] = { distance = 1.0, nozzleOffset = { forward = -0.20, right = -0.69, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["dinghy3"] = { distance = 1.0, nozzleOffset = { forward = -0.20, right = -0.69, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["dinghy4"] = { distance = 1.0, nozzleOffset = { forward = -0.20, right = -0.69, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["dinghy5"] = { distance = 1.0, nozzleOffset = { forward = -0.20, right = -0.69, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + + -- Jet + ["seashark"] = { distance = 2.0, nozzleOffset = { forward = 0.89, right = 0.00, up = 0.42 } , nozzleRotation = { x = -60, y = 0, z = 90} }, + ["seashark3"] = { distance = 2.0, nozzleOffset = { forward = 0.89, right = 0.00, up = 0.42 } , nozzleRotation = { x = -60, y = 0, z = 90} }, + ["seashark2"] = { distance = 2.0, nozzleOffset = { forward = 0.89, right = 0.00, up = 0.42 } , nozzleRotation = { x = -60, y = 0, z = 90} }, + + -- Submarine + ["avisa"] = { distance = 2.0, nozzleOffset = { forward = -0.40, right = -1.30, up = 0.14 } , nozzleRotation = { x = -85, y = 0, z = 90} }, + ["submersible2"] = { distance = 2.5, nozzleOffset = { forward = -0.40, right = -0.70, up = 1.20 } , nozzleRotation = { x = -90, y = 0, z = 0} }, + ["submersible"] = { distance = 2.5, nozzleOffset = { forward = -0.60, right = -0.85, up = 1.20 } , nozzleRotation = { x = -90, y = 0, z = 0} }, + + -- Big boats +-- ["kosatka"] = { distance = 6.0, nozzleOffset = { forward = 0.0, right = -0.15, up = 1.0 } }, ----- BIG SUBMARINE + ["tug"] = { distance = 2.5, nozzleOffset = { forward = 0.0, right = 0.79, up = 0.20} , nozzleRotation = { x = 0, y = 0, z = 180} }, + + -- Plane + ["streamer216"] = { distance = 1.5, nozzleOffset = { forward = -0.40, right = 1.02, up = 1.10 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["raiju"] = { distance = 3.0, nozzleOffset = { forward = 1.0, right = -0.85, up = -0.25 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["alkonost"] = { distance = 11.0, nozzleOffset = { forward = -2.60, right = -5.30, up = 0.90 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["strikeforce"] = { distance = 3.0, nozzleOffset = { forward = 3.0, right = 1.37, up = 1.60 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["blimp3"] = { distance = 2.0, nozzleOffset = { forward = -0.40, right = 0.41, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["avenger"] = { distance = 2.0, nozzleOffset = { forward = -0.60, right = -0.73, up = 1.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["avenger2"] = { distance = 2.0, nozzleOffset = { forward = -0.60, right = -0.73, up = 1.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["volatol"] = { distance = 2.0, nozzleOffset = { forward = 0.80, right = 0.46, up = 1.85} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["nokota"] = { distance = 2.5, nozzleOffset = { forward = -1.50, right = 1.60, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["seabreeze"] = { distance = 2.5, nozzleOffset = { forward = -0.20, right = -0.10, up = -1.70} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["pyro"] = { distance = 3.0, nozzleOffset = { forward = 2.10, right = 1.56, up = 0.90} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["mogul"] = { distance = 2.0, nozzleOffset = { forward = 0.30, right = -0.68, up = 1.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["howard"] = { distance = 2.5, nozzleOffset = { forward = 1.80, right = 1.25, up = 1.25} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["bombushka"] = { distance = 2.5, nozzleOffset = { forward = 0.50, right = -0.71, up = 1.25} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["molotok"] = { distance = 3.5, nozzleOffset = { forward = 2.00, right = -0.85, up = 0.55} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["microlight"] = { distance = 3.5, nozzleOffset = { forward = -0.02, right = 0.48, up = 0.30} , nozzleRotation = { x = 0, y = 0, z = 45} }, + ["tula"] = { distance = 3.5, nozzleOffset = { forward = 0.70, right = -0.52, up = -0.30} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["rogue"] = { distance = 3.5, nozzleOffset = { forward = 0.10, right = -0.15, up = 0.45} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["starling"] = { distance = 3.5, nozzleOffset = { forward = 0.10, right = -0.08, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["alphaz1"] = { distance = 3.5, nozzleOffset = { forward = -1.00, right = -0.03, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["nimbus"] = { distance = 5.0, nozzleOffset = { forward = 0.00, right = -0.10, up = -0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["luxor2"] = { distance = 5.0, nozzleOffset = { forward = 0.00, right = 0.80, up = -0.30} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["velum2"] = { distance = 5.0, nozzleOffset = { forward = -0.30, right = -0.20, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["hydra"] = { distance = 2.5, nozzleOffset = { forward = -1.80, right = 1.88, up = 1.35} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["blimp2"] = { distance = 2.0, nozzleOffset = { forward = -0.40, right = 0.41, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["dodo"] = { distance = 2.0, nozzleOffset = { forward = -0.15, right = -0.13, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["miljet"] = { distance = 6.0, nozzleOffset = { forward = 2.75, right = -0.47, up = -1.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["vestra"] = { distance = 2.5, nozzleOffset = { forward = 2.50, right = 1.35, up = 1.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["cargoplane"] = { distance = 2.5, nozzleOffset = { forward = 1.50, right = 0.27, up = 1.60} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["velum"] = { distance = 5.0, nozzleOffset = { forward = -0.30, right = -0.20, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["titan"] = { distance = 2.5, nozzleOffset = { forward = 0.50, right = -0.46, up = 1.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["shamal"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.10, up = -0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["lazer"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.09, up = -0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["stunt"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.08, up = -0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["luxor"] = { distance = 5.0, nozzleOffset = { forward = 0.00, right = 0.80, up = -0.30} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["jet"] = { distance = 15.0, nozzleOffset = { forward = 1.20, right = 1.20, up = 2.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["duster"] = { distance = 2.5, nozzleOffset = { forward = -0.20, right = -0.15, up = -0.05} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["cuban800"] = { distance = 2.5, nozzleOffset = { forward = 1.00, right = 0.51, up = 0.20} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["blimp"] = { distance = 2.0, nozzleOffset = { forward = -0.40, right = 0.41, up = 0.50 } , nozzleRotation = { x = 0, y = 0, z = 0} }, + + -- Service + ["brickade"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = 0.16, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, + ["brickade2"] = { distance = 1.3, nozzleOffset = { forward = 0.00, right = 0.16, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, + ["pbus2"] = { distance = 3.0, nozzleOffset = { forward = -2.38, right = -0.30, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["wastelander"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.05, up = -0.10} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["rallytruck"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = 0.64, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, +-- ["metrotrain"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = 0.64, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, --- metro +-- ["freight"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = 0.64, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, -- train +-- ["cablecar"] = { distance = 2.0, nozzleOffset = { forward = 0.00, right = 0.64, up = 0.0} , nozzleRotation = { x = 0, y = 0, z = 180} }, -- cablecar + ["trash"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.15, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["trash2"] = { distance = 2.5, nozzleOffset = { forward = 0.00, right = -0.15, up = 0.00} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["tourbus"] = { distance = 2.0, nozzleOffset = { forward = -0.50, right = -0.33, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["taxi"] = { distance = 2.0, nozzleOffset = { forward = -0.50, right = -0.20, up = 0.45} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["rentalbus"] = { distance = 2.0, nozzleOffset = { forward = -0.50, right = -0.33, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["coach"] = { distance = 2.0, nozzleOffset = { forward = -0.80, right = -0.29, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["bus"] = { distance = 2.0, nozzleOffset = { forward = -0.80, right = -0.28, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, + ["airbus"] = { distance = 2.0, nozzleOffset = { forward = -0.80, right = -0.28, up = 0.50} , nozzleRotation = { x = 0, y = 0, z = 0} }, + + -- Race + ["openwheel2"] = { distance = 2.0, nozzleOffset = { forward = 0.0, right = -0.60, up = 0.30} , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["openwheel1"] = { distance = 2.0, nozzleOffset = { forward = 0.0, right = -0.60, up = 0.35} , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["formula2"] = { distance = 2.0, nozzleOffset = { forward = 0.0, right = -0.50, up = 0.29} , nozzleRotation = { x = -75, y = 0, z = 90} }, + ["formula"] = { distance = 2.0, nozzleOffset = { forward = 0.0, right = -0.50, up = 0.29} , nozzleRotation = { x = -75, y = 0, z = 90} }, +} \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/client/client_electric.lua b/resources/[carscripts]/lc_fuel/client/client_electric.lua index af2dcee32..58da345e9 100644 --- a/resources/[carscripts]/lc_fuel/client/client_electric.lua +++ b/resources/[carscripts]/lc_fuel/client/client_electric.lua @@ -1,153 +1,153 @@ --- Do not load anything here if electric is disabled -if not Config.Electric.enabled then - return -end - -local electricChargers = {} - ------------------------------------------------------------------------------------------------------------------------------------------ --- Threads ------------------------------------------------------------------------------------------------------------------------------------------ - --- Create sphere zones for each station, hooking up onEnter/onExit -function createElectricZones() - assert(Utils.Zones, "You are using an outdated version of lc_utils. Please update your 'lc_utils' script to the latest version: https://github.com/LeonardoSoares98/lc_utils/releases/latest/download/lc_utils.zip") - - local stations = groupChargersByStation() - - for _, station in pairs(stations) do - Utils.Zones.createZone({ - coords = station.center, - radius = 50.0, - onEnter = function() - for _, charger in pairs(station.chargers) do - loadElectricCharger(charger) - end - end, - onExit = function() - for _, charger in pairs(station.chargers) do - unloadElectricCharger(charger) - end - end - }) - end -end - --- Thread to detect near electric chargers -function createElectricMarkersThread() - CreateThread(function() - while true do - local ped = PlayerPedId() - local playerCoords = GetEntityCoords(ped) - local pump, pumpModel = GetClosestPump(playerCoords, true) - - while pump and pump > 0 and #(playerCoords - GetEntityCoords(pump)) < 2.0 do - playerCoords = GetEntityCoords(ped) - if not mainUiOpen and not DoesEntityExist(fuelNozzle) then - Utils.Markers.showHelpNotification(cachedTranslations.open_recharge, true) - if IsControlJustPressed(0,38) then - clientOpenUI(pump, pumpModel, true) - end - end - Wait(2) - end - Wait(1000) - end - end) -end - -function createElectricTargetsThread() - local pumpModels = {} -- This will be the final list without duplicates - local seenModels = {} -- This acts as a set to track unique values - - for _, chargerData in pairs(Config.Electric.chargersLocation) do - local model = chargerData.prop - if not seenModels[model] then - seenModels[model] = true -- Mark model as seen - table.insert(pumpModels, model) -- Insert only if it's not a duplicate - end - end - - -- Pass unique models to the target creation function - Utils.Target.createTargetForModel(pumpModels, openElectricUICallback, Utils.translate('target.open_recharge'), "fas fa-plug", "#00a413",nil,nil,canOpenPumpUiTargetCallback) - - Utils.Target.createTargetForModel(pumpModels,returnNozzle,Utils.translate('target.return_nozzle'),"fas fa-plug","#a42100",nil,nil,canReturnNozzleTargetCallback) -end - -function openElectricUICallback() - local ped = PlayerPedId() - local playerCoords = GetEntityCoords(ped) - local pump, pumpModel = GetClosestPump(playerCoords, true) - if pump then - clientOpenUI(pump, pumpModel, true) - else - exports['lc_utils']:notify("error", Utils.translate("pump_not_found")) - end -end - ------------------------------------------------------------------------------------------------------------------------------------------ --- Utils ------------------------------------------------------------------------------------------------------------------------------------------ - -function loadElectricCharger(chargerData) - if not electricChargers[chargerData.location] then - RequestModel(chargerData.prop) - while not HasModelLoaded(chargerData.prop) do - Wait(10) - end - - local heading = chargerData.location.w + 180.0 - local electricCharger = CreateObject(chargerData.prop, chargerData.location.x, chargerData.location.y, chargerData.location.z, false, true, true) - SetEntityHeading(electricCharger, heading) - FreezeEntityPosition(electricCharger, true) - - electricChargers[chargerData.location] = electricCharger - end -end - -function unloadElectricCharger(chargerData) - local charger = electricChargers[chargerData.location] - if charger and DoesEntityExist(charger) then - DeleteEntity(charger) - electricChargers[chargerData.location] = nil - end -end - --- Utility to group chargers by their station -function groupChargersByStation() - local stations = {} - for _, charger in pairs(Config.Electric.chargersLocation) do - local assigned = false - for _, station in pairs(stations) do - local dist = #(station.center - vector3(charger.location.x, charger.location.y, charger.location.z)) - if dist < 20.0 then - table.insert(station.chargers, charger) - station.center = (station.center + vector3(charger.location.x, charger.location.y, charger.location.z)) / 2 - assigned = true - break - end - end - if not assigned then - table.insert(stations, { - center = vector3(charger.location.x, charger.location.y, charger.location.z), - chargers = { charger } - }) - end - end - return stations -end - -AddEventHandler('onResourceStop', function(resourceName) - if GetCurrentResourceName() ~= resourceName then return end - - deleteAllElectricChargers() -end) - -function deleteAllElectricChargers() - for _, charger in pairs(electricChargers) do - if DoesEntityExist(charger) then - DeleteEntity(charger) - end - end - electricChargers = {} +-- Do not load anything here if electric is disabled +if not Config.Electric.enabled then + return +end + +local electricChargers = {} + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Threads +----------------------------------------------------------------------------------------------------------------------------------------- + +-- Create sphere zones for each station, hooking up onEnter/onExit +function createElectricZones() + assert(Utils.Zones, "You are using an outdated version of lc_utils. Please update your 'lc_utils' script to the latest version: https://github.com/LeonardoSoares98/lc_utils/releases/latest/download/lc_utils.zip") + + local stations = groupChargersByStation() + + for _, station in pairs(stations) do + Utils.Zones.createZone({ + coords = station.center, + radius = 50.0, + onEnter = function() + for _, charger in pairs(station.chargers) do + loadElectricCharger(charger) + end + end, + onExit = function() + for _, charger in pairs(station.chargers) do + unloadElectricCharger(charger) + end + end + }) + end +end + +-- Thread to detect near electric chargers +function createElectricMarkersThread() + CreateThread(function() + while true do + local ped = PlayerPedId() + local playerCoords = GetEntityCoords(ped) + local pump, pumpModel = GetClosestPump(playerCoords, true) + + while pump and pump > 0 and #(playerCoords - GetEntityCoords(pump)) < 2.0 do + playerCoords = GetEntityCoords(ped) + if not mainUiOpen and not DoesEntityExist(fuelNozzle) then + Utils.Markers.showHelpNotification(cachedTranslations.open_recharge, true) + if IsControlJustPressed(0,38) then + clientOpenUI(pump, pumpModel, true) + end + end + Wait(2) + end + Wait(1000) + end + end) +end + +function createElectricTargetsThread() + local pumpModels = {} -- This will be the final list without duplicates + local seenModels = {} -- This acts as a set to track unique values + + for _, chargerData in pairs(Config.Electric.chargersLocation) do + local model = chargerData.prop + if not seenModels[model] then + seenModels[model] = true -- Mark model as seen + table.insert(pumpModels, model) -- Insert only if it's not a duplicate + end + end + + -- Pass unique models to the target creation function + Utils.Target.createTargetForModel(pumpModels, openElectricUICallback, Utils.translate('target.open_recharge'), "fas fa-plug", "#00a413",nil,nil,canOpenPumpUiTargetCallback) + + Utils.Target.createTargetForModel(pumpModels,returnNozzle,Utils.translate('target.return_nozzle'),"fas fa-plug","#a42100",nil,nil,canReturnNozzleTargetCallback) +end + +function openElectricUICallback() + local ped = PlayerPedId() + local playerCoords = GetEntityCoords(ped) + local pump, pumpModel = GetClosestPump(playerCoords, true) + if pump then + clientOpenUI(pump, pumpModel, true) + else + exports['lc_utils']:notify("error", Utils.translate("pump_not_found")) + end +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Utils +----------------------------------------------------------------------------------------------------------------------------------------- + +function loadElectricCharger(chargerData) + if not electricChargers[chargerData.location] then + RequestModel(chargerData.prop) + while not HasModelLoaded(chargerData.prop) do + Wait(10) + end + + local heading = chargerData.location.w + 180.0 + local electricCharger = CreateObject(chargerData.prop, chargerData.location.x, chargerData.location.y, chargerData.location.z, false, true, true) + SetEntityHeading(electricCharger, heading) + FreezeEntityPosition(electricCharger, true) + + electricChargers[chargerData.location] = electricCharger + end +end + +function unloadElectricCharger(chargerData) + local charger = electricChargers[chargerData.location] + if charger and DoesEntityExist(charger) then + DeleteEntity(charger) + electricChargers[chargerData.location] = nil + end +end + +-- Utility to group chargers by their station +function groupChargersByStation() + local stations = {} + for _, charger in pairs(Config.Electric.chargersLocation) do + local assigned = false + for _, station in pairs(stations) do + local dist = #(station.center - vector3(charger.location.x, charger.location.y, charger.location.z)) + if dist < 20.0 then + table.insert(station.chargers, charger) + station.center = (station.center + vector3(charger.location.x, charger.location.y, charger.location.z)) / 2 + assigned = true + break + end + end + if not assigned then + table.insert(stations, { + center = vector3(charger.location.x, charger.location.y, charger.location.z), + chargers = { charger } + }) + end + end + return stations +end + +AddEventHandler('onResourceStop', function(resourceName) + if GetCurrentResourceName() ~= resourceName then return end + + deleteAllElectricChargers() +end) + +function deleteAllElectricChargers() + for _, charger in pairs(electricChargers) do + if DoesEntityExist(charger) then + DeleteEntity(charger) + end + end + electricChargers = {} end \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/client/client_fuel_chart.lua b/resources/[carscripts]/lc_fuel/client/client_fuel_chart.lua index 5822a04e0..355ea103e 100644 --- a/resources/[carscripts]/lc_fuel/client/client_fuel_chart.lua +++ b/resources/[carscripts]/lc_fuel/client/client_fuel_chart.lua @@ -1,77 +1,77 @@ - - ------------------------------------------------------------------------------------------------------------------------------------------ --- Fuel consumption chart ------------------------------------------------------------------------------------------------------------------------------------------ - -if Config.FuelConsumptionChart.enabled then - RegisterCommand(Config.FuelConsumptionChart.command,function(source) - toggleFuelConsumptionChart() - end, false) - - RegisterCommand("fuel_focus", function() - if isFuelConsumptionChartOpen then - SetNuiFocus(true,true) - end - end, false) - - RegisterKeyMapping( - "fuel_focus", -- command triggered by key - "Focus Fuel Chart UI", -- description in keybindings - "keyboard", - Config.FuelConsumptionChart.focusShortcut - ) - - function toggleFuelConsumptionChart() - loadNuiVariables() - if isFuelConsumptionChartOpen then - closeFuelConsumptionChartUI() - else - local ped = PlayerPedId() - if not IsPedInAnyVehicle(ped, false) then - exports['lc_utils']:notify("error",Utils.translate("vehicle_not_found")) - return - end - local vehicle = GetVehiclePedIsIn(ped, false) - if GetPedInVehicleSeat(vehicle, -1) ~= ped or IsVehicleBlacklisted(vehicle) then - exports['lc_utils']:notify("error",Utils.translate("vehicle_not_found")) - return - end - - SendNUIMessage({ - showFuelConsumptionChart = true, - isRecording = isRecording, - position = Config.FuelConsumptionChart.position, - focusShortcut = Config.FuelConsumptionChart.focusShortcut, - }) - isFuelConsumptionChartOpen = true - end - end - - function updateFuelConsumptionChart(fuelConsumptionData) - SendNUIMessage({ - updateFuelConsumptionChart = true, - fuelConsumptionData = fuelConsumptionData, - }) - end - - function closeFuelConsumptionChartUI() - SendNUIMessage({ - hideFuelConsumptionChart = true, - }) - isFuelConsumptionChartOpen = false - SetNuiFocus(false,false) - end - - function storeDataForChart(vehicle, newFuelLevel, currentConsumption) - if not isRecording then - updateFuelConsumptionChart({ fuel = nil, speed = nil, consumption = nil }) - return - end - - local speed = GetEntitySpeed(vehicle) * 3.6 - if isFuelConsumptionChartOpen then - updateFuelConsumptionChart({ fuel = newFuelLevel, speed = speed, consumption = currentConsumption }) - end - end + + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Fuel consumption chart +----------------------------------------------------------------------------------------------------------------------------------------- + +if Config.FuelConsumptionChart.enabled then + RegisterCommand(Config.FuelConsumptionChart.command,function(source) + toggleFuelConsumptionChart() + end, false) + + RegisterCommand("fuel_focus", function() + if isFuelConsumptionChartOpen then + SetNuiFocus(true,true) + end + end, false) + + RegisterKeyMapping( + "fuel_focus", -- command triggered by key + "Focus Fuel Chart UI", -- description in keybindings + "keyboard", + Config.FuelConsumptionChart.focusShortcut + ) + + function toggleFuelConsumptionChart() + loadNuiVariables() + if isFuelConsumptionChartOpen then + closeFuelConsumptionChartUI() + else + local ped = PlayerPedId() + if not IsPedInAnyVehicle(ped, false) then + exports['lc_utils']:notify("error",Utils.translate("vehicle_not_found")) + return + end + local vehicle = GetVehiclePedIsIn(ped, false) + if GetPedInVehicleSeat(vehicle, -1) ~= ped or IsVehicleBlacklisted(vehicle) then + exports['lc_utils']:notify("error",Utils.translate("vehicle_not_found")) + return + end + + SendNUIMessage({ + showFuelConsumptionChart = true, + isRecording = isRecording, + position = Config.FuelConsumptionChart.position, + focusShortcut = Config.FuelConsumptionChart.focusShortcut, + }) + isFuelConsumptionChartOpen = true + end + end + + function updateFuelConsumptionChart(fuelConsumptionData) + SendNUIMessage({ + updateFuelConsumptionChart = true, + fuelConsumptionData = fuelConsumptionData, + }) + end + + function closeFuelConsumptionChartUI() + SendNUIMessage({ + hideFuelConsumptionChart = true, + }) + isFuelConsumptionChartOpen = false + SetNuiFocus(false,false) + end + + function storeDataForChart(vehicle, newFuelLevel, currentConsumption) + if not isRecording then + updateFuelConsumptionChart({ fuel = nil, speed = nil, consumption = nil }) + return + end + + local speed = GetEntitySpeed(vehicle) * 3.6 + if isFuelConsumptionChartOpen then + updateFuelConsumptionChart({ fuel = newFuelLevel, speed = speed, consumption = currentConsumption }) + end + end end \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/client/client_fuel_type.lua b/resources/[carscripts]/lc_fuel/client/client_fuel_type.lua new file mode 100644 index 000000000..6ab57642b --- /dev/null +++ b/resources/[carscripts]/lc_fuel/client/client_fuel_type.lua @@ -0,0 +1,10 @@ +RegisterCommand(Config.FuelTypeCommand, function() + local vehicle = GetVehiclePedIsIn(PlayerPedId(), false) + if not DoesEntityExist(vehicle) then + exports['lc_utils']:notify("error", Utils.translate("vehicle_not_found")) + return + end + + local fuelType = getVehicleFuelTypeFromServer(vehicle) + exports['lc_utils']:notify("info", Utils.translate("fuel_types.type_title"):format(Utils.translate("fuel_types."..fuelType))) +end, false) \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/client/client_gas.lua b/resources/[carscripts]/lc_fuel/client/client_gas.lua index 149bf37f5..579d9584d 100644 --- a/resources/[carscripts]/lc_fuel/client/client_gas.lua +++ b/resources/[carscripts]/lc_fuel/client/client_gas.lua @@ -1,140 +1,140 @@ -local customGasPumps = {} ------------------------------------------------------------------------------------------------------------------------------------------ --- Threads ------------------------------------------------------------------------------------------------------------------------------------------ - --- Thread to detect near fuel pumps -function createGasMarkersThread() - CreateThread(function() - while true do - local ped = PlayerPedId() - local playerCoords = GetEntityCoords(ped) - local pump, pumpModel = GetClosestPump(playerCoords, false) - - while pump and pump > 0 and #(playerCoords - GetEntityCoords(pump)) < 2.0 do - playerCoords = GetEntityCoords(ped) - if not mainUiOpen and not DoesEntityExist(fuelNozzle) then - Utils.Markers.showHelpNotification(cachedTranslations.open_refuel, true) - if IsControlJustPressed(0,38) then - clientOpenUI(pump, pumpModel, false) - end - end - Wait(2) - end - Wait(1000) - end - end) -end - -function createGasTargetsThread() - local pumpModels = {} - for _, v in pairs(Config.GasPumpProps) do - table.insert(pumpModels, v.prop) - end - Utils.Target.createTargetForModel(pumpModels,openFuelUICallback,Utils.translate('target.open_refuel'),"fas fa-gas-pump","#a42100",nil,nil,canOpenPumpUiTargetCallback) - - Utils.Target.createTargetForModel(pumpModels,returnNozzle,Utils.translate('target.return_nozzle'),"fas fa-gas-pump","#a42100",nil,nil,canReturnNozzleTargetCallback) -end - -function openFuelUICallback() - local ped = PlayerPedId() - local playerCoords = GetEntityCoords(ped) - local pump, pumpModel = GetClosestPump(playerCoords, false) - if pump then - clientOpenUI(pump, pumpModel, false) - else - exports['lc_utils']:notify("error", Utils.translate("pump_not_found")) - end -end - -function createCustomPumpModelsThread() - for _, pumpConfig in pairs(Config.CustomGasPumpLocations) do - RequestModel(pumpConfig.prop) - - while not HasModelLoaded(pumpConfig.prop) do - Wait(50) - end - - local heading = pumpConfig.location.w + 180.0 - local gasPump = CreateObject(pumpConfig.prop, pumpConfig.location.x, pumpConfig.location.y, pumpConfig.location.z, false, true, true) - SetEntityHeading(gasPump, heading) - FreezeEntityPosition(gasPump, true) - table.insert(customGasPumps, gasPump) - end -end - -AddEventHandler('onResourceStop', function(resourceName) - if GetCurrentResourceName() ~= resourceName then return end - - deleteAllCustomGasPumps() -end) - -function deleteAllCustomGasPumps() - for k, v in ipairs(customGasPumps) do - DeleteEntity(v) - end -end - ------------------------------------------------------------------------------------------------------------------------------------------ --- Jerry Cans ------------------------------------------------------------------------------------------------------------------------------------------ - --- Thread to handle the fuel consumption -function createJerryCanThread() - CreateThread(function() - while true do - Wait(1000) - local ped = PlayerPedId() - if not IsPedInAnyVehicle(ped, false) and GetSelectedPedWeapon(ped) == JERRY_CAN_HASH then - refuelLoop(true) - end - end - end) -end - --- Code to save jerry can ammo in any inventory -local currentWeaponData -function updateWeaponAmmo(ammo) - ammo = math.floor(ammo) -- This is needed or some inventories will break - - if currentWeaponData and currentWeaponData.info and currentWeaponData.info.ammo then - currentWeaponData.info.ammo = ammo - end - - TriggerServerEvent('ox_inventory:updateWeapon', "ammo", ammo) - TriggerServerEvent("weapons:server:UpdateWeaponAmmo", currentWeaponData, ammo) - TriggerServerEvent("qb-weapons:server:UpdateWeaponAmmo", currentWeaponData, ammo) - - if Config.Debug then print("updateWeaponAmmo:ammo",ammo) end - if Config.Debug then Utils.Debug.printTable("updateWeaponAmmo:currentWeaponData",currentWeaponData) end - - local ped = PlayerPedId() - SetPedAmmo(ped, JERRY_CAN_HASH, ammo) -end - -AddEventHandler('weapons:client:SetCurrentWeapon', function(data, bool) - if bool ~= false then - currentWeaponData = data - else - currentWeaponData = {} - end -end) - -AddEventHandler('qb-weapons:client:SetCurrentWeapon', function(data, bool) - if bool ~= false then - currentWeaponData = data - else - currentWeaponData = {} - end -end) - --- Get jerry can ammo by metadata -function getJerryCanAmmo() - if currentWeaponData and currentWeaponData.info and currentWeaponData.info.ammo then - if Config.Debug then print("getJerryCanAmmo:currentWeaponData.info.ammo", currentWeaponData.info.ammo) end - return currentWeaponData.info.ammo - end - local ped = PlayerPedId() - if Config.Debug then print("getJerryCanAmmo:GetAmmoInPedWeapon", GetAmmoInPedWeapon(ped, JERRY_CAN_HASH)) end - return GetAmmoInPedWeapon(ped, JERRY_CAN_HASH) +local customGasPumps = {} +----------------------------------------------------------------------------------------------------------------------------------------- +-- Threads +----------------------------------------------------------------------------------------------------------------------------------------- + +-- Thread to detect near fuel pumps +function createGasMarkersThread() + CreateThread(function() + while true do + local ped = PlayerPedId() + local playerCoords = GetEntityCoords(ped) + local pump, pumpModel = GetClosestPump(playerCoords, false) + + while pump and pump > 0 and #(playerCoords - GetEntityCoords(pump)) < 2.0 do + playerCoords = GetEntityCoords(ped) + if not mainUiOpen and not DoesEntityExist(fuelNozzle) then + Utils.Markers.showHelpNotification(cachedTranslations.open_refuel, true) + if IsControlJustPressed(0,38) then + clientOpenUI(pump, pumpModel, false) + end + end + Wait(2) + end + Wait(1000) + end + end) +end + +function createGasTargetsThread() + local pumpModels = {} + for _, v in pairs(Config.GasPumpProps) do + table.insert(pumpModels, v.prop) + end + Utils.Target.createTargetForModel(pumpModels,openFuelUICallback,Utils.translate('target.open_refuel'),"fas fa-gas-pump","#a42100",nil,nil,canOpenPumpUiTargetCallback) + + Utils.Target.createTargetForModel(pumpModels,returnNozzle,Utils.translate('target.return_nozzle'),"fas fa-gas-pump","#a42100",nil,nil,canReturnNozzleTargetCallback) +end + +function openFuelUICallback() + local ped = PlayerPedId() + local playerCoords = GetEntityCoords(ped) + local pump, pumpModel = GetClosestPump(playerCoords, false) + if pump then + clientOpenUI(pump, pumpModel, false) + else + exports['lc_utils']:notify("error", Utils.translate("pump_not_found")) + end +end + +function createCustomPumpModelsThread() + for _, pumpConfig in pairs(Config.CustomGasPumpLocations) do + RequestModel(pumpConfig.prop) + + while not HasModelLoaded(pumpConfig.prop) do + Wait(50) + end + + local heading = pumpConfig.location.w + 180.0 + local gasPump = CreateObject(pumpConfig.prop, pumpConfig.location.x, pumpConfig.location.y, pumpConfig.location.z, false, true, true) + SetEntityHeading(gasPump, heading) + FreezeEntityPosition(gasPump, true) + table.insert(customGasPumps, gasPump) + end +end + +AddEventHandler('onResourceStop', function(resourceName) + if GetCurrentResourceName() ~= resourceName then return end + + deleteAllCustomGasPumps() +end) + +function deleteAllCustomGasPumps() + for k, v in ipairs(customGasPumps) do + DeleteEntity(v) + end +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Jerry Cans +----------------------------------------------------------------------------------------------------------------------------------------- + +-- Thread to handle the fuel consumption +function createJerryCanThread() + CreateThread(function() + while true do + Wait(1000) + local ped = PlayerPedId() + if not IsPedInAnyVehicle(ped, false) and GetSelectedPedWeapon(ped) == JERRY_CAN_HASH then + refuelLoop(true) + end + end + end) +end + +-- Code to save jerry can ammo in any inventory +local currentWeaponData +function updateWeaponAmmo(ammo) + ammo = math.floor(ammo) -- This is needed or some inventories will break + + if currentWeaponData and currentWeaponData.info and currentWeaponData.info.ammo then + currentWeaponData.info.ammo = ammo + end + + TriggerServerEvent('ox_inventory:updateWeapon', "ammo", ammo) + TriggerServerEvent("weapons:server:UpdateWeaponAmmo", currentWeaponData, ammo) + TriggerServerEvent("qb-weapons:server:UpdateWeaponAmmo", currentWeaponData, ammo) + + if Config.Debug then print("updateWeaponAmmo:ammo",ammo) end + if Config.Debug then Utils.Debug.printTable("updateWeaponAmmo:currentWeaponData",currentWeaponData) end + + local ped = PlayerPedId() + SetPedAmmo(ped, JERRY_CAN_HASH, ammo) +end + +AddEventHandler('weapons:client:SetCurrentWeapon', function(data, bool) + if bool ~= false then + currentWeaponData = data + else + currentWeaponData = {} + end +end) + +AddEventHandler('qb-weapons:client:SetCurrentWeapon', function(data, bool) + if bool ~= false then + currentWeaponData = data + else + currentWeaponData = {} + end +end) + +-- Get jerry can ammo by metadata +function getJerryCanAmmo() + if currentWeaponData and currentWeaponData.info and currentWeaponData.info.ammo then + if Config.Debug then print("getJerryCanAmmo:currentWeaponData.info.ammo", currentWeaponData.info.ammo) end + return currentWeaponData.info.ammo + end + local ped = PlayerPedId() + if Config.Debug then print("getJerryCanAmmo:GetAmmoInPedWeapon", GetAmmoInPedWeapon(ped, JERRY_CAN_HASH)) end + return GetAmmoInPedWeapon(ped, JERRY_CAN_HASH) end \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/client/client_refuel.lua b/resources/[carscripts]/lc_fuel/client/client_refuel.lua index 0e02abff9..5a1100ce9 100644 --- a/resources/[carscripts]/lc_fuel/client/client_refuel.lua +++ b/resources/[carscripts]/lc_fuel/client/client_refuel.lua @@ -1,477 +1,497 @@ -local refuelingThread = nil -local isRefuelling = false -local inCooldown = false -local vehicleAttachedToNozzle = nil -local remainingFuelToRefuel = 0 -local currentFuelTypePurchased = nil -local distanceToCap, distanceToPump = math.maxinteger, math.maxinteger -local litersDeductedEachTick = 0.5 - ------------------------------------------------------------------------------------------------------------------------------------------ --- Refuelling ------------------------------------------------------------------------------------------------------------------------------------------ - -RegisterNetEvent('lc_fuel:getPumpNozzle') -AddEventHandler('lc_fuel:getPumpNozzle', function(fuelAmountPurchased, fuelTypePurchased) - closeUI() - if DoesEntityExist(fuelNozzle) then return end - if not currentPump then return end - local ped = PlayerPedId() - local pumpCoords = GetEntityCoords(currentPump) - - -- Animate the ped to grab the nozzle - Utils.Animations.loadAnimDict("anim@am_hold_up@male") - TaskPlayAnim(ped, "anim@am_hold_up@male", "shoplift_high", 2.0, 8.0, -1, 50, 0, false, false, false) - Wait(300) - StopAnimTask(ped, "anim@am_hold_up@male", "shoplift_high", 1.0) - - -- Spawn the nozzle - fuelNozzle = createFuelNozzleObject(fuelTypePurchased) - - -- Attach the nozzle - attachNozzleToPed() - if Config.EnablePumpRope then - fuelRope = CreateRopeToPump(pumpCoords) - end - - -- Get the max distance the player can go with the nozzle - local ropeLength = getNearestPumpRopeLength(fuelTypePurchased, pumpCoords) - - -- Thread to handle fuel nozzle - CreateThread(function() - while DoesEntityExist(fuelNozzle) do - local waitTime = 500 - local nozzleCoords = GetEntityCoords(fuelNozzle) -- Conside the nozzle position, not the ped - distanceToPump = #(pumpCoords - nozzleCoords) - -- If player reach the distance limit delete the nozzle - if distanceToPump > ropeLength then - exports['lc_utils']:notify("error", Utils.translate("too_far_away")) - deleteRopeAndNozzleProp() - end - -- If player is near the distance limit, show a notification to him - if distanceToPump > (ropeLength * 0.7) then - Utils.Markers.showHelpNotification(Utils.translate("too_far_away"), true) - end - -- Check if ped entered a vehicle - if IsPedSittingInAnyVehicle(ped) then - -- Gives him 2 seconds to leave before clearing the nozzle - SetTimeout(2000,function() - if IsPedSittingInAnyVehicle(ped) and DoesEntityExist(fuelNozzle) then - exports['lc_utils']:notify("error", Utils.translate("too_far_away")) - deleteRopeAndNozzleProp() - end - end) - end - if Utils.Config.custom_scripts_compatibility.target == "disabled" and distanceToPump < 1.5 then - waitTime = 2 - Utils.Markers.showHelpNotification(cachedTranslations.return_nozzle, true) - if IsControlJustPressed(0,38) then - -- See which one the player is nearer. The fuel cap or fuel pump - if distanceToPump < distanceToCap then - -- Avoid player press E to return nozzle and press E to refuel in same tick, so it gives preference to refuel - Wait(100) - returnNozzle() - end - end - end - Wait(waitTime) - end - -- Not near the pump anymore - distanceToPump = math.maxinteger - end) - - -- Thread to refuel the vehicle - CreateThread(function() - -- Set the fuel purchased in a global variable - remainingFuelToRefuel = fuelAmountPurchased - -- Set the fuel type in a global variable - currentFuelTypePurchased = fuelTypePurchased - -- Trigger the function to allow refuel on markers - if Utils.Config.custom_scripts_compatibility.target == "disabled" then - refuelLoop(false) - end - -- Not near the fuel cap anymore - distanceToCap = math.maxinteger - end) -end) - -function returnNozzle() - local ped = PlayerPedId() - - if not isRefuelling then - Utils.Animations.loadAnimDict("anim@am_hold_up@male") - TaskPlayAnim(ped, "anim@am_hold_up@male", "shoplift_high", 2.0, 8.0, -1, 50, 0, false, false, false) - Wait(300) - StopAnimTask(ped, "anim@am_hold_up@male", "shoplift_high", 1.0) - deleteRopeAndNozzleProp() - - if Config.ReturnNozzleRefund then - local isElectric = Utils.Table.contains({"electricnormal", "electricfast"}, currentFuelTypePurchased) - TriggerServerEvent('lc_fuel:returnNozzle', remainingFuelToRefuel, isElectric) - end - end -end - -function executeRefuelAction(isFromJerryCan, closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters) - if Config.Debug then print("executeRefuelAction:p", isFromJerryCan, closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters) end - local ped = PlayerPedId() - local refuelTick = Config.RefuelTick - local isElectric = false - local fuelTypePurchased = currentFuelTypePurchased - - -- Change the fuel tick if its electric charging - if fuelTypePurchased == "electricfast" then - isElectric = true - refuelTick = Config.Electric.chargeTypes.fast.time * 1000 / 2 -- Divide by 2 because each tick adds 0.5kWh. - end - if fuelTypePurchased == "electricnormal" then - isElectric = true - refuelTick = Config.Electric.chargeTypes.normal.time * 1000 / 2 - end - - local animationDuration = 1000 -- 1 sec - if isFromJerryCan then - animationDuration = -1 -- (infinite) Do not allow the player walk during refuel from jerry can - end - - -- Do not allow user mix electric and petrol fuel/vehicles - if (isElectric and Config.Electric.vehiclesListHash[closestVehicleHash]) or (not isElectric and not Config.Electric.vehiclesListHash[closestVehicleHash]) then - if not isRefuelling and not vehicleAttachedToNozzle then - if remainingFuelToRefuel > 0 then - -- Reset the vehicle fuel to 0 when refueling with a different fuel type - if not isFromJerryCan and not isElectric then - local fuelType = getVehicleFuelTypeFromServer(closestVehicle) - if fuelTypePurchased ~= fuelType then - changeVehicleFuelType(closestVehicle, fuelTypePurchased) - end - end - isRefuelling = true - - -- Animate the ped - TaskTurnPedToFaceCoord(ped, closestCapPos.x, closestCapPos.y, closestCapPos.z, animationDuration) - Utils.Animations.loadAnimDict("weapons@misc@jerrycan@") - TaskPlayAnim(ped, "weapons@misc@jerrycan@", "fire", 2.0, 8.0, animationDuration, 50, 0, false, false, false) - - -- Plug the nozzle in the car - attachNozzleToVehicle(closestVehicle, customVehicleParameters) - - -- Refuel the vehicle - refuelingThread = CreateThread(function() - local vehicleToRefuel = closestVehicle - local startingFuel = GetFuel(vehicleToRefuel) -- Get vehicle fuel level - local vehicleTankSize = getVehicleTankSize(vehicleToRefuel) - - local currentFuel = startingFuel - -- Loop keep happening while the player has not canceled, while the fuelNozzle exists and while the ped still has jerry can in hands - while isRefuelling and (DoesEntityExist(fuelNozzle) or (isFromJerryCan and GetSelectedPedWeapon(ped) == JERRY_CAN_HASH)) do - currentFuel = GetFuel(vehicleToRefuel) - local percentageOfFuelToAdd = calculateFuelToAddPercentage(vehicleTankSize) -- Add 0.5L each tick, but the % is proportional to the vehicle tank - if currentFuel + percentageOfFuelToAdd > 100 then - -- Increase the vehicle fuel level - percentageOfFuelToAdd = 100 - currentFuel - end - if remainingFuelToRefuel < litersDeductedEachTick then - -- Break when the user has used all the fuel he paid for - break - end - if percentageOfFuelToAdd <= 0.01 then - -- Break when the vehicle tank is full - exports['lc_utils']:notify("info", Utils.translate("vehicle_tank_full")) - break - end - -- Decrease the purchased fuel amount and increase the vehicle fuel level - remainingFuelToRefuel = remainingFuelToRefuel - litersDeductedEachTick - currentFuel = currentFuel + percentageOfFuelToAdd - SetFuel(vehicleToRefuel, currentFuel) - SendNUIMessage({ - showRefuelDisplay = true, - remainingFuelAmount = remainingFuelToRefuel, - currentVehicleTankSize = vehicleTankSize, - currentDisplayFuelAmount = getVehicleDisplayFuelAmount(currentFuel, vehicleTankSize), - isElectric = isElectric, - fuelTypePurchased = fuelTypePurchased - }) - if Config.Debug then print("executeRefuelAction:remainingFuelToRefuel", remainingFuelToRefuel) end - Wait(refuelTick) - end - if isFromJerryCan then - -- Update the jerry can ammo - SetPedAmmo(ped, JERRY_CAN_HASH, remainingFuelToRefuel) - updateWeaponAmmo(remainingFuelToRefuel) - vehicleAttachedToNozzle = nil - end - if isElectric then - exports['lc_utils']:notify("success", Utils.translate("vehicle_recharged"):format(Utils.Math.round(getVehicleDisplayFuelAmount(currentFuel, vehicleTankSize) - getVehicleDisplayFuelAmount(startingFuel, vehicleTankSize), 1))) - else - exports['lc_utils']:notify("success", Utils.translate("vehicle_refueled"):format(Utils.Math.round(getVehicleDisplayFuelAmount(currentFuel, vehicleTankSize) - getVehicleDisplayFuelAmount(startingFuel, vehicleTankSize), 1))) - end - - -- Stop refuelling - stopRefuelAnimation() - SendNUIMessage({ hideRefuelDisplay = true }) - isRefuelling = false - end) - else - exports['lc_utils']:notify("error", Utils.translate("not_enough_refuel")) - end - else - -- Terminate refuelling - stopRefuelAction() - -- Cooldown to prevent the user to spam E and glitch things - inCooldown = true - SetTimeout(refuelTick + 1,function() - inCooldown = false - end) - end - else - exports['lc_utils']:notify("error", Utils.translate("incompatible_fuel")) - end -end - -function calculateFuelToAddPercentage(totalVolumeLiters) - local percentage = (litersDeductedEachTick / totalVolumeLiters) * 100 - return percentage -end - - ------------------------------------------------------------------------------------------------------------------------------------------ --- Markers ------------------------------------------------------------------------------------------------------------------------------------------ - -function refuelLoop(isFromJerryCan) - -- Load variables to open te UI - loadNuiVariables() - - local ped = PlayerPedId() - local closestCapPos - local closestVehicle - local customVehicleParameters - local closestVehicleHash - - if isFromJerryCan then - remainingFuelToRefuel = getJerryCanAmmo() - end - - isRefuelling = false - while DoesEntityExist(fuelNozzle) or (isFromJerryCan and GetSelectedPedWeapon(ped) == JERRY_CAN_HASH) do - local waitTime = 200 - if closestCapPos then - distanceToCap = #(GetEntityCoords(ped) - vector3(closestCapPos.x,closestCapPos.y,closestCapPos.z + customVehicleParameters.nozzleOffset.up + 0.0)) - if distanceToCap < customVehicleParameters.distance + 0.0 and (not vehicleAttachedToNozzle or (vehicleAttachedToNozzle and DoesEntityExist(vehicleAttachedToNozzle) and vehicleAttachedToNozzle == closestVehicle)) then - waitTime = 1 - Utils.Markers.drawText3D(closestCapPos.x,closestCapPos.y,closestCapPos.z + customVehicleParameters.nozzleOffset.up + 0.0, cachedTranslations.interact_with_vehicle) - if IsControlJustPressed(0, 38) and not inCooldown then - -- See which one the player is nearer. The fuel cap or fuel pump - if distanceToPump >= distanceToCap then - executeRefuelAction(isFromJerryCan, closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters) - end - end - else - -- Player is not near the cap, set it to null to find it again later - closestCapPos = nil - end - else - -- Find the nearest vehicle and cap pos - closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters = getClosestVehicleVariables() - end - Wait(waitTime) - end - - terminateRefuelThread() -end - ------------------------------------------------------------------------------------------------------------------------------------------ --- Target ------------------------------------------------------------------------------------------------------------------------------------------ - -function createTargetForVehicleIteraction() - local attachParams = { - labelText = Utils.translate("target.start_refuel"), - icon = "fas fa-gas-pump", - iconColor = "#2986cc", - zone_id = "start_refuel", - distance = 2.0 - } - - Utils.Target.createTargetForBone(vehicleCapBoneList(),attachParams,executeRefuelActionFromTarget,nil,canAttachNozzleTargetCallback) - - local detachParams = { - labelText = Utils.translate("target.stop_refuel"), - icon = "fas fa-gas-pump", - iconColor = "#2986cc", - zone_id = "stop_refuel", - distance = 2.0 - } - Utils.Target.createTargetForBone(vehicleCapBoneList(),detachParams,stopRefuelAction,nil,canDetachNozzleTargetCallback) -end - -function executeRefuelActionFromTarget() - -- Load variables to open te UI - loadNuiVariables() - - local ped = PlayerPedId() - - -- Calculate if player is holding a jerry can - local isFromJerryCan = false - if not IsPedInAnyVehicle(ped, false) and GetSelectedPedWeapon(ped) == JERRY_CAN_HASH then - isFromJerryCan = true - remainingFuelToRefuel = getJerryCanAmmo() - if Config.Debug then print("executeRefuelActionFromTarget:remainingFuelToRefuel",remainingFuelToRefuel) end - end - - local closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters = getClosestVehicleVariables() - executeRefuelAction(isFromJerryCan, closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters) -end - -function canAttachNozzleTargetCallback(entity, distance) - local ped = PlayerPedId() - if (DoesEntityExist(fuelNozzle) or GetSelectedPedWeapon(ped) == JERRY_CAN_HASH) - and not isRefuelling - and not vehicleAttachedToNozzle then - return true - end - return false -end - -function canDetachNozzleTargetCallback(entity, distance) - local ped = PlayerPedId() - if (DoesEntityExist(fuelNozzle) or GetSelectedPedWeapon(ped) == JERRY_CAN_HASH) - and vehicleAttachedToNozzle then - return true - end - return false -end - -function canOpenPumpUiTargetCallback() - return not DoesEntityExist(fuelNozzle) -end - -function canReturnNozzleTargetCallback() - return DoesEntityExist(fuelNozzle) -end - ------------------------------------------------------------------------------------------------------------------------------------------ --- Utils ------------------------------------------------------------------------------------------------------------------------------------------ - -function getClosestVehicleVariables() - -- Get the closest vehicle and its cap pos - local closestVehicle = GetClosestVehicle() - local closestCapPos = GetVehicleCapPos(closestVehicle) - local closestVehicleHash = GetEntityModel(closestVehicle) - local customVehicleParameters = (Config.CustomVehicleParametersHash[closestVehicleHash] or Config.CustomVehicleParametersHash.default or { distance = 1.2, nozzleOffset = { forward = 0.0, right = -0.15, up = 0.5 }, nozzleRotation = { x = 0, y = 0, z = 0} }) - if not closestCapPos then - print("Cap not found for vehicle") - end - return closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters -end - -function terminateRefuelThread() - -- Stop the refueling process - if refuelingThread and IsThreadActive(refuelingThread) then - TerminateThread(refuelingThread) - refuelingThread = nil - end -end - -function stopRefuelAnimation() - local ped = PlayerPedId() - ClearPedTasks(ped) - RemoveAnimDict("weapons@misc@jerrycan@") -end - -function stopRefuelAction() - -- Stop refuelling - stopRefuelAnimation() - SendNUIMessage({ hideRefuelDisplay = true }) - attachNozzleToPed() - isRefuelling = false -end - -function attachNozzleToVehicle(closestVehicle, customVehicleParameters) - DetachEntity(fuelNozzle, true, true) - - -- Find the appropriate bone for the fuel cap - local tankBones = vehicleCapBoneList() - local boneIndex = -1 - - for _, boneName in ipairs(tankBones) do - boneIndex = GetEntityBoneIndexByName(closestVehicle, boneName) - if boneIndex ~= -1 then - break - end - end - - if boneIndex ~= -1 then - local vehicleRotation = GetEntityRotation(closestVehicle) - local forwardVector, rightVector, upVector, _ = GetEntityMatrix(closestVehicle) - - -- Adjust the offsets - local forwardOffset = forwardVector * customVehicleParameters.nozzleOffset.forward - local rightoffset = rightVector * customVehicleParameters.nozzleOffset.right - local upOffset = upVector * customVehicleParameters.nozzleOffset.up - local finalOffset = forwardOffset + rightoffset + upOffset - - -- Adjust the rotation - local nozzleRotation = customVehicleParameters.nozzleRotation or { x = 0, y = 0, z = 0 } - local finalRotationX = vehicleRotation.x + nozzleRotation.x - local finalRotationY = vehicleRotation.y + nozzleRotation.y - local finalRotationZ = vehicleRotation.z + nozzleRotation.z - - -- Attach the nozzle to the vehicle's fuel cap bone with the calculated rotation - AttachEntityToEntity(fuelNozzle, closestVehicle, boneIndex, finalOffset.x, finalOffset.y, finalOffset.z, finalRotationX - 45, finalRotationY, finalRotationZ - 90, false, false, false, false, 2, false) - else - print("No valid fuel cap bone found on the vehicle.") - end - - -- Set the global variable to indicate the vehicle attached to nozzle - vehicleAttachedToNozzle = closestVehicle -end - -function attachNozzleToPed() - DetachEntity(fuelNozzle, true, true) - - local ped = PlayerPedId() - local pedBone = GetPedBoneIndex(ped, 18905) - AttachEntityToEntity(fuelNozzle, ped, pedBone, 0.13, 0.04, 0.01, -42.0, -115.0, -63.42, false, true, false, true, 0, true) - - vehicleAttachedToNozzle = nil -end - -function getNearestPumpRopeLength(fuelTypePurchased, pumpCoords) - local distanceToFindPump = 10 - local ropeLength = Config.DefaultRopeLength - if fuelTypePurchased == "electricfast" or fuelTypePurchased == "electricnormal" then - for _, pumpConfig in pairs(Config.Electric.chargersLocation) do - local distance = #(vector3(pumpConfig.location.x, pumpConfig.location.y, pumpConfig.location.z) - pumpCoords) - if distance < distanceToFindPump then - ropeLength = pumpConfig.ropeLength - break - end - end - else - for _, pumpConfig in pairs(Config.CustomGasPumpLocations) do - local distance = #(vector3(pumpConfig.location.x, pumpConfig.location.y, pumpConfig.location.z) - pumpCoords) - if distance < distanceToFindPump then - ropeLength = pumpConfig.ropeLength - break - end - end - end - return ropeLength -end - -function createFuelNozzleObject(fuelTypePurchased) - local nozzle_prop_label = Config.NozzleProps.gas - -- Change the nozzle prop to electric - if fuelTypePurchased == "electricfast" or fuelTypePurchased == "electricnormal" then - nozzle_prop_label = Config.NozzleProps.electric - end - - RequestModel(nozzle_prop_label) - while not HasModelLoaded(nozzle_prop_label) do - Wait(50) - end - - return CreateObject(joaat(nozzle_prop_label), 1.0, 1.0, 1.0, true, true, false) +local refuelingThread = nil +local isRefuelling = false +local inCooldown = false +local vehicleAttachedToNozzle = nil +local remainingFuelToRefuel = 0 +local currentFuelTypePurchased = nil +local distanceToCap, distanceToPump = math.maxinteger, math.maxinteger +local litersDeductedEachTick = 0.5 + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Refuelling +----------------------------------------------------------------------------------------------------------------------------------------- + +RegisterNetEvent('lc_fuel:getPumpNozzle') +AddEventHandler('lc_fuel:getPumpNozzle', function(fuelAmountPurchased, fuelTypePurchased) + closeUI() + if DoesEntityExist(fuelNozzle) then return end + if not currentPump then return end + local ped = PlayerPedId() + local pumpCoords = GetEntityCoords(currentPump) + + -- Animate the ped to grab the nozzle + Utils.Animations.loadAnimDict("anim@am_hold_up@male") + TaskPlayAnim(ped, "anim@am_hold_up@male", "shoplift_high", 2.0, 8.0, -1, 50, 0, false, false, false) + Wait(300) + StopAnimTask(ped, "anim@am_hold_up@male", "shoplift_high", 1.0) + + -- Spawn the nozzle + fuelNozzle = createFuelNozzleObject(fuelTypePurchased) + + -- Attach the nozzle + attachNozzleToPed() + if Config.EnablePumpRope then + fuelRope = CreateRopeToPump(pumpCoords) + end + + -- Get the max distance the player can go with the nozzle + local ropeLength = getNearestPumpRopeLength(fuelTypePurchased, pumpCoords) + + -- Thread to handle fuel nozzle + CreateThread(function() + while DoesEntityExist(fuelNozzle) do + local waitTime = 500 + local nozzleCoords = GetEntityCoords(fuelNozzle) -- Conside the nozzle position, not the ped + distanceToPump = #(pumpCoords - nozzleCoords) + -- If player reach the distance limit delete the nozzle + if distanceToPump > ropeLength then + exports['lc_utils']:notify("error", Utils.translate("too_far_away")) + deleteRopeAndNozzleProp() + end + -- If player is near the distance limit, show a notification to him + if distanceToPump > (ropeLength * 0.7) then + Utils.Markers.showHelpNotification(Utils.translate("too_far_away"), true) + end + -- Check if ped entered a vehicle + if IsPedSittingInAnyVehicle(ped) then + -- Gives him 2 seconds to leave before clearing the nozzle + SetTimeout(2000,function() + if IsPedSittingInAnyVehicle(ped) and DoesEntityExist(fuelNozzle) then + exports['lc_utils']:notify("error", Utils.translate("too_far_away")) + deleteRopeAndNozzleProp() + end + end) + end + if Utils.Config.custom_scripts_compatibility.target == "disabled" and distanceToPump < 1.5 then + waitTime = 2 + Utils.Markers.showHelpNotification(cachedTranslations.return_nozzle, true) + if IsControlJustPressed(0,38) then + -- See which one the player is nearer. The fuel cap or fuel pump + if distanceToPump < distanceToCap then + -- Avoid player press E to return nozzle and press E to refuel in same tick, so it gives preference to refuel + Wait(100) + returnNozzle() + end + end + end + Wait(waitTime) + end + -- Not near the pump anymore + distanceToPump = math.maxinteger + end) + + -- Thread to refuel the vehicle + CreateThread(function() + -- Set the fuel purchased in a global variable + remainingFuelToRefuel = fuelAmountPurchased + -- Set the fuel type in a global variable + currentFuelTypePurchased = fuelTypePurchased + -- Trigger the function to allow refuel on markers + if Utils.Config.custom_scripts_compatibility.target == "disabled" then + refuelLoop(false) + end + -- Not near the fuel cap anymore + distanceToCap = math.maxinteger + end) +end) + +function returnNozzle() + local ped = PlayerPedId() + + if not isRefuelling then + Utils.Animations.loadAnimDict("anim@am_hold_up@male") + TaskPlayAnim(ped, "anim@am_hold_up@male", "shoplift_high", 2.0, 8.0, -1, 50, 0, false, false, false) + Wait(300) + StopAnimTask(ped, "anim@am_hold_up@male", "shoplift_high", 1.0) + deleteRopeAndNozzleProp() + + if Config.ReturnNozzleRefund then + local isElectric = Utils.Table.contains({"electricnormal", "electricfast"}, currentFuelTypePurchased) + TriggerServerEvent('lc_fuel:returnNozzle', remainingFuelToRefuel, isElectric) + end + end +end + +function executeRefuelAction(isFromJerryCan, closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters) + if Config.Debug then print("executeRefuelAction:p", isFromJerryCan, closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters) end + local ped = PlayerPedId() + local refuelTick = Config.RefuelTick + local isElectric = false + local fuelTypePurchased = currentFuelTypePurchased + + -- Change the fuel tick if its electric charging + if fuelTypePurchased == "electricfast" then + isElectric = true + refuelTick = Config.Electric.chargeTypes.fast.time * 1000 / 2 -- Divide by 2 because each tick adds 0.5kWh. + end + if fuelTypePurchased == "electricnormal" then + isElectric = true + refuelTick = Config.Electric.chargeTypes.normal.time * 1000 / 2 + end + + local animationDuration = 1000 -- 1 sec + if isFromJerryCan then + animationDuration = -1 -- (infinite) Do not allow the player walk during refuel from jerry can + end + + -- Do not allow user mix electric and petrol fuel/vehicles + if (isElectric and Config.Electric.vehiclesListHash[closestVehicleHash]) or (not isElectric and not Config.Electric.vehiclesListHash[closestVehicleHash]) then + if not isRefuelling and not vehicleAttachedToNozzle then + if remainingFuelToRefuel > 0 then + -- Reset the vehicle fuel to 0 when refueling with a different fuel type + if not isFromJerryCan and not isElectric then + local fuelType = getVehicleFuelTypeFromServer(closestVehicle) + if fuelTypePurchased ~= fuelType then + changeVehicleFuelType(closestVehicle, fuelTypePurchased) + end + end + isRefuelling = true + + -- Animate the ped + TaskTurnPedToFaceCoord(ped, closestCapPos.x, closestCapPos.y, closestCapPos.z, animationDuration) + Utils.Animations.loadAnimDict("weapons@misc@jerrycan@") + TaskPlayAnim(ped, "weapons@misc@jerrycan@", "fire", 2.0, 8.0, animationDuration, 50, 0, false, false, false) + + -- Plug the nozzle in the car + attachNozzleToVehicle(closestVehicle, customVehicleParameters) + + -- Refuel the vehicle + refuelingThread = CreateThread(function() + local vehicleToRefuel = closestVehicle + local startingFuel = GetFuel(vehicleToRefuel) -- Get vehicle fuel level + local vehicleTankSize = getVehicleTankSize(vehicleToRefuel) + + local currentFuel = startingFuel + -- Loop keep happening while the player has not canceled, while the fuelNozzle exists and while the ped still has jerry can in hands + while isRefuelling and (DoesEntityExist(fuelNozzle) or (isFromJerryCan and GetSelectedPedWeapon(ped) == JERRY_CAN_HASH)) do + currentFuel = GetFuel(vehicleToRefuel) + local percentageOfFuelToAdd = calculateFuelToAddPercentage(vehicleTankSize) -- Add 0.5L each tick, but the % is proportional to the vehicle tank + if currentFuel + percentageOfFuelToAdd > 100 then + -- Increase the vehicle fuel level + percentageOfFuelToAdd = 100 - currentFuel + end + if remainingFuelToRefuel < litersDeductedEachTick then + -- Break when the user has used all the fuel he paid for + break + end + if percentageOfFuelToAdd <= 0.01 then + -- Break when the vehicle tank is full + exports['lc_utils']:notify("info", Utils.translate("vehicle_tank_full")) + break + end + -- Decrease the purchased fuel amount and increase the vehicle fuel level + remainingFuelToRefuel = remainingFuelToRefuel - litersDeductedEachTick + currentFuel = currentFuel + percentageOfFuelToAdd + SetFuel(vehicleToRefuel, currentFuel) + SendNUIMessage({ + showRefuelDisplay = true, + remainingFuelAmount = remainingFuelToRefuel, + currentVehicleTankSize = vehicleTankSize, + currentDisplayFuelAmount = getVehicleDisplayFuelAmount(currentFuel, vehicleTankSize), + isElectric = isElectric, + fuelTypePurchased = fuelTypePurchased + }) + if Config.Debug then print("executeRefuelAction:remainingFuelToRefuel", remainingFuelToRefuel) end + Wait(refuelTick) + end + if isFromJerryCan then + -- Update the jerry can ammo + SetPedAmmo(ped, JERRY_CAN_HASH, remainingFuelToRefuel) + updateWeaponAmmo(remainingFuelToRefuel) + vehicleAttachedToNozzle = nil + end + if isElectric then + exports['lc_utils']:notify("success", Utils.translate("vehicle_recharged"):format(Utils.Math.round(getVehicleDisplayFuelAmount(currentFuel, vehicleTankSize) - getVehicleDisplayFuelAmount(startingFuel, vehicleTankSize), 1))) + else + exports['lc_utils']:notify("success", Utils.translate("vehicle_refueled"):format(Utils.Math.round(getVehicleDisplayFuelAmount(currentFuel, vehicleTankSize) - getVehicleDisplayFuelAmount(startingFuel, vehicleTankSize), 1))) + end + + -- Stop refuelling + stopRefuelAnimation() + SendNUIMessage({ hideRefuelDisplay = true }) + isRefuelling = false + end) + else + exports['lc_utils']:notify("error", Utils.translate("not_enough_refuel")) + end + else + -- Terminate refuelling + stopRefuelAction() + -- Cooldown to prevent the user to spam E and glitch things + inCooldown = true + SetTimeout(refuelTick + 1,function() + inCooldown = false + end) + end + else + exports['lc_utils']:notify("error", Utils.translate("incompatible_fuel")) + end +end + +function calculateFuelToAddPercentage(totalVolumeLiters) + local percentage = (litersDeductedEachTick / totalVolumeLiters) * 100 + return percentage +end + + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Markers +----------------------------------------------------------------------------------------------------------------------------------------- + +function refuelLoop(isFromJerryCan) + -- Load variables to open te UI + loadNuiVariables() + + local ped = PlayerPedId() + local closestCapPos + local closestVehicle + local customVehicleParameters + local closestVehicleHash + + if isFromJerryCan then + remainingFuelToRefuel = getJerryCanAmmo() + end + + isRefuelling = false + while DoesEntityExist(fuelNozzle) or (isFromJerryCan and GetSelectedPedWeapon(ped) == JERRY_CAN_HASH) do + local waitTime = 200 + if closestCapPos then + distanceToCap = #(GetEntityCoords(ped) - vector3(closestCapPos.x,closestCapPos.y,closestCapPos.z)) + if distanceToCap < customVehicleParameters.distance + 0.0 and (not vehicleAttachedToNozzle or (vehicleAttachedToNozzle and DoesEntityExist(vehicleAttachedToNozzle) and vehicleAttachedToNozzle == closestVehicle)) then + waitTime = 1 + Utils.Markers.drawText3D(closestCapPos.x,closestCapPos.y,closestCapPos.z, cachedTranslations.interact_with_vehicle) + if IsControlJustPressed(0, 38) and not inCooldown then + -- See which one the player is nearer. The fuel cap or fuel pump + if distanceToPump >= distanceToCap then + executeRefuelAction(isFromJerryCan, closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters) + end + end + else + -- Player is not near the cap, set it to null to find it again later + closestCapPos = nil + end + else + -- Find the nearest vehicle and cap pos + closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters = getClosestVehicleVariables() + end + Wait(waitTime) + end + + terminateRefuelThread() +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Target +----------------------------------------------------------------------------------------------------------------------------------------- + +function createTargetForVehicleIteraction() + local attachParams = { + labelText = Utils.translate("target.start_refuel"), + icon = "fas fa-gas-pump", + iconColor = "#2986cc", + zone_id = "start_refuel", + distance = 2.0 + } + + Utils.Target.createTargetForBone(vehicleCapBoneList(),attachParams,executeRefuelActionFromTarget,nil,canAttachNozzleTargetCallback) + + local detachParams = { + labelText = Utils.translate("target.stop_refuel"), + icon = "fas fa-gas-pump", + iconColor = "#2986cc", + zone_id = "stop_refuel", + distance = 2.0 + } + Utils.Target.createTargetForBone(vehicleCapBoneList(),detachParams,stopRefuelAction,nil,canDetachNozzleTargetCallback) +end + +function executeRefuelActionFromTarget() + -- Load variables to open te UI + loadNuiVariables() + + local ped = PlayerPedId() + + -- Calculate if player is holding a jerry can + local isFromJerryCan = false + if not IsPedInAnyVehicle(ped, false) and GetSelectedPedWeapon(ped) == JERRY_CAN_HASH then + isFromJerryCan = true + remainingFuelToRefuel = getJerryCanAmmo() + if Config.Debug then print("executeRefuelActionFromTarget:remainingFuelToRefuel",remainingFuelToRefuel) end + end + + local closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters = getClosestVehicleVariables() + executeRefuelAction(isFromJerryCan, closestVehicle, closestCapPos, closestVehicleHash, customVehicleParameters) +end + +function canAttachNozzleTargetCallback(entity, distance) + local ped = PlayerPedId() + if (DoesEntityExist(fuelNozzle) or GetSelectedPedWeapon(ped) == JERRY_CAN_HASH) + and not isRefuelling + and not vehicleAttachedToNozzle then + return true + end + return false +end + +function canDetachNozzleTargetCallback(entity, distance) + local ped = PlayerPedId() + if (DoesEntityExist(fuelNozzle) or GetSelectedPedWeapon(ped) == JERRY_CAN_HASH) + and vehicleAttachedToNozzle then + return true + end + return false +end + +function canOpenPumpUiTargetCallback() + return not DoesEntityExist(fuelNozzle) +end + +function canReturnNozzleTargetCallback() + return DoesEntityExist(fuelNozzle) +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Utils +----------------------------------------------------------------------------------------------------------------------------------------- + +function getClosestVehicleVariables() + -- Get the closest vehicle and its cap pos + local closestVehicle = GetClosestVehicle() + local closestCapPos = GetVehicleCapPos(closestVehicle) + local closestVehicleHash = GetEntityModel(closestVehicle) + local customVehicleParameters = (Config.CustomVehicleParametersHash[closestVehicleHash] or Config.CustomVehicleParametersHash.default or { distance = 1.2, nozzleOffset = { forward = 0.0, right = -0.15, up = 0.5 }, nozzleRotation = { x = 0, y = 0, z = 0} }) + if not closestCapPos then + print("Cap not found for vehicle") + end + + local finalWorldPos = getWorldPosFromOffset(closestVehicle, customVehicleParameters.nozzleOffset) + + return closestVehicle, finalWorldPos, closestVehicleHash, customVehicleParameters +end + +function getWorldPosFromOffset(vehicle, offset) + local closestCapPos = GetVehicleCapPos(vehicle) + local forwardVector, rightVector, upVector, _ = GetEntityMatrix(vehicle) + + -- Adjust the offsets + local forwardOffset = forwardVector * offset.forward + local rightoffset = rightVector * offset.right + local upOffset = upVector * offset.up + + -- Final world position of the nozzle point + return vector3( + closestCapPos.x + forwardOffset.x + rightoffset.x + upOffset.x, + closestCapPos.y + forwardOffset.y + rightoffset.y + upOffset.y, + closestCapPos.z + forwardOffset.z + rightoffset.z + upOffset.z + ) +end + +function terminateRefuelThread() + -- Stop the refueling process + if refuelingThread and IsThreadActive(refuelingThread) then + TerminateThread(refuelingThread) + refuelingThread = nil + end +end + +function stopRefuelAnimation() + local ped = PlayerPedId() + ClearPedTasks(ped) + RemoveAnimDict("weapons@misc@jerrycan@") +end + +function stopRefuelAction() + -- Stop refuelling + stopRefuelAnimation() + SendNUIMessage({ hideRefuelDisplay = true }) + attachNozzleToPed() + isRefuelling = false +end + +function attachNozzleToVehicle(closestVehicle, customVehicleParameters) + DetachEntity(fuelNozzle, true, true) + + -- Find the appropriate bone for the fuel cap + local tankBones = vehicleCapBoneList() + local boneIndex = -1 + + for _, boneName in ipairs(tankBones) do + boneIndex = GetEntityBoneIndexByName(closestVehicle, boneName) + if boneIndex ~= -1 then + break + end + end + + if boneIndex ~= -1 then + local vehicleRotation = GetEntityRotation(closestVehicle) + local forwardVector, rightVector, upVector, _ = GetEntityMatrix(closestVehicle) + + -- Adjust the offsets + local forwardOffset = forwardVector * customVehicleParameters.nozzleOffset.forward + local rightoffset = rightVector * customVehicleParameters.nozzleOffset.right + local upOffset = upVector * customVehicleParameters.nozzleOffset.up + local finalOffset = forwardOffset + rightoffset + upOffset + + -- Adjust the rotation + local nozzleRotation = customVehicleParameters.nozzleRotation or { x = 0, y = 0, z = 0 } + local finalRotationX = vehicleRotation.x + nozzleRotation.x + local finalRotationY = vehicleRotation.y + nozzleRotation.y + local finalRotationZ = vehicleRotation.z + nozzleRotation.z + + -- Attach the nozzle to the vehicle's fuel cap bone with the calculated rotation + AttachEntityToEntity(fuelNozzle, closestVehicle, boneIndex, finalOffset.x, finalOffset.y, finalOffset.z, finalRotationX - 45, finalRotationY, finalRotationZ - 90, false, false, false, false, 2, false) + else + print("No valid fuel cap bone found on the vehicle.") + end + + -- Set the global variable to indicate the vehicle attached to nozzle + vehicleAttachedToNozzle = closestVehicle +end + +function attachNozzleToPed() + DetachEntity(fuelNozzle, true, true) + + local ped = PlayerPedId() + local pedBone = GetPedBoneIndex(ped, 18905) + AttachEntityToEntity(fuelNozzle, ped, pedBone, 0.13, 0.04, 0.01, -42.0, -115.0, -63.42, false, true, false, true, 0, true) + + vehicleAttachedToNozzle = nil +end + +function getNearestPumpRopeLength(fuelTypePurchased, pumpCoords) + local distanceToFindPump = 10 + local ropeLength = Config.DefaultRopeLength + if fuelTypePurchased == "electricfast" or fuelTypePurchased == "electricnormal" then + for _, pumpConfig in pairs(Config.Electric.chargersLocation) do + local distance = #(vector3(pumpConfig.location.x, pumpConfig.location.y, pumpConfig.location.z) - pumpCoords) + if distance < distanceToFindPump then + ropeLength = pumpConfig.ropeLength + break + end + end + else + for _, pumpConfig in pairs(Config.CustomGasPumpLocations) do + local distance = #(vector3(pumpConfig.location.x, pumpConfig.location.y, pumpConfig.location.z) - pumpCoords) + if distance < distanceToFindPump then + ropeLength = pumpConfig.ropeLength + break + end + end + end + return ropeLength +end + +function createFuelNozzleObject(fuelTypePurchased) + local nozzle_prop_label = Config.NozzleProps.gas + -- Change the nozzle prop to electric + if fuelTypePurchased == "electricfast" or fuelTypePurchased == "electricnormal" then + nozzle_prop_label = Config.NozzleProps.electric + end + + RequestModel(nozzle_prop_label) + while not HasModelLoaded(nozzle_prop_label) do + Wait(50) + end + + return CreateObject(joaat(nozzle_prop_label), 1.0, 1.0, 1.0, true, true, false) end \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/fxmanifest.lua b/resources/[carscripts]/lc_fuel/fxmanifest.lua index 2e8c226f5..9c60e8458 100644 --- a/resources/[carscripts]/lc_fuel/fxmanifest.lua +++ b/resources/[carscripts]/lc_fuel/fxmanifest.lua @@ -1,48 +1,49 @@ -fx_version 'cerulean' -game 'gta5' -author 'LixeiroCharmoso' -name 'lc_fuel' - -ui_page "nui/ui.html" - -lua54 'yes' - -escrow_ignore { - '**' -} - -client_scripts { - "client/client.lua", - "client/client_gas.lua", - "client/client_electric.lua", - "client/client_refuel.lua", - "client/client_fuel_chart.lua", -} - -server_scripts { - "@mysql-async/lib/MySQL.lua", - "server/server.lua", -} - -shared_scripts { - "lang/*.lua", - "config.lua", - "@lc_utils/functions/loader.lua", -} - -files { - "version", - "nui/lang/*", - "nui/ui.html", - "nui/panel.js", - "nui/scripts/*", - "nui/css/*", - "nui/images/*", - "nui/fonts/Technology.woff", -} - -dependency "lc_utils" -provides 'LegacyFuel' - -data_file 'DLC_ITYP_REQUEST' 'stream/prop_electric_01.ytyp' +fx_version 'cerulean' +game 'gta5' +author 'LixeiroCharmoso' +name 'lc_fuel' + +ui_page "nui/ui.html" + +lua54 'yes' + +escrow_ignore { + '**' +} + +client_scripts { + "client/client.lua", + "client/client_gas.lua", + "client/client_electric.lua", + "client/client_refuel.lua", + "client/client_fuel_chart.lua", + "client/client_fuel_type.lua", +} + +server_scripts { + "@mysql-async/lib/MySQL.lua", + "server/server.lua", +} + +shared_scripts { + "lang/*.lua", + "config.lua", + "@lc_utils/functions/loader.lua", +} + +files { + "version", + "nui/lang/*", + "nui/ui.html", + "nui/panel.js", + "nui/scripts/*", + "nui/css/*", + "nui/images/*", + "nui/fonts/Technology.woff", +} + +dependency "lc_utils" +provides 'LegacyFuel' + +data_file 'DLC_ITYP_REQUEST' 'stream/prop_electric_01.ytyp' data_file 'DLC_ITYP_REQUEST' 'stream/prop_eletricpistol.ytyp' \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/lang/de.lua b/resources/[carscripts]/lc_fuel/lang/de.lua index 3786d0991..f7b3a6a4c 100644 --- a/resources/[carscripts]/lc_fuel/lang/de.lua +++ b/resources/[carscripts]/lc_fuel/lang/de.lua @@ -1,41 +1,49 @@ -if not Lang then Lang = {} end -Lang['de'] = { - ['markers'] = { - ['open_refuel'] = "Drücke ~INPUT_CONTEXT~, um zu tanken", - ['open_recharge'] = "Drücke ~INPUT_CONTEXT~, um aufzuladen", - ['interact_with_vehicle'] = "Drücke ~y~E~w~, um zu interagieren", - ['return_nozzle'] = "Drücke ~INPUT_CONTEXT~, um die Zapfpistole zurückzugeben", - }, - ['target'] = { - ['open_refuel'] = "Tankmenü öffnen", - ['open_recharge'] = "Lademenü öffnen", - ['start_refuel'] = "Fahrzeug betanken", - ['stop_refuel'] = "Tanken beenden", - ['return_nozzle'] = "Zapfpistole zurückgeben", - }, - ['blip_text'] = "Tankstelle", - ['not_enough_refuel'] = "Sie haben den gesamten bezahlten Kraftstoff bereits verbraucht. Bitte kaufen Sie bei Bedarf zusätzlichen Treibstoff", - ['invalid_value'] = "Ungültiger Wert", - ['not_enough_money'] = "Du hast nicht genug $%s geld um das zu Bezahlen", - ['not_enough_stock'] = "Diese Tankstelle verfügt nicht über genügend Lagerbestände, um diese Aktion durchzuführen", - ['refuel_paid'] = "$%s bezahlt zum Tanken", - ['returned_fuel'] = "Du hast %sL Kraftstoff zurückgegeben und $%s zurückerhalten", - ['returned_charge'] = "Du hast %skWh Ladung zurückgegeben und $%s zurückerhalten", - ['jerry_can_paid'] = "$%s bezahlt für den Kanister", - ['too_far_away'] = "Sie sind zu weit von der Zapfseule entfernt", - ['vehicle_refueled'] = "Sie haben %sL ins Fahrzeug getankt", - ['vehicle_recharged'] = "Sie haben %skWh im Fahrzeug aufgeladen", - ['vehicle_tank_full'] = "Auto ist voll getankt", - ['vehicle_tank_emptied'] = "Fahrzeugtank ist leer", - ['vehicle_not_found'] = "Fahrzeug konnte nicht gefunden werden", - ['pump_not_found'] = "Zapfseule konnte nicht gefunden werden", - ['vehicle_wrong_fuel'] = "Sie haben für dieses Fahrzeug den falschen Kraftstofftyp verwendet, was zu einer Panne geführt hat.", - ['incompatible_fuel'] = "Inkompatibler Kraftstofftyp erkannt. Bitte wählen Sie die richtige Betankungsoption für Ihr Fahrzeug aus.", - ['owned_gas_stations'] = { - ['balance_jerry_can'] = "Benzinkanister verkauft (%s Liter)", - ['balance_fuel'] = "Treibstoff verkauft (%s Liter)", - ['balance_electric'] = "Elektrische Ladung verkauft (%s kWh)", - ['refund_fuel'] = "Kraftstoff erstattet (%s Liter)", - ['refund_electric'] = "Stromladung erstattet (%s kWh)", - } -} +if not Lang then Lang = {} end +Lang['de'] = { + ['markers'] = { + ['open_refuel'] = "Drücke ~INPUT_CONTEXT~, um zu tanken", + ['open_recharge'] = "Drücke ~INPUT_CONTEXT~, um aufzuladen", + ['interact_with_vehicle'] = "Drücke ~y~E~w~, um zu interagieren", + ['return_nozzle'] = "Drücke ~INPUT_CONTEXT~, um die Zapfpistole zurückzugeben", + }, + ['target'] = { + ['open_refuel'] = "Tankmenü öffnen", + ['open_recharge'] = "Lademenü öffnen", + ['start_refuel'] = "Fahrzeug betanken", + ['stop_refuel'] = "Tanken beenden", + ['return_nozzle'] = "Zapfpistole zurückgeben", + }, + ['blip_text'] = "Tankstelle", + ['not_enough_refuel'] = "Sie haben den gesamten bezahlten Kraftstoff bereits verbraucht. Bitte kaufen Sie bei Bedarf zusätzlichen Treibstoff", + ['invalid_value'] = "Ungültiger Wert", + ['not_enough_money'] = "Du hast nicht genug $%s geld um das zu Bezahlen", + ['not_enough_stock'] = "Diese Tankstelle verfügt nicht über genügend Lagerbestände, um diese Aktion durchzuführen", + ['refuel_paid'] = "$%s bezahlt zum Tanken", + ['returned_fuel'] = "Du hast %sL Kraftstoff zurückgegeben und $%s zurückerhalten", + ['returned_charge'] = "Du hast %skWh Ladung zurückgegeben und $%s zurückerhalten", + ['jerry_can_paid'] = "$%s bezahlt für den Kanister", + ['too_far_away'] = "Sie sind zu weit von der Zapfseule entfernt", + ['vehicle_refueled'] = "Sie haben %sL ins Fahrzeug getankt", + ['vehicle_recharged'] = "Sie haben %skWh im Fahrzeug aufgeladen", + ['vehicle_tank_full'] = "Auto ist voll getankt", + ['vehicle_tank_emptied'] = "Fahrzeugtank ist leer", + ['vehicle_not_found'] = "Fahrzeug konnte nicht gefunden werden", + ['pump_not_found'] = "Zapfseule konnte nicht gefunden werden", + ['vehicle_wrong_fuel'] = "Sie haben für dieses Fahrzeug den falschen Kraftstofftyp verwendet, was zu einer Panne geführt hat.", + ['incompatible_fuel'] = "Inkompatibler Kraftstofftyp erkannt. Bitte wählen Sie die richtige Betankungsoption für Ihr Fahrzeug aus.", + ['owned_gas_stations'] = { + ['balance_jerry_can'] = "Benzinkanister verkauft (%s Liter)", + ['balance_fuel'] = "Treibstoff verkauft (%s Liter)", + ['balance_electric'] = "Elektrische Ladung verkauft (%s kWh)", + ['refund_fuel'] = "Kraftstoff erstattet (%s Liter)", + ['refund_electric'] = "Stromladung erstattet (%s kWh)", + }, + ['fuel_types'] = { + ['type_title'] = "Kraftstoffart: %s", + ['electric'] = "Elektrisch", + ['regular'] = "Regulär", + ['plus'] = "Plus", + ['premium'] = "Premium", + ['diesel'] = "Diesel", + }, +} diff --git a/resources/[carscripts]/lc_fuel/lang/en.lua b/resources/[carscripts]/lc_fuel/lang/en.lua index a0137aa61..4de19dddd 100644 --- a/resources/[carscripts]/lc_fuel/lang/en.lua +++ b/resources/[carscripts]/lc_fuel/lang/en.lua @@ -1,41 +1,49 @@ -if not Lang then Lang = {} end -Lang['en'] = { - ['markers'] = { - ['open_refuel'] = "Press ~INPUT_CONTEXT~ to refuel", - ['open_recharge'] = "Press ~INPUT_CONTEXT~ to recharge", - ['interact_with_vehicle'] = "Press ~y~E~w~ to interact", - ['return_nozzle'] = "Press ~INPUT_CONTEXT~ to return the nozzle", - }, - ['target'] = { - ['open_refuel'] = "Open refuel menu", - ['open_recharge'] = "Open recharge menu", - ['start_refuel'] = "Refuel vehicle", - ['stop_refuel'] = "Stop refuel", - ['return_nozzle'] = "Return the nozzle", - }, - ['blip_text'] = "Gas stations", - ['not_enough_refuel'] = "You've already used all the fuel you paid for. Please purchase additional fuel if needed", - ['invalid_value'] = "Invalid value", - ['not_enough_money'] = "You don't have $%s to pay this", - ['not_enough_stock'] = "This gas station don't have enough stock to perform this action", - ['refuel_paid'] = "Paid $%s for this refuel", - ['returned_fuel'] = "You've returned %sL of fuel and received back $%s", - ['returned_charge'] = "You've returned %skWh of fuel and received back $%s", - ['jerry_can_paid'] = "Paid $%s for this jerry can", - ['too_far_away'] = "You are too far from the pump", - ['vehicle_refueled'] = "You refueled %sL in the vehicle", - ['vehicle_recharged'] = "You recharged %skWh in the vehicle", - ['vehicle_tank_full'] = "Vehicle tank is full", - ['vehicle_tank_emptied'] = "Vehicle tank emptied", - ['vehicle_not_found'] = "Vehicle not found", - ['pump_not_found'] = "Pump not found", - ['vehicle_wrong_fuel'] = "You used the wrong fuel type for this vehicle, causing it to break down.", - ['incompatible_fuel'] = "Incompatible fuel type detected. Please select the correct refueling option for your vehicle.", - ['owned_gas_stations'] = { - ['balance_jerry_can'] = "Gas can sold (%s Liters)", - ['balance_fuel'] = "Fuel sold (%s Liters)", - ['balance_electric'] = "Electric charge sold (%s kWh)", - ['refund_fuel'] = "Fuel refunded (%s Liters)", - ['refund_electric'] = "Electric charge refunded (%s kWh)", - } +if not Lang then Lang = {} end +Lang['en'] = { + ['markers'] = { + ['open_refuel'] = "Press ~INPUT_CONTEXT~ to refuel", + ['open_recharge'] = "Press ~INPUT_CONTEXT~ to recharge", + ['interact_with_vehicle'] = "Press ~y~E~w~ to interact", + ['return_nozzle'] = "Press ~INPUT_CONTEXT~ to return the nozzle", + }, + ['target'] = { + ['open_refuel'] = "Open refuel menu", + ['open_recharge'] = "Open recharge menu", + ['start_refuel'] = "Refuel vehicle", + ['stop_refuel'] = "Stop refuel", + ['return_nozzle'] = "Return the nozzle", + }, + ['blip_text'] = "Gas stations", + ['not_enough_refuel'] = "You've already used all the fuel you paid for. Please purchase additional fuel if needed", + ['invalid_value'] = "Invalid value", + ['not_enough_money'] = "You don't have $%s to pay this", + ['not_enough_stock'] = "This gas station don't have enough stock to perform this action", + ['refuel_paid'] = "Paid $%s for this refuel", + ['returned_fuel'] = "You've returned %sL of fuel and received back $%s", + ['returned_charge'] = "You've returned %skWh of fuel and received back $%s", + ['jerry_can_paid'] = "Paid $%s for this jerry can", + ['too_far_away'] = "You are too far from the pump", + ['vehicle_refueled'] = "You refueled %sL in the vehicle", + ['vehicle_recharged'] = "You recharged %skWh in the vehicle", + ['vehicle_tank_full'] = "Vehicle tank is full", + ['vehicle_tank_emptied'] = "Vehicle tank emptied", + ['vehicle_not_found'] = "Vehicle not found", + ['pump_not_found'] = "Pump not found", + ['vehicle_wrong_fuel'] = "You used the wrong fuel type for this vehicle, causing it to break down.", + ['incompatible_fuel'] = "Incompatible fuel type detected. Please select the correct refueling option for your vehicle.", + ['owned_gas_stations'] = { + ['balance_jerry_can'] = "Gas can sold (%s Liters)", + ['balance_fuel'] = "Fuel sold (%s Liters)", + ['balance_electric'] = "Electric charge sold (%s kWh)", + ['refund_fuel'] = "Fuel refunded (%s Liters)", + ['refund_electric'] = "Electric charge refunded (%s kWh)", + }, + ['fuel_types'] = { + ['type_title'] = "Fuel Type: %s", + ['electric'] = "Electric", + ['regular'] = "Regular", + ['plus'] = "Plus", + ['premium'] = "Premium", + ['diesel'] = "Diesel", + }, } \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/lang/es.lua b/resources/[carscripts]/lc_fuel/lang/es.lua index 0f57b0fb2..440407716 100644 --- a/resources/[carscripts]/lc_fuel/lang/es.lua +++ b/resources/[carscripts]/lc_fuel/lang/es.lua @@ -1,41 +1,49 @@ -if not Lang then Lang = {} end -Lang['es'] = { - ['markers'] = { - ['open_refuel'] = "Pulsa ~INPUT_CONTEXT~ para repostar", - ['open_recharge'] = "Pulsa ~INPUT_CONTEXT~ para cargar", - ['interact_with_vehicle'] = "Pulsa ~y~E~w~ para interactuar", - ['return_nozzle'] = "Pulsa ~INPUT_CONTEXT~ para devolver la manguera", - }, - ['target'] = { - ['open_refuel'] = "Abrir menú de repostaje", - ['open_recharge'] = "Abrir menú de carga", - ['start_refuel'] = "Repostar vehículo", - ['stop_refuel'] = "Detener repostaje", - ['return_nozzle'] = "Devolver la manguera", - }, - ['blip_text'] = "Gasolineras", - ['not_enough_refuel'] = "Ya has usado todo el combustible que pagaste. Compra más combustible si es necesario", - ['invalid_value'] = "Valor inválido", - ['not_enough_money'] = "No tienes $%s para pagar esto", - ['not_enough_stock'] = "Esta gasolinera no tiene suficiente stock para realizar esta acción", - ['refuel_paid'] = "Pagaste $%s por este repostaje", - ['returned_fuel'] = "Has devuelto %sL de combustible y recibido $%s", - ['returned_charge'] = "Has devuelto %skWh de carga y recibido $%s", - ['jerry_can_paid'] = "Pagaste $%s por este bidón de gasolina", - ['too_far_away'] = "Estás demasiado lejos del surtidor", - ['vehicle_refueled'] = "Repostaste %sL en el vehículo", - ['vehicle_recharged'] = "Recargaste %skWh en el vehículo", - ['vehicle_tank_full'] = "El tanque del vehículo está lleno", - ['vehicle_tank_emptied'] = "El tanque del vehículo se ha vaciado", - ['vehicle_not_found'] = "Vehículo no encontrado", - ['pump_not_found'] = "Surtidor no encontrado", - ['vehicle_wrong_fuel'] = "Usaste el tipo de combustible incorrecto para este vehículo, causando que se averíe.", - ['incompatible_fuel'] = "Tipo de combustible incompatible detectado. Por favor, selecciona la opción de repostaje correcta para tu vehículo.", - ['owned_gas_stations'] = { - ['balance_jerry_can'] = "Bidón de gasolina vendido (%s Litros)", - ['balance_fuel'] = "Combustible vendido (%s Litros)", - ['balance_electric'] = "Carga eléctrica vendida (%s kWh)", - ['refund_fuel'] = "Combustible reembolsado (%s litros)", - ['refund_electric'] = "Carga eléctrica reembolsada (%s kWh)", - } -} +if not Lang then Lang = {} end +Lang['es'] = { + ['markers'] = { + ['open_refuel'] = "Pulsa ~INPUT_CONTEXT~ para repostar", + ['open_recharge'] = "Pulsa ~INPUT_CONTEXT~ para cargar", + ['interact_with_vehicle'] = "Pulsa ~y~E~w~ para interactuar", + ['return_nozzle'] = "Pulsa ~INPUT_CONTEXT~ para devolver la manguera", + }, + ['target'] = { + ['open_refuel'] = "Abrir menú de repostaje", + ['open_recharge'] = "Abrir menú de carga", + ['start_refuel'] = "Repostar vehículo", + ['stop_refuel'] = "Detener repostaje", + ['return_nozzle'] = "Devolver la manguera", + }, + ['blip_text'] = "Gasolineras", + ['not_enough_refuel'] = "Ya has usado todo el combustible que pagaste. Compra más combustible si es necesario", + ['invalid_value'] = "Valor inválido", + ['not_enough_money'] = "No tienes $%s para pagar esto", + ['not_enough_stock'] = "Esta gasolinera no tiene suficiente stock para realizar esta acción", + ['refuel_paid'] = "Pagaste $%s por este repostaje", + ['returned_fuel'] = "Has devuelto %sL de combustible y recibido $%s", + ['returned_charge'] = "Has devuelto %skWh de carga y recibido $%s", + ['jerry_can_paid'] = "Pagaste $%s por este bidón de gasolina", + ['too_far_away'] = "Estás demasiado lejos del surtidor", + ['vehicle_refueled'] = "Repostaste %sL en el vehículo", + ['vehicle_recharged'] = "Recargaste %skWh en el vehículo", + ['vehicle_tank_full'] = "El tanque del vehículo está lleno", + ['vehicle_tank_emptied'] = "El tanque del vehículo se ha vaciado", + ['vehicle_not_found'] = "Vehículo no encontrado", + ['pump_not_found'] = "Surtidor no encontrado", + ['vehicle_wrong_fuel'] = "Usaste el tipo de combustible incorrecto para este vehículo, causando que se averíe.", + ['incompatible_fuel'] = "Tipo de combustible incompatible detectado. Por favor, selecciona la opción de repostaje correcta para tu vehículo.", + ['owned_gas_stations'] = { + ['balance_jerry_can'] = "Bidón de gasolina vendido (%s Litros)", + ['balance_fuel'] = "Combustible vendido (%s Litros)", + ['balance_electric'] = "Carga eléctrica vendida (%s kWh)", + ['refund_fuel'] = "Combustible reembolsado (%s litros)", + ['refund_electric'] = "Carga eléctrica reembolsada (%s kWh)", + }, + ['fuel_types'] = { + ['type_title'] = "Tipo de combustible: %s", + ['electric'] = "Eléctrico", + ['regular'] = "Sin plomo", + ['plus'] = "Plus", + ['premium'] = "Premium", + ['diesel'] = "Diésel", + }, +} diff --git a/resources/[carscripts]/lc_fuel/lang/fr.lua b/resources/[carscripts]/lc_fuel/lang/fr.lua index b22f15033..e78a40f70 100644 --- a/resources/[carscripts]/lc_fuel/lang/fr.lua +++ b/resources/[carscripts]/lc_fuel/lang/fr.lua @@ -1,41 +1,49 @@ -if not Lang then Lang = {} end -Lang['fr'] = { - ['markers'] = { - ['open_refuel'] = "Appuyez sur ~INPUT_CONTEXT~ pour faire le plein", - ['open_recharge'] = "Appuyez sur ~INPUT_CONTEXT~ pour recharger", - ['interact_with_vehicle'] = "Appuyez sur ~y~E~w~ pour interagir", - ['return_nozzle'] = "Appuyez sur ~INPUT_CONTEXT~ pour remettre le pistolet", - }, - ['target'] = { - ['open_refuel'] = "Ouvrir le menu de ravitaillement", - ['open_recharge'] = "Ouvrir le menu de recharge", - ['start_refuel'] = "Faire le plein du véhicule", - ['stop_refuel'] = "Arrêter le ravitaillement", - ['return_nozzle'] = "Ranger le pistolet", - }, - ['blip_text'] = "Stations-service", - ['not_enough_refuel'] = "Vous avez déjà utilisé tout le carburant que vous avez payé. Veuillez acheter plus de carburant si nécessaire", - ['invalid_value'] = "Valeur invalide", - ['not_enough_money'] = "Vous n'avez pas $%s pour payer cela", - ['not_enough_stock'] = "Cette station-service n'a pas assez de stock pour effectuer cette action", - ['refuel_paid'] = "Vous avez payé $%s pour ce ravitaillement", - ['returned_fuel'] = "Vous avez rendu %sL de carburant et reçu $%s", - ['returned_charge'] = "Vous avez rendu %skWh de charge et reçu $%s", - ['jerry_can_paid'] = "Vous avez payé $%s pour ce bidon d'essence", - ['too_far_away'] = "Vous êtes trop loin de la pompe", - ['vehicle_refueled'] = "Vous avez mis %sL dans le véhicule", - ['vehicle_recharged'] = "Vous avez rechargé %skWh dans le véhicule", - ['vehicle_tank_full'] = "Le réservoir du véhicule est plein", - ['vehicle_tank_emptied'] = "Le réservoir du véhicule est vide", - ['vehicle_not_found'] = "Véhicule non trouvé", - ['pump_not_found'] = "Pompe non trouvée", - ['vehicle_wrong_fuel'] = "Vous avez utilisé le mauvais type de carburant pour ce véhicule, ce qui l'a endommagé.", - ['incompatible_fuel'] = "Type de carburant incompatible détecté. Veuillez sélectionner la bonne option de ravitaillement pour votre véhicule.", - ['owned_gas_stations'] = { - ['balance_jerry_can'] = "Bidon d'essence vendu (%s Litres)", - ['balance_fuel'] = "Carburant vendu (%s Litres)", - ['balance_electric'] = "Charge électrique vendue (%s kWh)", - ['refund_fuel'] = "Carburant remboursé (%s litres)", - ['refund_electric'] = "Recharge électrique remboursée (%s kWh)", - } +if not Lang then Lang = {} end +Lang['fr'] = { + ['markers'] = { + ['open_refuel'] = "Appuyez sur ~INPUT_CONTEXT~ pour faire le plein", + ['open_recharge'] = "Appuyez sur ~INPUT_CONTEXT~ pour recharger", + ['interact_with_vehicle'] = "Appuyez sur ~y~E~w~ pour interagir", + ['return_nozzle'] = "Appuyez sur ~INPUT_CONTEXT~ pour remettre le pistolet", + }, + ['target'] = { + ['open_refuel'] = "Ouvrir le menu de ravitaillement", + ['open_recharge'] = "Ouvrir le menu de recharge", + ['start_refuel'] = "Faire le plein du véhicule", + ['stop_refuel'] = "Arrêter le ravitaillement", + ['return_nozzle'] = "Ranger le pistolet", + }, + ['blip_text'] = "Stations-service", + ['not_enough_refuel'] = "Vous avez déjà utilisé tout le carburant que vous avez payé. Veuillez acheter plus de carburant si nécessaire", + ['invalid_value'] = "Valeur invalide", + ['not_enough_money'] = "Vous n'avez pas $%s pour payer cela", + ['not_enough_stock'] = "Cette station-service n'a pas assez de stock pour effectuer cette action", + ['refuel_paid'] = "Vous avez payé $%s pour ce ravitaillement", + ['returned_fuel'] = "Vous avez rendu %sL de carburant et reçu $%s", + ['returned_charge'] = "Vous avez rendu %skWh de charge et reçu $%s", + ['jerry_can_paid'] = "Vous avez payé $%s pour ce bidon d'essence", + ['too_far_away'] = "Vous êtes trop loin de la pompe", + ['vehicle_refueled'] = "Vous avez mis %sL dans le véhicule", + ['vehicle_recharged'] = "Vous avez rechargé %skWh dans le véhicule", + ['vehicle_tank_full'] = "Le réservoir du véhicule est plein", + ['vehicle_tank_emptied'] = "Le réservoir du véhicule est vide", + ['vehicle_not_found'] = "Véhicule non trouvé", + ['pump_not_found'] = "Pompe non trouvée", + ['vehicle_wrong_fuel'] = "Vous avez utilisé le mauvais type de carburant pour ce véhicule, ce qui l'a endommagé.", + ['incompatible_fuel'] = "Type de carburant incompatible détecté. Veuillez sélectionner la bonne option de ravitaillement pour votre véhicule.", + ['owned_gas_stations'] = { + ['balance_jerry_can'] = "Bidon d'essence vendu (%s Litres)", + ['balance_fuel'] = "Carburant vendu (%s Litres)", + ['balance_electric'] = "Charge électrique vendue (%s kWh)", + ['refund_fuel'] = "Carburant remboursé (%s litres)", + ['refund_electric'] = "Recharge électrique remboursée (%s kWh)", + }, + ['fuel_types'] = { + ['type_title'] = "Type de carburant : %s", + ['electric'] = "Électrique", + ['regular'] = "Ordinaire", + ['plus'] = "Plus", + ['premium'] = "Premium", + ['diesel'] = "Diesel", + }, } \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/lang/ja.lua b/resources/[carscripts]/lc_fuel/lang/ja.lua index bf3cf5ca5..f42e9d4fc 100644 --- a/resources/[carscripts]/lc_fuel/lang/ja.lua +++ b/resources/[carscripts]/lc_fuel/lang/ja.lua @@ -1,41 +1,49 @@ -if not Lang then Lang = {} end -Lang['ja'] = { - ['markers'] = { - ['open_refuel'] = "~INPUT_CONTEXT~を押して燃料を補給します", - ['open_recharge'] = "~INPUT_CONTEXT~を押して充電", - ['interact_with_vehicle'] = "~y~E~w~を押して車両と操作", - ['return_nozzle'] = "~INPUT_CONTEXT~を押してノズルを戻す", - }, - ['target'] = { - ['open_refuel'] = "燃料補給メニューを開く", - ['open_recharge'] = "充電メニューを開く", - ['start_refuel'] = "車両に燃料補給", - ['stop_refuel'] = "燃料補給を停止", - ['return_nozzle'] = "ノズルを戻す", - }, - ['blip_text'] = "ガソリンスタンド", - ['not_enough_refuel'] = "既に支払った燃料を使い切っています。必要であれば追加購入してください。", - ['invalid_value'] = "無効な値です", - ['not_enough_money'] = "この支払いには$%sが不足しています", - ['not_enough_stock'] = "このガソリンスタンドでは在庫が不足しているため、この操作は実行できません", - ['refuel_paid'] = "この燃料補給に$%s支払いました", - ['returned_fuel'] = "燃料を%sL返却し、$%sを受け取りました", - ['returned_charge'] = "電力を%skWh返却し、$%sを受け取りました", - ['jerry_can_paid'] = "このガソリン缶に$%s支払いました", - ['too_far_away'] = "ポンプから離れすぎています", - ['vehicle_refueled'] = "車両に%sLの燃料を補給しました", - ['vehicle_recharged'] = "車両に%s kWhの充電を行いました", - ['vehicle_tank_full'] = "車両のタンクが満タンです", - ['vehicle_tank_emptied'] = "車両のタンクが空になりました", - ['vehicle_not_found'] = "車両が見つかりませんでした", - ['pump_not_found'] = "ポンプが見つかりませんでした", - ['vehicle_wrong_fuel'] = "この車両には間違った燃料タイプを使用したため、故障しました。", - ['incompatible_fuel'] = "互換性のない燃料タイプが検出されました。正しい燃料補給オプションを選択してください。", - ['owned_gas_stations'] = { - ['balance_jerry_can'] = "ガソリン缶販売 (%s L)", - ['balance_fuel'] = "燃料販売 (%s L)", - ['balance_electric'] = "電気充電販売 (%s kWh)", - ['refund_fuel'] = "燃料が返金されました(%sリットル)", - ['refund_electric'] = "電気充電が返金されました(%s kWh)", - } +if not Lang then Lang = {} end +Lang['ja'] = { + ['markers'] = { + ['open_refuel'] = "~INPUT_CONTEXT~を押して燃料を補給します", + ['open_recharge'] = "~INPUT_CONTEXT~を押して充電", + ['interact_with_vehicle'] = "~y~E~w~を押して車両と操作", + ['return_nozzle'] = "~INPUT_CONTEXT~を押してノズルを戻す", + }, + ['target'] = { + ['open_refuel'] = "燃料補給メニューを開く", + ['open_recharge'] = "充電メニューを開く", + ['start_refuel'] = "車両に燃料補給", + ['stop_refuel'] = "燃料補給を停止", + ['return_nozzle'] = "ノズルを戻す", + }, + ['blip_text'] = "ガソリンスタンド", + ['not_enough_refuel'] = "既に支払った燃料を使い切っています。必要であれば追加購入してください。", + ['invalid_value'] = "無効な値です", + ['not_enough_money'] = "この支払いには$%sが不足しています", + ['not_enough_stock'] = "このガソリンスタンドでは在庫が不足しているため、この操作は実行できません", + ['refuel_paid'] = "この燃料補給に$%s支払いました", + ['returned_fuel'] = "燃料を%sL返却し、$%sを受け取りました", + ['returned_charge'] = "電力を%skWh返却し、$%sを受け取りました", + ['jerry_can_paid'] = "このガソリン缶に$%s支払いました", + ['too_far_away'] = "ポンプから離れすぎています", + ['vehicle_refueled'] = "車両に%sLの燃料を補給しました", + ['vehicle_recharged'] = "車両に%s kWhの充電を行いました", + ['vehicle_tank_full'] = "車両のタンクが満タンです", + ['vehicle_tank_emptied'] = "車両のタンクが空になりました", + ['vehicle_not_found'] = "車両が見つかりませんでした", + ['pump_not_found'] = "ポンプが見つかりませんでした", + ['vehicle_wrong_fuel'] = "この車両には間違った燃料タイプを使用したため、故障しました。", + ['incompatible_fuel'] = "互換性のない燃料タイプが検出されました。正しい燃料補給オプションを選択してください。", + ['owned_gas_stations'] = { + ['balance_jerry_can'] = "ガソリン缶販売 (%s L)", + ['balance_fuel'] = "燃料販売 (%s L)", + ['balance_electric'] = "電気充電販売 (%s kWh)", + ['refund_fuel'] = "燃料が返金されました(%sリットル)", + ['refund_electric'] = "電気充電が返金されました(%s kWh)", + }, + ['fuel_types'] = { + ['type_title'] = "燃料の種類: %s", + ['electric'] = "電気", + ['regular'] = "レギュラー", + ['plus'] = "プラス", + ['premium'] = "プレミアム", + ['diesel'] = "ディーゼル", + }, } \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/lang/tr.lua b/resources/[carscripts]/lc_fuel/lang/tr.lua index b6b808b7d..3766d6010 100644 --- a/resources/[carscripts]/lc_fuel/lang/tr.lua +++ b/resources/[carscripts]/lc_fuel/lang/tr.lua @@ -1,41 +1,49 @@ -if not Lang then Lang = {} end -Lang['tr'] = { - ['markers'] = { - ['open_refuel'] = "Yakıt almak için ~INPUT_CONTEXT~ tuşuna basın", - ['open_recharge'] = "Şarj etmek için ~INPUT_CONTEXT~ tuşuna basın", - ['interact_with_vehicle'] = "Etkileşim için ~y~E~w~ tuşuna basın", - ['return_nozzle'] = "Tabancayı geri koymak için ~INPUT_CONTEXT~ tuşuna basın", - }, - ['target'] = { - ['open_refuel'] = "Yakıt menüsünü aç", - ['open_recharge'] = "Şarj menüsünü aç", - ['start_refuel'] = "Araca yakıt doldur", - ['stop_refuel'] = "Yakıt doldurmayı durdur", - ['return_nozzle'] = "Tabancayı geri koy", - }, - ['blip_text'] = "Benzin istasyonları", - ['not_enough_refuel'] = "Zaten ödediğiniz tüm yakıtı kullandınız. Gerekirse ek yakıt satın alın", - ['invalid_value'] = "Geçersiz değer", - ['not_enough_money'] = "Bunu ödemek için $%s paranız yok", - ['not_enough_stock'] = "Bu benzin istasyonunda bu işlemi gerçekleştirmek için yeterli stok yok", - ['refuel_paid'] = "Bu yakıt için $%s ödendi", - ['returned_fuel'] = "%sL yakıt iade ettiniz ve $%s geri aldınız", - ['returned_charge'] = "%skWh şarj iade ettiniz ve $%s geri aldınız", - ['jerry_can_paid'] = "Bu bidon için $%s ödendi", - ['too_far_away'] = "Pompadan çok uzaktasınız", - ['vehicle_refueled'] = "Araca %sL yakıt dolduruldu", - ['vehicle_recharged'] = "Araca %skWh şarj yapıldı", - ['vehicle_tank_full'] = "Araç deposu dolu", - ['vehicle_tank_emptied'] = "Araç deposu boşaltıldı", - ['vehicle_not_found'] = "Araç bulunamadı", - ['pump_not_found'] = "Pompa bulunamadı", - ['vehicle_wrong_fuel'] = "Bu araç için yanlış yakıt kullandınız ve aracınız bozuldu.", - ['incompatible_fuel'] = "Uyumsuz yakıt türü tespit edildi. Lütfen aracınız için doğru yakıt seçeneğini seçin.", - ['owned_gas_stations'] = { - ['balance_jerry_can'] = "Yakıt bidonu satıldı (%s Litre)", - ['balance_fuel'] = "Yakıt satıldı (%s Litre)", - ['balance_electric'] = "Elektrik şarjı satıldı (%s kWh)", - ['refund_fuel'] = "Yakıt iade edildi (%s litre)", - ['refund_electric'] = "Elektrik şarjı iade edildi (%s kWh)", - } -} +if not Lang then Lang = {} end +Lang['tr'] = { + ['markers'] = { + ['open_refuel'] = "Yakıt almak için ~INPUT_CONTEXT~ tuşuna basın", + ['open_recharge'] = "Şarj etmek için ~INPUT_CONTEXT~ tuşuna basın", + ['interact_with_vehicle'] = "Etkileşim için ~y~E~w~ tuşuna basın", + ['return_nozzle'] = "Tabancayı geri koymak için ~INPUT_CONTEXT~ tuşuna basın", + }, + ['target'] = { + ['open_refuel'] = "Yakıt menüsünü aç", + ['open_recharge'] = "Şarj menüsünü aç", + ['start_refuel'] = "Araca yakıt doldur", + ['stop_refuel'] = "Yakıt doldurmayı durdur", + ['return_nozzle'] = "Tabancayı geri koy", + }, + ['blip_text'] = "Benzin istasyonları", + ['not_enough_refuel'] = "Zaten ödediğiniz tüm yakıtı kullandınız. Gerekirse ek yakıt satın alın", + ['invalid_value'] = "Geçersiz değer", + ['not_enough_money'] = "Bunu ödemek için $%s paranız yok", + ['not_enough_stock'] = "Bu benzin istasyonunda bu işlemi gerçekleştirmek için yeterli stok yok", + ['refuel_paid'] = "Bu yakıt için $%s ödendi", + ['returned_fuel'] = "%sL yakıt iade ettiniz ve $%s geri aldınız", + ['returned_charge'] = "%skWh şarj iade ettiniz ve $%s geri aldınız", + ['jerry_can_paid'] = "Bu bidon için $%s ödendi", + ['too_far_away'] = "Pompadan çok uzaktasınız", + ['vehicle_refueled'] = "Araca %sL yakıt dolduruldu", + ['vehicle_recharged'] = "Araca %skWh şarj yapıldı", + ['vehicle_tank_full'] = "Araç deposu dolu", + ['vehicle_tank_emptied'] = "Araç deposu boşaltıldı", + ['vehicle_not_found'] = "Araç bulunamadı", + ['pump_not_found'] = "Pompa bulunamadı", + ['vehicle_wrong_fuel'] = "Bu araç için yanlış yakıt kullandınız ve aracınız bozuldu.", + ['incompatible_fuel'] = "Uyumsuz yakıt türü tespit edildi. Lütfen aracınız için doğru yakıt seçeneğini seçin.", + ['owned_gas_stations'] = { + ['balance_jerry_can'] = "Yakıt bidonu satıldı (%s Litre)", + ['balance_fuel'] = "Yakıt satıldı (%s Litre)", + ['balance_electric'] = "Elektrik şarjı satıldı (%s kWh)", + ['refund_fuel'] = "Yakıt iade edildi (%s litre)", + ['refund_electric'] = "Elektrik şarjı iade edildi (%s kWh)", + }, + ['fuel_types'] = { + ['type_title'] = "Yakıt Türü: %s", + ['electric'] = "Elektrikli", + ['regular'] = "Normal", + ['plus'] = "Plus", + ['premium'] = "Premium", + ['diesel'] = "Dizel", + }, +} diff --git a/resources/[carscripts]/lc_fuel/lang/zh-cn.lua b/resources/[carscripts]/lc_fuel/lang/zh-cn.lua index b2094525f..fe13b87e9 100644 --- a/resources/[carscripts]/lc_fuel/lang/zh-cn.lua +++ b/resources/[carscripts]/lc_fuel/lang/zh-cn.lua @@ -1,41 +1,49 @@ -if not Lang then Lang = {} end -Lang['zh-cn'] = { - ['markers'] = { - ['open_refuel'] = "按下 ~INPUT_CONTEXT~ 进行加油", - ['open_recharge'] = "按下 ~INPUT_CONTEXT~ 进行充电", - ['interact_with_vehicle'] = "按下 ~y~E~w~ 与车辆交互", - ['return_nozzle'] = "按下 ~INPUT_CONTEXT~ 归还油枪", - }, - ['target'] = { - ['open_refuel'] = "进行加油", - ['open_recharge'] = "进行充电", - ['start_refuel'] = "开始加油", - ['stop_refuel'] = "终止加油", - ['return_nozzle'] = "归还油枪", - }, - ['blip_text'] = "加油站", - ['not_enough_refuel'] = "您已用完所支付的燃油。如需继续,请购买更多燃油", - ['invalid_value'] = "无效数值", - ['not_enough_money'] = "您的账户余额不足支付当前金额 ($%s) ", - ['not_enough_stock'] = "本站点当前无充足库存完成该操作", - ['refuel_paid'] = "本次燃油补给消费 $%s", - ['returned_fuel'] = "你已归还%s升燃料,并收到$%s", - ['returned_charge'] = "你已归还%skWh电量,并收到$%s", - ['jerry_can_paid'] = "本次便携油桶购买消费 $%s", - ['too_far_away'] = "您与加油设备间距超出操作范围!", - ['vehicle_refueled'] = "车辆成功加注燃油 %s 升(L)", - ['vehicle_recharged'] = "车辆成功充入电能 %s 千瓦时(kWh)", - ['vehicle_tank_full'] = "车辆油箱已达最大容量", - ['vehicle_tank_emptied'] = "车辆油箱燃油已全部清空", - ['vehicle_not_found'] = "未检测到关联载具", - ['pump_not_found'] = "未检测到可用加油设备", - ['vehicle_wrong_fuel'] = "误用燃油类型导致车辆受损, 请立即停止操作!", - ['incompatible_fuel'] = "燃油类型与车辆配置不匹配,请选择适配的加油方案.", - ['owned_gas_stations'] = { - ['balance_jerry_can'] = "便携式油桶销售量 (%s 升)", - ['balance_fuel'] = "燃油销售总量 (%s 升)", - ['balance_electric'] = "电能销售收入 (%s 千瓦时)", - ['refund_fuel'] = "燃料已退款(%s 升)", - ['refund_electric'] = "电力已退款(%s 千瓦时)", - } +if not Lang then Lang = {} end +Lang['zh-cn'] = { + ['markers'] = { + ['open_refuel'] = "按下 ~INPUT_CONTEXT~ 进行加油", + ['open_recharge'] = "按下 ~INPUT_CONTEXT~ 进行充电", + ['interact_with_vehicle'] = "按下 ~y~E~w~ 与车辆交互", + ['return_nozzle'] = "按下 ~INPUT_CONTEXT~ 归还油枪", + }, + ['target'] = { + ['open_refuel'] = "进行加油", + ['open_recharge'] = "进行充电", + ['start_refuel'] = "开始加油", + ['stop_refuel'] = "终止加油", + ['return_nozzle'] = "归还油枪", + }, + ['blip_text'] = "加油站", + ['not_enough_refuel'] = "您已用完所支付的燃油。如需继续,请购买更多燃油", + ['invalid_value'] = "无效数值", + ['not_enough_money'] = "您的账户余额不足支付当前金额 ($%s) ", + ['not_enough_stock'] = "本站点当前无充足库存完成该操作", + ['refuel_paid'] = "本次燃油补给消费 $%s", + ['returned_fuel'] = "你已归还%s升燃料,并收到$%s", + ['returned_charge'] = "你已归还%skWh电量,并收到$%s", + ['jerry_can_paid'] = "本次便携油桶购买消费 $%s", + ['too_far_away'] = "您与加油设备间距超出操作范围!", + ['vehicle_refueled'] = "车辆成功加注燃油 %s 升(L)", + ['vehicle_recharged'] = "车辆成功充入电能 %s 千瓦时(kWh)", + ['vehicle_tank_full'] = "车辆油箱已达最大容量", + ['vehicle_tank_emptied'] = "车辆油箱燃油已全部清空", + ['vehicle_not_found'] = "未检测到关联载具", + ['pump_not_found'] = "未检测到可用加油设备", + ['vehicle_wrong_fuel'] = "误用燃油类型导致车辆受损, 请立即停止操作!", + ['incompatible_fuel'] = "燃油类型与车辆配置不匹配,请选择适配的加油方案.", + ['owned_gas_stations'] = { + ['balance_jerry_can'] = "便携式油桶销售量 (%s 升)", + ['balance_fuel'] = "燃油销售总量 (%s 升)", + ['balance_electric'] = "电能销售收入 (%s 千瓦时)", + ['refund_fuel'] = "燃料已退款(%s 升)", + ['refund_electric'] = "电力已退款(%s 千瓦时)", + }, + ['fuel_types'] = { + ['type_title'] = "燃料类型:%s", + ['electric'] = "电动", + ['regular'] = "92号燃油", + ['plus'] = "95号燃油", + ['premium'] = "98号燃油", + ['diesel'] = "柴油", + }, } \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/nui/css/style.css b/resources/[carscripts]/lc_fuel/nui/css/style.css index c2c2fda76..6d619d04d 100644 --- a/resources/[carscripts]/lc_fuel/nui/css/style.css +++ b/resources/[carscripts]/lc_fuel/nui/css/style.css @@ -1,1004 +1,1004 @@ -@font-face { - font-family: 'Technology'; - font-style: normal; - font-weight: normal; - src: url('../fonts/Technology.woff') format('woff'); -} - -body { - margin: 0; -} - -/* PUMP structure */ -.gas-pump-container { - display: flex; - justify-content: center; - align-items: flex-end; - height: 100%; - width: 100%; - background: #00000070; -} - -.gas-pump-elements-container { - position: absolute; - bottom: 69px; - width: 100%; - display: grid; - justify-items: center; - line-height: 18px; -} - -.gas-pump-values-container { - display: grid; - grid-template-columns: 110px 274px 224.18px 186.52px 110px; - white-space: nowrap; -} - -.digital-text { - color: #EBFEFF; - font-family: Technology; - font-size: 17px; - letter-spacing: 1.7px; - display: inline-block; -} - -.digital-text-2 { - color: #EBFEFF; - text-align: center; - font-family: Technology; - font-size: 15px; - font-style: normal; - font-weight: 700; - line-height: normal; - letter-spacing: 1.5px; - display: inline-block; -} - -.digital-text.dark { - color: #263D3D; -} - -/* Side buttons */ -.controls-container { - display: grid; - justify-items: start; - align-items: center; - font-family: 'Lexend'; -} - -.controls-container > button { - width: 37px; - height: 37px; - border-radius: 100px; - border: 1px solid rgba(200, 200, 200, 0.15); - background: rgba(0, 0, 0, 0.4); - transition: all 0.2s ease, cursor 0.2s ease; - - color: #FFF; - font-size: 15px; - font-style: normal; - font-weight: 600; - line-height: normal; - - /* Align the text */ - display: flex; - align-items: center; - justify-content: center; -} - -.controls-container > button:hover { - cursor: pointer; - background: rgba(50, 50, 50, 0.4); -} - -/* Interactive buttons */ -.gas-pump-interactive-button { - position: absolute; - bottom: 520px; - left: 50%; - transform: translateX(140px); - border-radius: 100px; - border: 1px solid rgba(255, 255, 255, 0.05); - background: rgb(255 255 255 / 9%); - display: inline-flex; - padding: 7px; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.gas-pump-interactive-inner-button { - width: 38px; - height: 38px; - border-radius: 100px; - border: 1px solid rgba(255, 255, 255, 0.15); - background: rgb(255 255 255 / 29%); - transition: all 0.2s ease, cursor 0.2s ease; -} - -.gas-pump-interactive-button:hover { - border: 1px solid rgba(255, 255, 255, 0.1); - background: rgb(255 255 255 / 6%); - cursor: pointer; -} - -.gas-pump-interactive-button:hover .gas-pump-interactive-inner-button { - border: 1px solid rgba(255, 255, 255, 0.25); - background: rgb(255 255 255 / 20%); -} - -/* Center side of the PUMP */ -.quantity-input-container { - display: flex; - justify-content: center; - margin: 0px 15px 38px; -} - -.quantity-input-container > input { - width: 68%; - padding: 0px 5px; - text-align: center; - border:none; - background-image:none; - background-color:transparent; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - outline: none; -} - -/* Chrome, Safari, Edge, Opera */ -.quantity-input-container > input::-webkit-outer-spin-button, -.quantity-input-container > input::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; -} - -/* Firefox */ -.quantity-input-container > input[type=number] { - -moz-appearance: textfield; -} - -.quantity-input-container > button { - color: #EBFEFF; - font-family: Technology; - font-weight: 700; - width: 31px; - height: 31px; - padding: 1px; - - border: 2px solid #72697E; - background: #251825; - - transition: all 0.2s ease, cursor 0.2s ease; -} -.quantity-input-container > button:hover { - cursor: pointer; - background: rgba(99, 105, 105, 0.15); -} -.quantity-input-container > .sub { - font-size: 25px; -} -.quantity-input-container > .add { - font-size: 17px; -} - -.price-per-liter { - margin: 10px 30px 26px 15px; -} - -.stock-values-container { - display: grid; - grid-template-columns: 50% 50%; -} - -.stock-values-container > span { - margin: 0px 14px 13px; -} - -/* Right side of the PUMP */ -.money-display-container { - padding: 22px 18px 0px 11px; - display: grid; - justify-items: start; -} - -.bank-balance { - margin: 6px; -} - -.cash-balance { - margin: 26px 6px 15px; -} - -.confirm-button { - color: #263D3D; - font-family: "Lexend Zetta"; - font-weight: 800; - letter-spacing: -2.2px; - width: 100%; - height: fit-content; - padding: 6px 6px; - font-size: 13px; - background: #909B9C; - border: solid 2px #697474; - transition: all 0.2s ease, cursor 0.2s ease; -} - -.confirm-button:hover { - cursor: pointer; - background: #a3afb1; -} - -/* Footer of the PUMP */ -.gas-pump-fuel-list-container { - display: grid; - grid-template-columns: 26% 26% 26% 22%; - margin-top: 22px; - height: 34px; - align-items: center; - width: 685px; -} - -.fuel-type-button { - height: -webkit-fill-available; - background: #ffffff00; - border: none; - color: rgba(255, 255, 255, 0.45); - text-align: right; - font-family: Arial; - font-size: 15px; - font-weight: 700; - padding-right: 12px; - margin: 1px; - transition: all 0.1s ease, color 0.1s ease; -} - -.fuel-type-button:hover { - cursor: pointer; -} - -@keyframes pulseRegular { - 0% { - background-color: rgba(255, 255, 255, 0.15); - box-shadow: rgb(255 255 255 / 30%) 0px 0px 8px 2px, rgb(255 255 255 / 10%) 0px 0px 4px 2px; - } - 50% { - background-color: rgba(255, 255, 255, 0.35); - box-shadow: rgb(255 255 255 / 50%) 0px 0px 12px 4px, rgb(255 255 255 / 20%) 0px 0px 6px 3px; - } - 100% { - background-color: rgba(255, 255, 255, 0.15); - box-shadow: rgb(255 255 255 / 30%) 0px 0px 8px 2px, rgb(255 255 255 / 10%) 0px 0px 4px 2px; - } -} - -@keyframes pulsePlus { - 0% { - background-color: rgba(0, 119, 255, 0.15); - box-shadow: rgba(0, 119, 255, 0.2) 0px 0px 8px 2px, rgba(0, 119, 255, 0.22) 0px 0px 4px 2px; - } - 50% { - background-color: rgba(0, 119, 255, 0.35); - box-shadow: rgba(0, 119, 255, 0.5) 0px 0px 12px 4px, rgba(0, 119, 255, 0.35) 0px 0px 6px 3px; - } - 100% { - background-color: rgba(0, 119, 255, 0.15); - box-shadow: rgba(0, 119, 255, 0.2) 0px 0px 8px 2px, rgba(0, 119, 255, 0.22) 0px 0px 4px 2px; - } -} - -@keyframes pulsePremium { - 0% { - background-color: rgba(255, 0, 0, 0.15); - box-shadow: rgba(255, 0, 0, 0.2) 0px 0px 8px 2px, rgba(255, 0, 0, 0.22) 0px 0px 4px 2px; - } - 50% { - background-color: rgba(255, 0, 0, 0.35); - box-shadow: rgba(255, 0, 0, 0.5) 0px 0px 12px 4px, rgba(255, 0, 0, 0.35) 0px 0px 6px 3px; - } - 100% { - background-color: rgba(255, 0, 0, 0.15); - box-shadow: rgba(255, 0, 0, 0.2) 0px 0px 8px 2px, rgba(255, 0, 0, 0.22) 0px 0px 4px 2px; - } -} - -@keyframes pulseDiesel { - 0% { - background-color: rgba(0, 165, 0, 0.15); - box-shadow: rgba(0, 165, 0, 0.2) 0px 0px 8px 2px, rgba(0, 165, 0, 0.20) 0px 0px 4px 2px; - } - 50% { - background-color: rgba(0, 165, 0, 0.35); - box-shadow: rgba(0, 165, 0, 0.5) 0px 0px 12px 4px, rgba(0, 165, 0, 0.30) 0px 0px 6px 3px; - } - 100% { - background-color: rgba(0, 165, 0, 0.15); - box-shadow: rgba(0, 165, 0, 0.2) 0px 0px 8px 2px, rgba(0, 165, 0, 0.20) 0px 0px 4px 2px; - } -} - -.fuel-type-button.regular.selected, -.fuel-type-button.regular:hover { - background: rgba(255, 255, 255, 0.15); - cursor: pointer; - animation: pulseRegular 3s infinite ease-in-out; -} - -.fuel-type-button.plus.selected, -.fuel-type-button.plus:hover { - background: rgba(0, 119, 255, 0.15); - cursor: pointer; - animation: pulsePlus 3s infinite ease-in-out; -} - -.fuel-type-button.premium.selected, -.fuel-type-button.premium:hover { - background: rgba(255, 0, 0, 0.15); - cursor: pointer; - animation: pulsePremium 3s infinite ease-in-out; -} - -.fuel-type-button.diesel.selected, -.fuel-type-button.diesel:hover { - background: rgba(0, 165, 0, 0.15); - cursor: pointer; - animation: pulseDiesel 3s infinite ease-in-out; -} - - -.fuel-type-button.regular { - color: rgba(0, 0, 0, 0.75); -} - -/* Refuel display */ -.refuel-display-body { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: flex; - align-items: end; - justify-content: center; - z-index: 1000; - font-family: Technology; -} - - -.refuel-display-container { - margin-bottom: 10vh; - padding: 30px 40px; - background-image: url('../images/gas_refuel_display.png'); - background-repeat: no-repeat; - background-position: center; - width: 205px; - height: 108px; - background-size: 100%; -} - -.refuel-display-info-container { - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.refuel-display-label { - color: rgba(255, 255, 255, 0.6); - font-size: 15px; - font-style: normal; - font-weight: 400; - line-height: normal; - letter-spacing: 0.5; -} - -.refuel-display-value { - color: #FFF; - font-size: 25px; - font-style: normal; - font-weight: 400; - line-height: normal; - letter-spacing: 1; -} - -.refuel-display-liters { - color: #FFF; - font-size: 19px; -} - -/* Recharge display */ -.recharge-display-body { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: flex; - align-items: end; - justify-content: center; - z-index: 1000; - font-family: Inter; - color: white; -} - -.recharge-display-container { - margin-bottom: 10vh; - background-image: url('../images/electric_charger_display.png'); - background-repeat: no-repeat; - background-position: center; - padding: 55px; - width: 210px; - height: 115px; - background-size: 100%; -} - -.recharge-display-title-container { - display: flex; - justify-content: center; -} - -.recharge-display-title-container h3 { - margin: 5px 0px 0px; -} - -.recharge-display-battery-container { - display: flex; - gap: 10px; - align-items: center; - - border-top: 1px solid #ffffff27; - padding-top: 10px; - margin-top: 10px; -} - -:root { - /* color */ - --gradient-color-red: linear-gradient(90deg, - hsl( 7, 89%, 46%) 15%, - hsl(11, 93%, 68%) 100%); - --gradient-color-orange: linear-gradient(90deg, - hsl( 22, 89%, 46%) 15%, - hsl(54, 93%, 68%) 100%); - --gradient-color-yellow: linear-gradient(90deg, - hsl( 54, 89%, 46%) 15%, - hsl(90, 93%, 68%) 100%); - --gradient-color-green: linear-gradient(90deg, - hsl( 92, 89%, 46%) 15%, - hsl(90, 93%, 68%) 100%); -} - -.recharge-display-battery-bar-container { - position: relative; - width: 100%; - height: 10px; - background-color: rgba(255, 255, 255, 0.13); - backdrop-filter: blur(10px); - border: 2px solid rgba(247, 240, 240, 0.1); - border-radius: 3rem; - justify-self: flex-start; - -} -.recharge-display-battery-level { - position: absolute; - inset: 2px; - border-radius: 3rem; - overflow: hidden; -} -.recharge-display-battery-liquid { - position: absolute; - bottom: 0; - top: 0; - left: 0; - width: 36px; - background: var(--gradient-color-yellow); - box-shadow: inset 12px 0 12px hsla(0, 0, 0, 0.15), - inset -12px 0 12px hsla(0, 0, 0, 0.1); - transition: 0.3s; - -} -.recharge-display-battery-liquid::after { - content: ""; - position: absolute; - width: 8px; - background: var(--gradient-color-yellow); - box-shadow: inset 0px -3px 6px hsla(0, 0, 0, 0.2); - top: 0; - left: 0; - margin: 0 auto; - right: -4px; - border-radius: 50%; -} - -.recharge-display-remaining-time-container { - display: flex; - flex-direction: column; - align-items: center; - - border-top: 1px solid #ffffff27; - padding-top: 5px; - margin-top: 10px; -} - -.recharge-display-remaining-time-title { - color: #FFF; - font-family: Inter; - font-size: 13px; - font-style: normal; - font-weight: 500; - line-height: normal; -} - -.recharge-display-remaining-time-value { - color: #FFF; - font-family: Inter; - font-size: 16px; - font-style: normal; - font-weight: 700; - line-height: normal; -} - -/* ELECTRIC CHARGER structure */ -.electric-charger-container { - display: flex; - justify-content: center; - align-items: flex-end; - height: 100%; - width: 100%; - background: #00000070; - font-family: Inter !important; -} - -.electric-charger-elements-container { - position: absolute; - bottom: 440px; - width: 540px; - height: 535px; - margin-right: 65px; - display: grid; - justify-items: center; -} - -.electric-charger-button { - padding: 10px; - width: 285px; - border-radius: 10px; - background: rgba(255, 255, 255, 0.15); - border: 0; - transition: all 0.2s ease, cursor 0.2s ease; - - color: #FFF; - text-align: center; - font-family: Inter; - font-size: 15px; - font-style: normal; - font-weight: 700; - line-height: normal; -} - -.electric-charger-button:hover { - cursor: pointer; - background: rgba(255, 255, 255, 0.24); -} - -.electric-charger-title { - color: #FFF; - text-align: center; - font-size: 40px; - font-style: normal; - font-weight: 900; - line-height: normal; - margin: 50px 0px -} - -.electric-charger-subtitle { - color: #FFF; - text-align: center; - font-size: 20px; - font-style: normal; - font-weight: 500; - line-height: normal; - margin: 5px 0px 40px 0px -} - -.electric-charger-buttons-container { - display: flex; - gap: 15px; - margin-bottom: 16px; -} - -.electric-charger-return-container { - position: absolute; - top: 20px; - left: 20px; -} - -.electric-charger-return-container svg { - height: 25px; - fill: #ffffffa8; -} - -.electric-charger-return-container svg:hover { - fill: #ffffffcc; - cursor: pointer; - transition: all 0.2s ease, cursor 0.2s ease; -} - -/* RECHARGE TYPE */ -.electric-charger-type-container { - display: flex; - flex-direction: column; - align-items: center; -} - -.electric-charger-type-input { - display: none; -} - -.electric-charger-type-input:not(:disabled) ~ .electric-charger-type-label { - cursor: pointer; -} - -.electric-charger-type-container .electric-charger-type-label { - height: 150px; - padding: 14px 13px 10px 13px; -} - -.electric-charger-type-label { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - - text-align: center; - height: 125px; - width: 115px; - padding: 9px; - border-radius: 10px; - - color: #FFF; - font-size: 15px; - font-style: normal; - font-weight: 700; - line-height: normal; - - background: rgba(255, 255, 255, 0.15); - border: 1px solid rgba(255, 255, 255, 0); -} - -.electric-charger-type-label:hover { - cursor: pointer; - background: rgba(255, 255, 255, 0.24); - transition: all 0.2s ease, cursor 0.2s ease; -} - -/* Disable hover when input is disabled */ -input.electric-charger-type-input:disabled + .electric-charger-type-label { - pointer-events: none; /* Disable hover and interaction */ - cursor: not-allowed; /* Optional: show not-allowed cursor */ - opacity: 0.5; /* Indicate visually that it's disabled */ -} - -.electric-charger-type-label h2 { - margin: 15 0 5 0; -} - -.electric-charger-type-input:checked + .electric-charger-type-label { - border: 1px solid rgba(255, 255, 255, 0.35); - background: rgba(255, 255, 255, 0.35); -} - -.electric-charger-type-label-item-container { - display: flex; - gap: 5px; - - border-top: 1px solid #ffffff15; - padding-top: 3px; - margin-top: 3px; -} - -.electric-charger-type-label-item-container svg { - height: 15px; - fill: #FFF; -} - -.electric-charger-type-label-item-container span { - font-size: 13px; -} - -/* SELECT AMOUNT */ -.electric-charger-amount-container { - display: flex; - flex-direction: column; - align-items: center; -} - -/* PAYMENT METHOD */ -.electric-charger-payment-container { - display: flex; - flex-direction: column; - align-items: center; -} - -.electric-charger-amount-input-container { - display: flex; - gap: 10px; -} - -.electric-charger-amount-input { - display: flex; - width: 125px; - padding: 10px; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 7px; - flex-shrink: 0; - border-radius: 10px; - border: 1px solid rgba(255, 255, 255, 0.25); - background: rgba(255, 255, 255, 0.15); - - color: rgba(255, 255, 255, 0.6); - text-align: center; - font-size: 15px; - font-style: normal; - font-weight: 700; - line-height: normal; -} - -.electric-charger-amount-input::-webkit-outer-spin-button, -.electric-charger-amount-input::-webkit-inner-spin-button { --webkit-appearance: none; -margin: 0; -} - -.recharge-sub, .recharge-add { - display: flex; - width: 44px; - height: 44px; - padding: 10px; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 7px; - flex-shrink: 0; - border-radius: 10px; - border: 1px solid rgba(255, 255, 255, 0.35); - background: rgba(255, 255, 255, 0.35); - - color: #FFF; - text-align: center; - font-style: normal; - font-weight: 400; - line-height: normal; -} - -.recharge-sub { - font-size: 30px; - padding-bottom: 15px; -} - -.recharge-add { - font-size: 25px; -} - -.electric-amount-info-container { - margin: 20px 0px 15px 0px; -} - -.electric-amount-progress-container { - display: flex; - align-items: center; - gap: 10px; - width: 300px; - margin-bottom: 5px; -} - -.electric-time-to-recharge { - margin-left: 10px; - color: #FFF; - font-size: 15px; - font-style: normal; - font-weight: 600; - line-height: normal; -} - -.electric-time-to-recharge > span { - font-weight: 800; -} - -/* Makes the texts not selectable */ -body { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - background: transparent !important; - user-select: none; -} - -/* Modal */ -.modal { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.6); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -} - -.modal-content { - padding: 20px; - border-radius: 8px; - text-align: center; - width: 300px; - box-shadow: 0 4px 8px 2px rgba(0, 0, 0, 0.2); - position: relative; - - border-radius: 5px; - border: 2px solid rgba(75, 75, 75, 0.50); - background: rgba(0, 0, 0, 0.5); - color: white; - font-family: "Lexend"; - margin-bottom: 100px; -} - -.close-button { - position: absolute; - top: 2px; - right: 13px; - font-size: 24px; - font-weight: bold; - color: #6a6a6a; - cursor: pointer; -} - -.modal-buttons { - display: flex; - justify-content: space-around; - margin-top: 20px; - gap: 15px; -} - -.modal-button { - color: white; - padding: 10px 20px; - border: none; - cursor: pointer; - font-size: 16px; - - border-radius: 5px; - background: rgba(255, 255, 255, 0.12); - font-family: "Lexend"; - font-style: normal; - font-weight: 700; - line-height: normal; - transition: all 0.2s ease, cursor 0.2s ease; -} - -.modal-button:hover { - background-color: #135015; -} - -.modal-button.cancel-button:hover { - background-color: #501313; -} - -/* Chart dialog */ -.chart-dialog { - position: fixed; - width: 450px; - box-shadow: 0 4px 8px 2px rgba(0, 0, 0, 0.3); - z-index: 9999; - border-radius: 8px; - text-align: center; - border: 2px solid rgba(45, 45, 45, 0.60); - background: rgba(0, 0, 0, 0.7); - color: white; - font-family: "Lexend"; - flex-direction: column; - overflow: hidden; -} - -.chart-dialog .dialog-body { - flex: 1; - position: relative; - overflow: hidden; -} - -.chart-dialog .dialog-header, .chart-dialog .dialog-footer { - padding: 10px; - flex-shrink: 0; - font-weight: bold; - font-size: 15px; -} - -.chart-dialog .dialog-header { - cursor: move; -} - -.chart-dialog .dialog-footer { - display: flex; - align-items: flex-end; - justify-content: space-between; - font-size: 13px; - font-weight: 400; -} - -.chart-dialog .dialog-footer-inputs { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 5px; -} - -.chart-dialog .decrease-chart-recording, -.chart-dialog .increase-chart-recording { - color: inherit; - font: inherit; - - background: #0000003d; - border: 1px solid #a7a7a745; - border-radius: 5px; - - width: 24px; - height: 24px; - text-align: center; - padding: 0px; - font-size: 14px; - font-weight: 300; -} - -.chart-dialog .increase-chart-recording { - padding-bottom: 2px; -} - -.chart-dialog .chart-recording-input-container { - display: flex; - align-items: center; - gap: 5px; -} - -.chart-dialog canvas { - margin-bottom: 20px; -} - -/* Resizable properties */ -.ui-resizable-e { - cursor: e-resize; - width: 7px; - right: -5px; - top: 0; - height: 100%; -} -.ui-resizable-s { - cursor: s-resize; - height: 7px; - width: 100%; - bottom: -5px; - left: 0; -} -.ui-resizable-se { - width: 16px; - height: 16px; - bottom: 0; - right: 0; - cursor: se-resize; - background: url('data:image/svg+xml;utf8,') no-repeat center center; - background-size: 12px 12px; - opacity: 0.5; -} -.ui-resizable-se:hover::after, .ui-resizable-se:hover { - opacity: 0.8; -} -.ui-resizable-handle { - position: absolute; - font-size: 0.1px; - display: block; - -ms-touch-action: none; - touch-action: none; +@font-face { + font-family: 'Technology'; + font-style: normal; + font-weight: normal; + src: url('../fonts/Technology.woff') format('woff'); +} + +body { + margin: 0; +} + +/* PUMP structure */ +.gas-pump-container { + display: flex; + justify-content: center; + align-items: flex-end; + height: 100%; + width: 100%; + background: #00000070; +} + +.gas-pump-elements-container { + position: absolute; + bottom: 69px; + width: 100%; + display: grid; + justify-items: center; + line-height: 18px; +} + +.gas-pump-values-container { + display: grid; + grid-template-columns: 110px 274px 224.18px 186.52px 110px; + white-space: nowrap; +} + +.digital-text { + color: #EBFEFF; + font-family: Technology; + font-size: 17px; + letter-spacing: 1.7px; + display: inline-block; +} + +.digital-text-2 { + color: #EBFEFF; + text-align: center; + font-family: Technology; + font-size: 15px; + font-style: normal; + font-weight: 700; + line-height: normal; + letter-spacing: 1.5px; + display: inline-block; +} + +.digital-text.dark { + color: #263D3D; +} + +/* Side buttons */ +.controls-container { + display: grid; + justify-items: start; + align-items: center; + font-family: 'Lexend'; +} + +.controls-container > button { + width: 37px; + height: 37px; + border-radius: 100px; + border: 1px solid rgba(200, 200, 200, 0.15); + background: rgba(0, 0, 0, 0.4); + transition: all 0.2s ease, cursor 0.2s ease; + + color: #FFF; + font-size: 15px; + font-style: normal; + font-weight: 600; + line-height: normal; + + /* Align the text */ + display: flex; + align-items: center; + justify-content: center; +} + +.controls-container > button:hover { + cursor: pointer; + background: rgba(50, 50, 50, 0.4); +} + +/* Interactive buttons */ +.gas-pump-interactive-button { + position: absolute; + bottom: 520px; + left: 50%; + transform: translateX(140px); + border-radius: 100px; + border: 1px solid rgba(255, 255, 255, 0.05); + background: rgb(255 255 255 / 9%); + display: inline-flex; + padding: 7px; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.gas-pump-interactive-inner-button { + width: 38px; + height: 38px; + border-radius: 100px; + border: 1px solid rgba(255, 255, 255, 0.15); + background: rgb(255 255 255 / 29%); + transition: all 0.2s ease, cursor 0.2s ease; +} + +.gas-pump-interactive-button:hover { + border: 1px solid rgba(255, 255, 255, 0.1); + background: rgb(255 255 255 / 6%); + cursor: pointer; +} + +.gas-pump-interactive-button:hover .gas-pump-interactive-inner-button { + border: 1px solid rgba(255, 255, 255, 0.25); + background: rgb(255 255 255 / 20%); +} + +/* Center side of the PUMP */ +.quantity-input-container { + display: flex; + justify-content: center; + margin: 0px 15px 38px; +} + +.quantity-input-container > input { + width: 68%; + padding: 0px 5px; + text-align: center; + border:none; + background-image:none; + background-color:transparent; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + outline: none; +} + +/* Chrome, Safari, Edge, Opera */ +.quantity-input-container > input::-webkit-outer-spin-button, +.quantity-input-container > input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +/* Firefox */ +.quantity-input-container > input[type=number] { + -moz-appearance: textfield; +} + +.quantity-input-container > button { + color: #EBFEFF; + font-family: Technology; + font-weight: 700; + width: 31px; + height: 31px; + padding: 1px; + + border: 2px solid #72697E; + background: #251825; + + transition: all 0.2s ease, cursor 0.2s ease; +} +.quantity-input-container > button:hover { + cursor: pointer; + background: rgba(99, 105, 105, 0.15); +} +.quantity-input-container > .sub { + font-size: 25px; +} +.quantity-input-container > .add { + font-size: 17px; +} + +.price-per-liter { + margin: 10px 30px 26px 15px; +} + +.stock-values-container { + display: grid; + grid-template-columns: 50% 50%; +} + +.stock-values-container > span { + margin: 0px 14px 13px; +} + +/* Right side of the PUMP */ +.money-display-container { + padding: 22px 18px 0px 11px; + display: grid; + justify-items: start; +} + +.bank-balance { + margin: 6px; +} + +.cash-balance { + margin: 26px 6px 15px; +} + +.confirm-button { + color: #263D3D; + font-family: "Lexend Zetta"; + font-weight: 800; + letter-spacing: -2.2px; + width: 100%; + height: fit-content; + padding: 6px 6px; + font-size: 13px; + background: #909B9C; + border: solid 2px #697474; + transition: all 0.2s ease, cursor 0.2s ease; +} + +.confirm-button:hover { + cursor: pointer; + background: #a3afb1; +} + +/* Footer of the PUMP */ +.gas-pump-fuel-list-container { + display: grid; + grid-template-columns: 26% 26% 26% 22%; + margin-top: 22px; + height: 34px; + align-items: center; + width: 685px; +} + +.fuel-type-button { + height: -webkit-fill-available; + background: #ffffff00; + border: none; + color: rgba(255, 255, 255, 0.45); + text-align: right; + font-family: Arial; + font-size: 15px; + font-weight: 700; + padding-right: 12px; + margin: 1px; + transition: all 0.1s ease, color 0.1s ease; +} + +.fuel-type-button:hover { + cursor: pointer; +} + +@keyframes pulseRegular { + 0% { + background-color: rgba(255, 255, 255, 0.15); + box-shadow: rgb(255 255 255 / 30%) 0px 0px 8px 2px, rgb(255 255 255 / 10%) 0px 0px 4px 2px; + } + 50% { + background-color: rgba(255, 255, 255, 0.35); + box-shadow: rgb(255 255 255 / 50%) 0px 0px 12px 4px, rgb(255 255 255 / 20%) 0px 0px 6px 3px; + } + 100% { + background-color: rgba(255, 255, 255, 0.15); + box-shadow: rgb(255 255 255 / 30%) 0px 0px 8px 2px, rgb(255 255 255 / 10%) 0px 0px 4px 2px; + } +} + +@keyframes pulsePlus { + 0% { + background-color: rgba(0, 119, 255, 0.15); + box-shadow: rgba(0, 119, 255, 0.2) 0px 0px 8px 2px, rgba(0, 119, 255, 0.22) 0px 0px 4px 2px; + } + 50% { + background-color: rgba(0, 119, 255, 0.35); + box-shadow: rgba(0, 119, 255, 0.5) 0px 0px 12px 4px, rgba(0, 119, 255, 0.35) 0px 0px 6px 3px; + } + 100% { + background-color: rgba(0, 119, 255, 0.15); + box-shadow: rgba(0, 119, 255, 0.2) 0px 0px 8px 2px, rgba(0, 119, 255, 0.22) 0px 0px 4px 2px; + } +} + +@keyframes pulsePremium { + 0% { + background-color: rgba(255, 0, 0, 0.15); + box-shadow: rgba(255, 0, 0, 0.2) 0px 0px 8px 2px, rgba(255, 0, 0, 0.22) 0px 0px 4px 2px; + } + 50% { + background-color: rgba(255, 0, 0, 0.35); + box-shadow: rgba(255, 0, 0, 0.5) 0px 0px 12px 4px, rgba(255, 0, 0, 0.35) 0px 0px 6px 3px; + } + 100% { + background-color: rgba(255, 0, 0, 0.15); + box-shadow: rgba(255, 0, 0, 0.2) 0px 0px 8px 2px, rgba(255, 0, 0, 0.22) 0px 0px 4px 2px; + } +} + +@keyframes pulseDiesel { + 0% { + background-color: rgba(0, 165, 0, 0.15); + box-shadow: rgba(0, 165, 0, 0.2) 0px 0px 8px 2px, rgba(0, 165, 0, 0.20) 0px 0px 4px 2px; + } + 50% { + background-color: rgba(0, 165, 0, 0.35); + box-shadow: rgba(0, 165, 0, 0.5) 0px 0px 12px 4px, rgba(0, 165, 0, 0.30) 0px 0px 6px 3px; + } + 100% { + background-color: rgba(0, 165, 0, 0.15); + box-shadow: rgba(0, 165, 0, 0.2) 0px 0px 8px 2px, rgba(0, 165, 0, 0.20) 0px 0px 4px 2px; + } +} + +.fuel-type-button.regular.selected, +.fuel-type-button.regular:hover { + background: rgba(255, 255, 255, 0.15); + cursor: pointer; + animation: pulseRegular 3s infinite ease-in-out; +} + +.fuel-type-button.plus.selected, +.fuel-type-button.plus:hover { + background: rgba(0, 119, 255, 0.15); + cursor: pointer; + animation: pulsePlus 3s infinite ease-in-out; +} + +.fuel-type-button.premium.selected, +.fuel-type-button.premium:hover { + background: rgba(255, 0, 0, 0.15); + cursor: pointer; + animation: pulsePremium 3s infinite ease-in-out; +} + +.fuel-type-button.diesel.selected, +.fuel-type-button.diesel:hover { + background: rgba(0, 165, 0, 0.15); + cursor: pointer; + animation: pulseDiesel 3s infinite ease-in-out; +} + + +.fuel-type-button.regular { + color: rgba(0, 0, 0, 0.75); +} + +/* Refuel display */ +.refuel-display-body { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + align-items: end; + justify-content: center; + z-index: 1000; + font-family: Technology; +} + + +.refuel-display-container { + margin-bottom: 10vh; + padding: 30px 40px; + background-image: url('../images/gas_refuel_display.png'); + background-repeat: no-repeat; + background-position: center; + width: 205px; + height: 108px; + background-size: 100%; +} + +.refuel-display-info-container { + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.refuel-display-label { + color: rgba(255, 255, 255, 0.6); + font-size: 15px; + font-style: normal; + font-weight: 400; + line-height: normal; + letter-spacing: 0.5; +} + +.refuel-display-value { + color: #FFF; + font-size: 25px; + font-style: normal; + font-weight: 400; + line-height: normal; + letter-spacing: 1; +} + +.refuel-display-liters { + color: #FFF; + font-size: 19px; +} + +/* Recharge display */ +.recharge-display-body { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + align-items: end; + justify-content: center; + z-index: 1000; + font-family: Inter; + color: white; +} + +.recharge-display-container { + margin-bottom: 10vh; + background-image: url('../images/electric_charger_display.png'); + background-repeat: no-repeat; + background-position: center; + padding: 55px; + width: 210px; + height: 115px; + background-size: 100%; +} + +.recharge-display-title-container { + display: flex; + justify-content: center; +} + +.recharge-display-title-container h3 { + margin: 5px 0px 0px; +} + +.recharge-display-battery-container { + display: flex; + gap: 10px; + align-items: center; + + border-top: 1px solid #ffffff27; + padding-top: 10px; + margin-top: 10px; +} + +:root { + /* color */ + --gradient-color-red: linear-gradient(90deg, + hsl( 7, 89%, 46%) 15%, + hsl(11, 93%, 68%) 100%); + --gradient-color-orange: linear-gradient(90deg, + hsl( 22, 89%, 46%) 15%, + hsl(54, 93%, 68%) 100%); + --gradient-color-yellow: linear-gradient(90deg, + hsl( 54, 89%, 46%) 15%, + hsl(90, 93%, 68%) 100%); + --gradient-color-green: linear-gradient(90deg, + hsl( 92, 89%, 46%) 15%, + hsl(90, 93%, 68%) 100%); +} + +.recharge-display-battery-bar-container { + position: relative; + width: 100%; + height: 10px; + background-color: rgba(255, 255, 255, 0.13); + backdrop-filter: blur(10px); + border: 2px solid rgba(247, 240, 240, 0.1); + border-radius: 3rem; + justify-self: flex-start; + +} +.recharge-display-battery-level { + position: absolute; + inset: 2px; + border-radius: 3rem; + overflow: hidden; +} +.recharge-display-battery-liquid { + position: absolute; + bottom: 0; + top: 0; + left: 0; + width: 36px; + background: var(--gradient-color-yellow); + box-shadow: inset 12px 0 12px hsla(0, 0, 0, 0.15), + inset -12px 0 12px hsla(0, 0, 0, 0.1); + transition: 0.3s; + +} +.recharge-display-battery-liquid::after { + content: ""; + position: absolute; + width: 8px; + background: var(--gradient-color-yellow); + box-shadow: inset 0px -3px 6px hsla(0, 0, 0, 0.2); + top: 0; + left: 0; + margin: 0 auto; + right: -4px; + border-radius: 50%; +} + +.recharge-display-remaining-time-container { + display: flex; + flex-direction: column; + align-items: center; + + border-top: 1px solid #ffffff27; + padding-top: 5px; + margin-top: 10px; +} + +.recharge-display-remaining-time-title { + color: #FFF; + font-family: Inter; + font-size: 13px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.recharge-display-remaining-time-value { + color: #FFF; + font-family: Inter; + font-size: 16px; + font-style: normal; + font-weight: 700; + line-height: normal; +} + +/* ELECTRIC CHARGER structure */ +.electric-charger-container { + display: flex; + justify-content: center; + align-items: flex-end; + height: 100%; + width: 100%; + background: #00000070; + font-family: Inter !important; +} + +.electric-charger-elements-container { + position: absolute; + bottom: 440px; + width: 540px; + height: 535px; + margin-right: 65px; + display: grid; + justify-items: center; +} + +.electric-charger-button { + padding: 10px; + width: 285px; + border-radius: 10px; + background: rgba(255, 255, 255, 0.15); + border: 0; + transition: all 0.2s ease, cursor 0.2s ease; + + color: #FFF; + text-align: center; + font-family: Inter; + font-size: 15px; + font-style: normal; + font-weight: 700; + line-height: normal; +} + +.electric-charger-button:hover { + cursor: pointer; + background: rgba(255, 255, 255, 0.24); +} + +.electric-charger-title { + color: #FFF; + text-align: center; + font-size: 40px; + font-style: normal; + font-weight: 900; + line-height: normal; + margin: 50px 0px +} + +.electric-charger-subtitle { + color: #FFF; + text-align: center; + font-size: 20px; + font-style: normal; + font-weight: 500; + line-height: normal; + margin: 5px 0px 40px 0px +} + +.electric-charger-buttons-container { + display: flex; + gap: 15px; + margin-bottom: 16px; +} + +.electric-charger-return-container { + position: absolute; + top: 20px; + left: 20px; +} + +.electric-charger-return-container svg { + height: 25px; + fill: #ffffffa8; +} + +.electric-charger-return-container svg:hover { + fill: #ffffffcc; + cursor: pointer; + transition: all 0.2s ease, cursor 0.2s ease; +} + +/* RECHARGE TYPE */ +.electric-charger-type-container { + display: flex; + flex-direction: column; + align-items: center; +} + +.electric-charger-type-input { + display: none; +} + +.electric-charger-type-input:not(:disabled) ~ .electric-charger-type-label { + cursor: pointer; +} + +.electric-charger-type-container .electric-charger-type-label { + height: 150px; + padding: 14px 13px 10px 13px; +} + +.electric-charger-type-label { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + text-align: center; + height: 125px; + width: 115px; + padding: 9px; + border-radius: 10px; + + color: #FFF; + font-size: 15px; + font-style: normal; + font-weight: 700; + line-height: normal; + + background: rgba(255, 255, 255, 0.15); + border: 1px solid rgba(255, 255, 255, 0); +} + +.electric-charger-type-label:hover { + cursor: pointer; + background: rgba(255, 255, 255, 0.24); + transition: all 0.2s ease, cursor 0.2s ease; +} + +/* Disable hover when input is disabled */ +input.electric-charger-type-input:disabled + .electric-charger-type-label { + pointer-events: none; /* Disable hover and interaction */ + cursor: not-allowed; /* Optional: show not-allowed cursor */ + opacity: 0.5; /* Indicate visually that it's disabled */ +} + +.electric-charger-type-label h2 { + margin: 15 0 5 0; +} + +.electric-charger-type-input:checked + .electric-charger-type-label { + border: 1px solid rgba(255, 255, 255, 0.35); + background: rgba(255, 255, 255, 0.35); +} + +.electric-charger-type-label-item-container { + display: flex; + gap: 5px; + + border-top: 1px solid #ffffff15; + padding-top: 3px; + margin-top: 3px; +} + +.electric-charger-type-label-item-container svg { + height: 15px; + fill: #FFF; +} + +.electric-charger-type-label-item-container span { + font-size: 13px; +} + +/* SELECT AMOUNT */ +.electric-charger-amount-container { + display: flex; + flex-direction: column; + align-items: center; +} + +/* PAYMENT METHOD */ +.electric-charger-payment-container { + display: flex; + flex-direction: column; + align-items: center; +} + +.electric-charger-amount-input-container { + display: flex; + gap: 10px; +} + +.electric-charger-amount-input { + display: flex; + width: 125px; + padding: 10px; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 7px; + flex-shrink: 0; + border-radius: 10px; + border: 1px solid rgba(255, 255, 255, 0.25); + background: rgba(255, 255, 255, 0.15); + + color: rgba(255, 255, 255, 0.6); + text-align: center; + font-size: 15px; + font-style: normal; + font-weight: 700; + line-height: normal; +} + +.electric-charger-amount-input::-webkit-outer-spin-button, +.electric-charger-amount-input::-webkit-inner-spin-button { +-webkit-appearance: none; +margin: 0; +} + +.recharge-sub, .recharge-add { + display: flex; + width: 44px; + height: 44px; + padding: 10px; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 7px; + flex-shrink: 0; + border-radius: 10px; + border: 1px solid rgba(255, 255, 255, 0.35); + background: rgba(255, 255, 255, 0.35); + + color: #FFF; + text-align: center; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.recharge-sub { + font-size: 30px; + padding-bottom: 15px; +} + +.recharge-add { + font-size: 25px; +} + +.electric-amount-info-container { + margin: 20px 0px 15px 0px; +} + +.electric-amount-progress-container { + display: flex; + align-items: center; + gap: 10px; + width: 300px; + margin-bottom: 5px; +} + +.electric-time-to-recharge { + margin-left: 10px; + color: #FFF; + font-size: 15px; + font-style: normal; + font-weight: 600; + line-height: normal; +} + +.electric-time-to-recharge > span { + font-weight: 800; +} + +/* Makes the texts not selectable */ +body { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + background: transparent !important; + user-select: none; +} + +/* Modal */ +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.modal-content { + padding: 20px; + border-radius: 8px; + text-align: center; + width: 300px; + box-shadow: 0 4px 8px 2px rgba(0, 0, 0, 0.2); + position: relative; + + border-radius: 5px; + border: 2px solid rgba(75, 75, 75, 0.50); + background: rgba(0, 0, 0, 0.5); + color: white; + font-family: "Lexend"; + margin-bottom: 100px; +} + +.close-button { + position: absolute; + top: 2px; + right: 13px; + font-size: 24px; + font-weight: bold; + color: #6a6a6a; + cursor: pointer; +} + +.modal-buttons { + display: flex; + justify-content: space-around; + margin-top: 20px; + gap: 15px; +} + +.modal-button { + color: white; + padding: 10px 20px; + border: none; + cursor: pointer; + font-size: 16px; + + border-radius: 5px; + background: rgba(255, 255, 255, 0.12); + font-family: "Lexend"; + font-style: normal; + font-weight: 700; + line-height: normal; + transition: all 0.2s ease, cursor 0.2s ease; +} + +.modal-button:hover { + background-color: #135015; +} + +.modal-button.cancel-button:hover { + background-color: #501313; +} + +/* Chart dialog */ +.chart-dialog { + position: fixed; + width: 450px; + box-shadow: 0 4px 8px 2px rgba(0, 0, 0, 0.3); + z-index: 9999; + border-radius: 8px; + text-align: center; + border: 2px solid rgba(45, 45, 45, 0.60); + background: rgba(0, 0, 0, 0.7); + color: white; + font-family: "Lexend"; + flex-direction: column; + overflow: hidden; +} + +.chart-dialog .dialog-body { + flex: 1; + position: relative; + overflow: hidden; +} + +.chart-dialog .dialog-header, .chart-dialog .dialog-footer { + padding: 10px; + flex-shrink: 0; + font-weight: bold; + font-size: 15px; +} + +.chart-dialog .dialog-header { + cursor: move; +} + +.chart-dialog .dialog-footer { + display: flex; + align-items: flex-end; + justify-content: space-between; + font-size: 13px; + font-weight: 400; +} + +.chart-dialog .dialog-footer-inputs { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 5px; +} + +.chart-dialog .decrease-chart-recording, +.chart-dialog .increase-chart-recording { + color: inherit; + font: inherit; + + background: #0000003d; + border: 1px solid #a7a7a745; + border-radius: 5px; + + width: 24px; + height: 24px; + text-align: center; + padding: 0px; + font-size: 14px; + font-weight: 300; +} + +.chart-dialog .increase-chart-recording { + padding-bottom: 2px; +} + +.chart-dialog .chart-recording-input-container { + display: flex; + align-items: center; + gap: 5px; +} + +.chart-dialog canvas { + margin-bottom: 20px; +} + +/* Resizable properties */ +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +.ui-resizable-se { + width: 16px; + height: 16px; + bottom: 0; + right: 0; + cursor: se-resize; + background: url('data:image/svg+xml;utf8,') no-repeat center center; + background-size: 12px 12px; + opacity: 0.5; +} +.ui-resizable-se:hover::after, .ui-resizable-se:hover { + opacity: 0.8; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + -ms-touch-action: none; + touch-action: none; } \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/nui/lang/de.js b/resources/[carscripts]/lc_fuel/nui/lang/de.js index 32239732d..459e3cf2a 100644 --- a/resources/[carscripts]/lc_fuel/nui/lang/de.js +++ b/resources/[carscripts]/lc_fuel/nui/lang/de.js @@ -1,85 +1,85 @@ -if (Lang == undefined) { - var Lang = []; -} -Lang["de"] = { - pumpInterface: { - stationStock: "{0} L", - vehicleFuel: "{0} L", - confirm: "Bestätigen", - vehicleFuelTooltip: "Aktueller Kraftstoff / Tankkapazität", - fuelTypes: { - regular: "Regulär", - plus: "Plus", - premium: "Premium", - diesel: "Diesel", - }, - }, - pumpRefuelDisplay: { - liters: "L", - carTank: "Autotank", - remaining: "Übrig", - }, - confirmRefuelModal: { - title: "Bestätigen Sie „Tanken“.", - description: "Sie kaufen {0}L {1} Kraftstoff für {2}.", - paymentBank: "Bezahlen Sie mit der Bank", - paymentCash: "Bezahlen Sie mit Bargeld", - }, - confirmBuyJerryCanModal: { - title: "Kauf Bestätigen", - paymentBank: "Bezahlen Sie mit der Bank", - paymentCash: "Bezahlen Sie mit Bargeld", - }, - confirmFuelChangeModal: { - title: "Kraftstoffe können nicht gemischt werden", - description: "⚠️ Kraftstoffe können nicht gemischt werden", - }, - electricInterface: { - chargerType: { - title: "LADEGERÄTETYP", - fast: { - title: "SCHNELL", - power: "220kW", - }, - normal: { - title: "NORMAL", - power: "100kW", - }, - pricePerKWh: "{0}/kWh", - }, - chargerAmount: { - title: "BETRAG WÄHLEN", - typeSelected: "{0} AUSGEWÄHLT", - placeholder: "Menge", - timeToRechargeText: "Zeit zum Aufladen:", - timeToRechargeValue: "{0} min {1} sec", - }, - chargerPayment: { - title: "Bezahlmethode", - money: "Bar", - bank: "Karte", - payButton: "Bezahlen {0}", - }, - continueButton: "Bestätigen", - outOfStock: "Ausverkauft", - }, - rechargerDisplay: { - title: "AUFLADEN...", - remainingTimeText: "VERBLEIBENDE ZEIT", - remainingTimeValue: "{0} min {1} sek", - }, - fuelConsumptionChart: { - title: "Kraftstoffverbrauchsdiagramm", - chartLabels: { - fuel: "Kraftstoff (%)", - speed: "Geschwindigkeit (km/h)", - consumption: "Verbrauch (L/s)", - shortSeconds: "{0}s", - }, - footer: { - focus: "F3 zum Fokussieren umschalten", - toggleRecording: "Aufnahme umschalten", - recordsLength: "Verlaufslänge ({0}s)", - }, - }, +if (Lang == undefined) { + var Lang = []; +} +Lang["de"] = { + pumpInterface: { + stationStock: "{0} L", + vehicleFuel: "{0} L", + confirm: "Bestätigen", + vehicleFuelTooltip: "Aktueller Kraftstoff / Tankkapazität", + fuelTypes: { + regular: "Regulär", + plus: "Plus", + premium: "Premium", + diesel: "Diesel", + }, + }, + pumpRefuelDisplay: { + liters: "L", + carTank: "Autotank", + remaining: "Übrig", + }, + confirmRefuelModal: { + title: "Bestätigen Sie „Tanken“.", + description: "Sie kaufen {0}L {1} Kraftstoff für {2}.", + paymentBank: "Bezahlen Sie mit der Bank", + paymentCash: "Bezahlen Sie mit Bargeld", + }, + confirmBuyJerryCanModal: { + title: "Kauf Bestätigen", + paymentBank: "Bezahlen Sie mit der Bank", + paymentCash: "Bezahlen Sie mit Bargeld", + }, + confirmFuelChangeModal: { + title: "Kraftstoffe können nicht gemischt werden", + description: "⚠️ Kraftstoffe können nicht gemischt werden", + }, + electricInterface: { + chargerType: { + title: "LADEGERÄTETYP", + fast: { + title: "SCHNELL", + power: "220kW", + }, + normal: { + title: "NORMAL", + power: "100kW", + }, + pricePerKWh: "{0}/kWh", + }, + chargerAmount: { + title: "BETRAG WÄHLEN", + typeSelected: "{0} AUSGEWÄHLT", + placeholder: "Menge", + timeToRechargeText: "Zeit zum Aufladen:", + timeToRechargeValue: "{0} min {1} sec", + }, + chargerPayment: { + title: "Bezahlmethode", + money: "Bar", + bank: "Karte", + payButton: "Bezahlen {0}", + }, + continueButton: "Bestätigen", + outOfStock: "Ausverkauft", + }, + rechargerDisplay: { + title: "AUFLADEN...", + remainingTimeText: "VERBLEIBENDE ZEIT", + remainingTimeValue: "{0} min {1} sek", + }, + fuelConsumptionChart: { + title: "Kraftstoffverbrauchsdiagramm", + chartLabels: { + fuel: "Kraftstoff (%)", + speed: "Geschwindigkeit (km/h)", + consumption: "Verbrauch (L/s)", + shortSeconds: "{0}s", + }, + footer: { + focus: "F3 zum Fokussieren umschalten", + toggleRecording: "Aufnahme umschalten", + recordsLength: "Verlaufslänge ({0}s)", + }, + }, }; \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/nui/lang/en.js b/resources/[carscripts]/lc_fuel/nui/lang/en.js index 044bd6818..9e1ff6bb4 100644 --- a/resources/[carscripts]/lc_fuel/nui/lang/en.js +++ b/resources/[carscripts]/lc_fuel/nui/lang/en.js @@ -1,85 +1,85 @@ -if (Lang == undefined) { - var Lang = []; -} -Lang["en"] = { - pumpInterface: { - stationStock: "{0} L", - vehicleFuel: "{0} L", - confirm: "CONFIRM", - vehicleFuelTooltip: "Current Fuel / Tank Capacity", - fuelTypes: { - regular: "Regular", - plus: "Plus", - premium: "Premium", - diesel: "Diesel", - }, - }, - pumpRefuelDisplay: { - liters: "L", - carTank: "Car Tank", - remaining: "Remaining", - }, - confirmRefuelModal: { - title: "Confirm Refuel", - description: "You are purchasing {0}L of {1} fuel for {2}.", - paymentBank: "Pay with bank", - paymentCash: "Pay with cash", - }, - confirmBuyJerryCanModal: { - title: "Confirm Purchase", - paymentBank: "Pay with bank", - paymentCash: "Pay with cash", - }, - confirmFuelChangeModal: { - title: "Fuels cannot be mixed", - description: "⚠️ To change the fuel type in your vehicle, the tank will be emptied.", - }, - electricInterface: { - chargerType: { - title: "CHARGER TYPE", - fast: { - title: "FAST", - power: "220kW", - }, - normal: { - title: "NORMAL", - power: "100kW", - }, - pricePerKWh: "{0}/kWh", - }, - chargerAmount: { - title: "SELECT AMOUNT", - typeSelected: "{0} CHARGER", - placeholder: "Amount", - timeToRechargeText: "Time to recharge:", - timeToRechargeValue: "{0} min {1} sec", - }, - chargerPayment: { - title: "PAYMENT METHOD", - money: "MONEY", - bank: "BANK", - payButton: "PAY {0}", - }, - continueButton: "CONTINUE", - outOfStock: "Out of stock", - }, - rechargerDisplay: { - title: "CHARGING...", - remainingTimeText: "REMAINING TIME", - remainingTimeValue: "{0} min {1} sec", - }, - fuelConsumptionChart: { - title: "Fuel consumption chart", - chartLabels: { - fuel: "Fuel (%)", - speed: "Speed (km/h)", - consumption: "Consumption (L/s)", - shortSeconds: "{0}s", - }, - footer: { - focus: "F3 to toggle focus", - toggleRecording: "Toggle Recording", - recordsLength: "History Length ({0}s)", - }, - }, +if (Lang == undefined) { + var Lang = []; +} +Lang["en"] = { + pumpInterface: { + stationStock: "{0} L", + vehicleFuel: "{0} L", + confirm: "CONFIRM", + vehicleFuelTooltip: "Current Fuel / Tank Capacity", + fuelTypes: { + regular: "Regular", + plus: "Plus", + premium: "Premium", + diesel: "Diesel", + }, + }, + pumpRefuelDisplay: { + liters: "L", + carTank: "Car Tank", + remaining: "Remaining", + }, + confirmRefuelModal: { + title: "Confirm Refuel", + description: "You are purchasing {0}L of {1} fuel for {2}.", + paymentBank: "Pay with bank", + paymentCash: "Pay with cash", + }, + confirmBuyJerryCanModal: { + title: "Confirm Purchase", + paymentBank: "Pay with bank", + paymentCash: "Pay with cash", + }, + confirmFuelChangeModal: { + title: "Fuels cannot be mixed", + description: "⚠️ To change the fuel type in your vehicle, the tank will be emptied.", + }, + electricInterface: { + chargerType: { + title: "CHARGER TYPE", + fast: { + title: "FAST", + power: "220kW", + }, + normal: { + title: "NORMAL", + power: "100kW", + }, + pricePerKWh: "{0}/kWh", + }, + chargerAmount: { + title: "SELECT AMOUNT", + typeSelected: "{0} CHARGER", + placeholder: "Amount", + timeToRechargeText: "Time to recharge:", + timeToRechargeValue: "{0} min {1} sec", + }, + chargerPayment: { + title: "PAYMENT METHOD", + money: "MONEY", + bank: "BANK", + payButton: "PAY {0}", + }, + continueButton: "CONTINUE", + outOfStock: "Out of stock", + }, + rechargerDisplay: { + title: "CHARGING...", + remainingTimeText: "REMAINING TIME", + remainingTimeValue: "{0} min {1} sec", + }, + fuelConsumptionChart: { + title: "Fuel consumption chart", + chartLabels: { + fuel: "Fuel (%)", + speed: "Speed (km/h)", + consumption: "Consumption (L/s)", + shortSeconds: "{0}s", + }, + footer: { + focus: "F3 to toggle focus", + toggleRecording: "Toggle Recording", + recordsLength: "History Length ({0}s)", + }, + }, }; \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/nui/lang/es.js b/resources/[carscripts]/lc_fuel/nui/lang/es.js index 01b903136..2fd9f9bad 100644 --- a/resources/[carscripts]/lc_fuel/nui/lang/es.js +++ b/resources/[carscripts]/lc_fuel/nui/lang/es.js @@ -1,85 +1,85 @@ -if (Lang == undefined) { - var Lang = []; -} -Lang["es"] = { - pumpInterface: { - stationStock: "{0} L", - vehicleFuel: "{0} L", - confirm: "CONFIRMAR", - vehicleFuelTooltip: "Combustible actual / Capacidad del tanque", - fuelTypes: { - regular: "Sin plomo", - plus: "Plus", - premium: "Premium", - diesel: "Diésel", - }, - }, - pumpRefuelDisplay: { - liters: "L", - carTank: "Depósito del coche", - remaining: "Restante", - }, - confirmRefuelModal: { - title: "Confirmar Repostaje", - description: "Estás comprando {0}L de combustible {1} por {2}.", - paymentBank: "Pagar con el banco", - paymentCash: "Pagar en efectivo", - }, - confirmBuyJerryCanModal: { - title: "Confirmar Compra", - paymentBank: "Pagar con el banco", - paymentCash: "Pagar en efectivo", - }, - confirmFuelChangeModal: { - title: "No se pueden mezclar combustibles", - description: "⚠️ Para cambiar el tipo de combustible en tu vehículo, el depósito se vaciará.", - }, - electricInterface: { - chargerType: { - title: "TIPO DE CARGADOR", - fast: { - title: "RÁPIDO", - power: "220kW", - }, - normal: { - title: "NORMAL", - power: "100kW", - }, - pricePerKWh: "{0}/kWh", - }, - chargerAmount: { - title: "SELECCIONAR CANTIDAD", - typeSelected: "CARGADOR {0}", - placeholder: "Cantidad", - timeToRechargeText: "Tiempo de recarga:", - timeToRechargeValue: "{0} min {1} seg", - }, - chargerPayment: { - title: "MÉTODO DE PAGO", - money: "EFECTIVO", - bank: "BANCO", - payButton: "PAGAR {0}", - }, - continueButton: "CONTINUAR", - outOfStock: "Sin stock", - }, - rechargerDisplay: { - title: "CARGANDO...", - remainingTimeText: "TIEMPO RESTANTE", - remainingTimeValue: "{0} min {1} seg", - }, - fuelConsumptionChart: { - title: "Gráfico de consumo de combustible", - chartLabels: { - fuel: "Combustible (%)", - speed: "Velocidad (km/h)", - consumption: "Consumo (L/s)", - shortSeconds: "{0}s", - }, - footer: { - focus: "F3 para alternar enfoque", - toggleRecording: "Alternar grabación", - recordsLength: "Duración del historial ({0}s)", - }, - }, -}; +if (Lang == undefined) { + var Lang = []; +} +Lang["es"] = { + pumpInterface: { + stationStock: "{0} L", + vehicleFuel: "{0} L", + confirm: "CONFIRMAR", + vehicleFuelTooltip: "Combustible actual / Capacidad del tanque", + fuelTypes: { + regular: "Sin plomo", + plus: "Plus", + premium: "Premium", + diesel: "Diésel", + }, + }, + pumpRefuelDisplay: { + liters: "L", + carTank: "Depósito del coche", + remaining: "Restante", + }, + confirmRefuelModal: { + title: "Confirmar Repostaje", + description: "Estás comprando {0}L de combustible {1} por {2}.", + paymentBank: "Pagar con el banco", + paymentCash: "Pagar en efectivo", + }, + confirmBuyJerryCanModal: { + title: "Confirmar Compra", + paymentBank: "Pagar con el banco", + paymentCash: "Pagar en efectivo", + }, + confirmFuelChangeModal: { + title: "No se pueden mezclar combustibles", + description: "⚠️ Para cambiar el tipo de combustible en tu vehículo, el depósito se vaciará.", + }, + electricInterface: { + chargerType: { + title: "TIPO DE CARGADOR", + fast: { + title: "RÁPIDO", + power: "220kW", + }, + normal: { + title: "NORMAL", + power: "100kW", + }, + pricePerKWh: "{0}/kWh", + }, + chargerAmount: { + title: "SELECCIONAR CANTIDAD", + typeSelected: "CARGADOR {0}", + placeholder: "Cantidad", + timeToRechargeText: "Tiempo de recarga:", + timeToRechargeValue: "{0} min {1} seg", + }, + chargerPayment: { + title: "MÉTODO DE PAGO", + money: "EFECTIVO", + bank: "BANCO", + payButton: "PAGAR {0}", + }, + continueButton: "CONTINUAR", + outOfStock: "Sin stock", + }, + rechargerDisplay: { + title: "CARGANDO...", + remainingTimeText: "TIEMPO RESTANTE", + remainingTimeValue: "{0} min {1} seg", + }, + fuelConsumptionChart: { + title: "Gráfico de consumo de combustible", + chartLabels: { + fuel: "Combustible (%)", + speed: "Velocidad (km/h)", + consumption: "Consumo (L/s)", + shortSeconds: "{0}s", + }, + footer: { + focus: "F3 para alternar enfoque", + toggleRecording: "Alternar grabación", + recordsLength: "Duración del historial ({0}s)", + }, + }, +}; diff --git a/resources/[carscripts]/lc_fuel/nui/lang/fr.js b/resources/[carscripts]/lc_fuel/nui/lang/fr.js index 63eb492c8..5c4c10e1c 100644 --- a/resources/[carscripts]/lc_fuel/nui/lang/fr.js +++ b/resources/[carscripts]/lc_fuel/nui/lang/fr.js @@ -1,85 +1,85 @@ -if (Lang == undefined) { - var Lang = []; -} -Lang["fr"] = { - pumpInterface: { - stationStock: "{0} L", - vehicleFuel: "{0} L", - confirm: "CONFIRMER", - vehicleFuelTooltip: "Carburant actuel / Capacité du réservoir", - fuelTypes: { - regular: "Ordinaire", - plus: "Plus", - premium: "Premium", - diesel: "Diesel", - }, - }, - pumpRefuelDisplay: { - liters: "L", - carTank: "Réservoir", - remaining: "Restant", - }, - confirmRefuelModal: { - title: "Confirmer le ravitaillement", - description: "Vous achetez {0} L de carburant {1} pour {2}.", - paymentBank: "Payer avec la banque", - paymentCash: "Payer en espèces", - }, - confirmBuyJerryCanModal: { - title: "Confirmer l'achat", - paymentBank: "Payer avec la banque", - paymentCash: "Payer en espèces", - }, - confirmFuelChangeModal: { - title: "Les carburants ne peuvent pas être mélangés", - description: "⚠️ Pour changer le type de carburant dans votre véhicule, le réservoir sera vidé.", - }, - electricInterface: { - chargerType: { - title: "TYPE DE CHARGEUR", - fast: { - title: "RAPIDE", - power: "220kW", - }, - normal: { - title: "NORMAL", - power: "100kW", - }, - pricePerKWh: "{0}/kWh", - }, - chargerAmount: { - title: "SÉLECTIONNER LA QUANTITÉ", - typeSelected: "{0} CHARGEUR", - placeholder: "Quantité", - timeToRechargeText: "Temps pour recharger :", - timeToRechargeValue: "{0} min {1} sec", - }, - chargerPayment: { - title: "MODE DE PAIEMENT", - money: "ESPÈCES", - bank: "BANQUE", - payButton: "PAYER {0}", - }, - continueButton: "CONTINUER", - outOfStock: "Rupture de stock", - }, - rechargerDisplay: { - title: "CHARGE EN COURS...", - remainingTimeText: "TEMPS RESTANT", - remainingTimeValue: "{0} min {1} sec", - }, - fuelConsumptionChart: { - title: "Graphique de consommation de carburant", - chartLabels: { - fuel: "Carburant (%)", - speed: "Vitesse (km/h)", - consumption: "Consommation (L/s)", - shortSeconds: "{0}s", - }, - footer: { - focus: "F3 pour basculer le focus", - toggleRecording: "Activer/Désactiver l’enregistrement", - recordsLength: "Durée de l’historique ({0}s)", - }, - }, -}; +if (Lang == undefined) { + var Lang = []; +} +Lang["fr"] = { + pumpInterface: { + stationStock: "{0} L", + vehicleFuel: "{0} L", + confirm: "CONFIRMER", + vehicleFuelTooltip: "Carburant actuel / Capacité du réservoir", + fuelTypes: { + regular: "Ordinaire", + plus: "Plus", + premium: "Premium", + diesel: "Diesel", + }, + }, + pumpRefuelDisplay: { + liters: "L", + carTank: "Réservoir", + remaining: "Restant", + }, + confirmRefuelModal: { + title: "Confirmer le ravitaillement", + description: "Vous achetez {0} L de carburant {1} pour {2}.", + paymentBank: "Payer avec la banque", + paymentCash: "Payer en espèces", + }, + confirmBuyJerryCanModal: { + title: "Confirmer l'achat", + paymentBank: "Payer avec la banque", + paymentCash: "Payer en espèces", + }, + confirmFuelChangeModal: { + title: "Les carburants ne peuvent pas être mélangés", + description: "⚠️ Pour changer le type de carburant dans votre véhicule, le réservoir sera vidé.", + }, + electricInterface: { + chargerType: { + title: "TYPE DE CHARGEUR", + fast: { + title: "RAPIDE", + power: "220kW", + }, + normal: { + title: "NORMAL", + power: "100kW", + }, + pricePerKWh: "{0}/kWh", + }, + chargerAmount: { + title: "SÉLECTIONNER LA QUANTITÉ", + typeSelected: "{0} CHARGEUR", + placeholder: "Quantité", + timeToRechargeText: "Temps pour recharger :", + timeToRechargeValue: "{0} min {1} sec", + }, + chargerPayment: { + title: "MODE DE PAIEMENT", + money: "ESPÈCES", + bank: "BANQUE", + payButton: "PAYER {0}", + }, + continueButton: "CONTINUER", + outOfStock: "Rupture de stock", + }, + rechargerDisplay: { + title: "CHARGE EN COURS...", + remainingTimeText: "TEMPS RESTANT", + remainingTimeValue: "{0} min {1} sec", + }, + fuelConsumptionChart: { + title: "Graphique de consommation de carburant", + chartLabels: { + fuel: "Carburant (%)", + speed: "Vitesse (km/h)", + consumption: "Consommation (L/s)", + shortSeconds: "{0}s", + }, + footer: { + focus: "F3 pour basculer le focus", + toggleRecording: "Activer/Désactiver l’enregistrement", + recordsLength: "Durée de l’historique ({0}s)", + }, + }, +}; diff --git a/resources/[carscripts]/lc_fuel/nui/lang/ja.js b/resources/[carscripts]/lc_fuel/nui/lang/ja.js index d940a0bb0..3099e52d2 100644 --- a/resources/[carscripts]/lc_fuel/nui/lang/ja.js +++ b/resources/[carscripts]/lc_fuel/nui/lang/ja.js @@ -1,85 +1,85 @@ -if (Lang == undefined) { - var Lang = []; -} -Lang["ja"] = { - pumpInterface: { - stationStock: "{0} L", - vehicleFuel: "{0} L", - confirm: "確認", - vehicleFuelTooltip: "現在の燃料 / タンク容量", - fuelTypes: { - regular: "レギュラー", - plus: "プラス", - premium: "プレミアム", - diesel: "ディーゼル", - }, - }, - pumpRefuelDisplay: { - liters: "L", - carTank: "自動車タンク", - remaining: "残り", - }, - confirmRefuelModal: { - title: "給油の確認", - description: "{0}Lの{1}を{2}で購入します。", - paymentBank: "銀行で支払う", - paymentCash: "現金で支払う", - }, - confirmBuyJerryCanModal: { - title: "購入を確認する", - paymentBank: "銀行で支払う", - paymentCash: "現金で支払う", - }, - confirmFuelChangeModal: { - title: "燃料の混合はできません", - description: "⚠️ 燃料の種類を変更する場合はタンクを空にしてください。", - }, - electricInterface: { - chargerType: { - title: "チャージャータイプ", - fast: { - title: "高速", - power: "220kW", - }, - normal: { - title: "普通", - power: "100kW", - }, - pricePerKWh: "{0}/kWh", - }, - chargerAmount: { - title: "金額選択", - typeSelected: "{0}チャージャー", - placeholder: "金額", - timeToRechargeText: "充電時間:", - timeToRechargeValue: "{0} 分 {1} 秒", - }, - chargerPayment: { - title: "支払い方法", - money: "現金", - bank: "銀行", - payButton: "{0}で支払う", - }, - continueButton: "決定", - outOfStock: "在庫切れ", - }, - rechargerDisplay: { - title: "充電中・・・", - remainingTimeText: "残り時間", - remainingTimeValue: "{0} 分 {1} 秒", - }, - fuelConsumptionChart: { - title: "燃料消費チャート", - chartLabels: { - fuel: "燃料 (%)", - speed: "速度 (km/h)", - consumption: "消費量 (L/s)", - shortSeconds: "{0}秒", - }, - footer: { - focus: "F3でフォーカス切替", - toggleRecording: "記録の切り替え", - recordsLength: "履歴の長さ ({0}秒)", - }, - }, +if (Lang == undefined) { + var Lang = []; +} +Lang["ja"] = { + pumpInterface: { + stationStock: "{0} L", + vehicleFuel: "{0} L", + confirm: "確認", + vehicleFuelTooltip: "現在の燃料 / タンク容量", + fuelTypes: { + regular: "レギュラー", + plus: "プラス", + premium: "プレミアム", + diesel: "ディーゼル", + }, + }, + pumpRefuelDisplay: { + liters: "L", + carTank: "自動車タンク", + remaining: "残り", + }, + confirmRefuelModal: { + title: "給油の確認", + description: "{0}Lの{1}を{2}で購入します。", + paymentBank: "銀行で支払う", + paymentCash: "現金で支払う", + }, + confirmBuyJerryCanModal: { + title: "購入を確認する", + paymentBank: "銀行で支払う", + paymentCash: "現金で支払う", + }, + confirmFuelChangeModal: { + title: "燃料の混合はできません", + description: "⚠️ 燃料の種類を変更する場合はタンクを空にしてください。", + }, + electricInterface: { + chargerType: { + title: "チャージャータイプ", + fast: { + title: "高速", + power: "220kW", + }, + normal: { + title: "普通", + power: "100kW", + }, + pricePerKWh: "{0}/kWh", + }, + chargerAmount: { + title: "金額選択", + typeSelected: "{0}チャージャー", + placeholder: "金額", + timeToRechargeText: "充電時間:", + timeToRechargeValue: "{0} 分 {1} 秒", + }, + chargerPayment: { + title: "支払い方法", + money: "現金", + bank: "銀行", + payButton: "{0}で支払う", + }, + continueButton: "決定", + outOfStock: "在庫切れ", + }, + rechargerDisplay: { + title: "充電中・・・", + remainingTimeText: "残り時間", + remainingTimeValue: "{0} 分 {1} 秒", + }, + fuelConsumptionChart: { + title: "燃料消費チャート", + chartLabels: { + fuel: "燃料 (%)", + speed: "速度 (km/h)", + consumption: "消費量 (L/s)", + shortSeconds: "{0}秒", + }, + footer: { + focus: "F3でフォーカス切替", + toggleRecording: "記録の切り替え", + recordsLength: "履歴の長さ ({0}秒)", + }, + }, }; \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/nui/lang/tr.js b/resources/[carscripts]/lc_fuel/nui/lang/tr.js index 16dd4a844..c65c3a431 100644 --- a/resources/[carscripts]/lc_fuel/nui/lang/tr.js +++ b/resources/[carscripts]/lc_fuel/nui/lang/tr.js @@ -1,85 +1,85 @@ -if (Lang == undefined) { - var Lang = []; -} -Lang["tr"] = { - pumpInterface: { - stationStock: "{0} L", - vehicleFuel: "{0} L", - confirm: "ONAYLA", - vehicleFuelTooltip: "Mevcut Yakıt / Depo Kapasitesi", - fuelTypes: { - regular: "Normal", - plus: "Plus", - premium: "Premium", - diesel: "Dizel", - }, - }, - pumpRefuelDisplay: { - liters: "L", - carTank: "Araç Deposu", - remaining: "Kalan", - }, - confirmRefuelModal: { - title: "Yakıt Alımını Onayla", - description: "{0}L {1} yakıtı {2} karşılığında satın alıyorsunuz.", - paymentBank: "Banka ile öde", - paymentCash: "Nakit ile öde", - }, - confirmBuyJerryCanModal: { - title: "Satın Alımı Onayla", - paymentBank: "Banka ile öde", - paymentCash: "Nakit ile öde", - }, - confirmFuelChangeModal: { - title: "Yakıtlar Karıştırılamaz", - description: "⚠️ Araçtaki yakıt türünü değiştirmek için depo boşaltılacaktır.", - }, - electricInterface: { - chargerType: { - title: "ŞARJ CİHAZI TÜRÜ", - fast: { - title: "HIZLI", - power: "220kW", - }, - normal: { - title: "NORMAL", - power: "100kW", - }, - pricePerKWh: "{0}/kWh", - }, - chargerAmount: { - title: "MİKTAR SEÇİN", - typeSelected: "{0} ŞARJ CİHAZI", - placeholder: "Miktar", - timeToRechargeText: "Şarj süresi:", - timeToRechargeValue: "{0} dk {1} sn", - }, - chargerPayment: { - title: "ÖDEME YÖNTEMİ", - money: "NAKİT", - bank: "BANKA", - payButton: "{0} ÖDE", - }, - continueButton: "DEVAM", - outOfStock: "Stokta yok", - }, - rechargerDisplay: { - title: "ŞARJ EDİLİYOR...", - remainingTimeText: "KALAN SÜRE", - remainingTimeValue: "{0} dk {1} sn", - }, - fuelConsumptionChart: { - title: "Yakıt tüketim grafiği", - chartLabels: { - fuel: "Yakıt (%)", - speed: "Hız (km/s)", - consumption: "Tüketim (L/s)", - shortSeconds: "{0}s", - }, - footer: { - focus: "Odağı değiştirmek için F3", - toggleRecording: "Kaydı Aç/Kapat", - recordsLength: "Geçmiş Uzunluğu ({0}s)", - }, - }, -}; +if (Lang == undefined) { + var Lang = []; +} +Lang["tr"] = { + pumpInterface: { + stationStock: "{0} L", + vehicleFuel: "{0} L", + confirm: "ONAYLA", + vehicleFuelTooltip: "Mevcut Yakıt / Depo Kapasitesi", + fuelTypes: { + regular: "Normal", + plus: "Plus", + premium: "Premium", + diesel: "Dizel", + }, + }, + pumpRefuelDisplay: { + liters: "L", + carTank: "Araç Deposu", + remaining: "Kalan", + }, + confirmRefuelModal: { + title: "Yakıt Alımını Onayla", + description: "{0}L {1} yakıtı {2} karşılığında satın alıyorsunuz.", + paymentBank: "Banka ile öde", + paymentCash: "Nakit ile öde", + }, + confirmBuyJerryCanModal: { + title: "Satın Alımı Onayla", + paymentBank: "Banka ile öde", + paymentCash: "Nakit ile öde", + }, + confirmFuelChangeModal: { + title: "Yakıtlar Karıştırılamaz", + description: "⚠️ Araçtaki yakıt türünü değiştirmek için depo boşaltılacaktır.", + }, + electricInterface: { + chargerType: { + title: "ŞARJ CİHAZI TÜRÜ", + fast: { + title: "HIZLI", + power: "220kW", + }, + normal: { + title: "NORMAL", + power: "100kW", + }, + pricePerKWh: "{0}/kWh", + }, + chargerAmount: { + title: "MİKTAR SEÇİN", + typeSelected: "{0} ŞARJ CİHAZI", + placeholder: "Miktar", + timeToRechargeText: "Şarj süresi:", + timeToRechargeValue: "{0} dk {1} sn", + }, + chargerPayment: { + title: "ÖDEME YÖNTEMİ", + money: "NAKİT", + bank: "BANKA", + payButton: "{0} ÖDE", + }, + continueButton: "DEVAM", + outOfStock: "Stokta yok", + }, + rechargerDisplay: { + title: "ŞARJ EDİLİYOR...", + remainingTimeText: "KALAN SÜRE", + remainingTimeValue: "{0} dk {1} sn", + }, + fuelConsumptionChart: { + title: "Yakıt tüketim grafiği", + chartLabels: { + fuel: "Yakıt (%)", + speed: "Hız (km/s)", + consumption: "Tüketim (L/s)", + shortSeconds: "{0}s", + }, + footer: { + focus: "Odağı değiştirmek için F3", + toggleRecording: "Kaydı Aç/Kapat", + recordsLength: "Geçmiş Uzunluğu ({0}s)", + }, + }, +}; diff --git a/resources/[carscripts]/lc_fuel/nui/lang/zh-cn.js b/resources/[carscripts]/lc_fuel/nui/lang/zh-cn.js index d925cafc9..997c09416 100644 --- a/resources/[carscripts]/lc_fuel/nui/lang/zh-cn.js +++ b/resources/[carscripts]/lc_fuel/nui/lang/zh-cn.js @@ -1,85 +1,85 @@ -if (Lang == undefined) { - var Lang = []; -} -Lang["zh-cn"] = { - pumpInterface: { - stationStock: "{0} 升(L)", - vehicleFuel: "{0} 升(L)", - confirm: "确认", - vehicleFuelTooltip: "当前燃料 / 油箱容量", - fuelTypes: { - regular: "92号燃油", - plus: "95号燃油", - premium: "98号燃油", - diesel: "柴油", - }, - }, - pumpRefuelDisplay: { - liters: "升(L)", - carTank: "车辆油箱", - remaining: "剩余容量", - }, - confirmRefuelModal: { - title: "加油确认", - description: "您正在为车牌号 {2} 的车辆补充 {0} 升 {1} 类燃油", - paymentBank: "银行结算", - paymentCash: "现金结算", - }, - confirmBuyJerryCanModal: { - title: "确认购买", - paymentBank: "银行结算", - paymentCash: "现金结算", - }, - confirmFuelChangeModal: { - title: "燃油类型变更提示", - description: "⚠️ 更换燃油种类前请确保油箱完全排空", - }, - electricInterface: { - chargerType: { - title: "充电规格选择", - fast: { - title: "极速充电", - power: "220kW", - }, - normal: { - title: "标准充电", - power: "100kW", - }, - pricePerKWh: "{0}/kWh", - }, - chargerAmount: { - title: "充电金额设定", - typeSelected: "充电桩 {0} 台", - placeholder: "输入金额", - timeToRechargeText: "预计充电时长:", - timeToRechargeValue: "{0} 分 {1} 秒", - }, - chargerPayment: { - title: "支付方式选择", - money: "现金结算", - bank: "银行解锁", - payButton: "支付 {0}", - }, - continueButton: "下一步", - outOfStock: "库存不足", - }, - rechargerDisplay: { - title: "充电中...", - remainingTimeText: "剩余充电时间", - remainingTimeValue: "{0} 分 {1} 秒", - }, - fuelConsumptionChart: { - title: "燃料消耗图表", - chartLabels: { - fuel: "燃料 (%)", - speed: "速度 (km/h)", - consumption: "消耗 (L/s)", - shortSeconds: "{0}秒", - }, - footer: { - focus: "按F3切换焦点", - toggleRecording: "切换录制", - recordsLength: "历史长度({0}秒)", - }, - }, +if (Lang == undefined) { + var Lang = []; +} +Lang["zh-cn"] = { + pumpInterface: { + stationStock: "{0} 升(L)", + vehicleFuel: "{0} 升(L)", + confirm: "确认", + vehicleFuelTooltip: "当前燃料 / 油箱容量", + fuelTypes: { + regular: "92号燃油", + plus: "95号燃油", + premium: "98号燃油", + diesel: "柴油", + }, + }, + pumpRefuelDisplay: { + liters: "升(L)", + carTank: "车辆油箱", + remaining: "剩余容量", + }, + confirmRefuelModal: { + title: "加油确认", + description: "您正在为车牌号 {2} 的车辆补充 {0} 升 {1} 类燃油", + paymentBank: "银行结算", + paymentCash: "现金结算", + }, + confirmBuyJerryCanModal: { + title: "确认购买", + paymentBank: "银行结算", + paymentCash: "现金结算", + }, + confirmFuelChangeModal: { + title: "燃油类型变更提示", + description: "⚠️ 更换燃油种类前请确保油箱完全排空", + }, + electricInterface: { + chargerType: { + title: "充电规格选择", + fast: { + title: "极速充电", + power: "220kW", + }, + normal: { + title: "标准充电", + power: "100kW", + }, + pricePerKWh: "{0}/kWh", + }, + chargerAmount: { + title: "充电金额设定", + typeSelected: "充电桩 {0} 台", + placeholder: "输入金额", + timeToRechargeText: "预计充电时长:", + timeToRechargeValue: "{0} 分 {1} 秒", + }, + chargerPayment: { + title: "支付方式选择", + money: "现金结算", + bank: "银行解锁", + payButton: "支付 {0}", + }, + continueButton: "下一步", + outOfStock: "库存不足", + }, + rechargerDisplay: { + title: "充电中...", + remainingTimeText: "剩余充电时间", + remainingTimeValue: "{0} 分 {1} 秒", + }, + fuelConsumptionChart: { + title: "燃料消耗图表", + chartLabels: { + fuel: "燃料 (%)", + speed: "速度 (km/h)", + consumption: "消耗 (L/s)", + shortSeconds: "{0}秒", + }, + footer: { + focus: "按F3切换焦点", + toggleRecording: "切换录制", + recordsLength: "历史长度({0}秒)", + }, + }, }; \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/nui/panel.js b/resources/[carscripts]/lc_fuel/nui/panel.js index 78477f9e9..1620cde56 100644 --- a/resources/[carscripts]/lc_fuel/nui/panel.js +++ b/resources/[carscripts]/lc_fuel/nui/panel.js @@ -1,724 +1,724 @@ -let currentPumpData; -let selectedFuelType; -let fuelTypeWarnSent; - -// Fuel Consumption Chart Dialog -let fuelChart, speedChart, consumptionChart; -let toggleChartFocusShortcut; -let chartTimestampsIndex = 1; -let chartTimestamps = [ 15, 30, 60, 90, 120, 150, 180 ]; -let isRecording; - -window.addEventListener("message", async function(event) { - const item = event.data; - if (item.data) { - currentPumpData = item.data; - } - if (item.resourceName) { - Utils.setResourceName(item.resourceName); - } - if (item.utils) { - await Utils.loadLanguageModules(item.utils); - Utils.post("setNuiVariablesLoaded", null, "setNuiVariablesLoaded"); - } - if (item.type === "copyToClipboard") { - const el = document.createElement("textarea"); - el.value = item.text; - document.body.appendChild(el); - el.select(); - document.execCommand("copy"); - document.body.removeChild(el); - } - if (item.openMainUI) { - if (currentPumpData.isElectric) { - $("#electric-time-to-recharge").html(`${Utils.translate("electricInterface.chargerAmount.timeToRechargeText")} `); - - $("#electric-charger-type-title").text(Utils.translate("electricInterface.chargerType.title")); - $("#electric-charger-type-fast").text(Utils.translate("electricInterface.chargerType.fast.title")); - $("#electric-charger-type-normal").text(Utils.translate("electricInterface.chargerType.normal.title")); - $("#electric-charger-type-label-item-fast-price").text(Utils.translate("electricInterface.chargerType.pricePerKWh").format(Utils.currencyFormat(currentPumpData.pricePerLiter.electricfast))); - $("#electric-charger-type-label-item-normal-price").text(Utils.translate("electricInterface.chargerType.pricePerKWh").format(Utils.currencyFormat(currentPumpData.pricePerLiter.electricnormal))); - $("#electric-charger-type-label-item-fast-power").text(Utils.translate("electricInterface.chargerType.fast.power")); - $("#electric-charger-type-label-item-normal-power").text(Utils.translate("electricInterface.chargerType.normal.power")); - $("#electric-charger-continue-type-button").text(Utils.translate("electricInterface.continueButton")); - - $("#electric-charger-amount-title").text(Utils.translate("electricInterface.chargerAmount.title")); - $("#electric-charger-amount-input").attr("placeholder", Utils.translate("electricInterface.chargerAmount.placeholder")); - $("#electric-charger-continue-amount-button").text(Utils.translate("electricInterface.continueButton")); - - $("#electric-charger-payment-title").text(Utils.translate("electricInterface.chargerPayment.title")); - $("#electric-charger-payment-bank").text(Utils.translate("electricInterface.chargerPayment.bank")); - $("#electric-charger-payment-money").text(Utils.translate("electricInterface.chargerPayment.money")); - - if (currentPumpData.stationStock.electricfast == 0) { - $("#electric-charger-fast-label-wrapper") - .attr("data-tooltip", Utils.translate("electricInterface.outOfStock")) - .attr("data-tooltip-location", "top"); - $("#charger-type-fast").prop("disabled", true); - } else { - $("#electric-charger-fast-label-wrapper") - .removeAttr("data-tooltip") - .removeAttr("data-tooltip-location"); - $("#charger-type-fast").prop("disabled", false); - } - - if (currentPumpData.stationStock.electricnormal == 0) { - $("#electric-charger-normal-label-wrapper") - .attr("data-tooltip", Utils.translate("electricInterface.outOfStock")) - .attr("data-tooltip-location", "top"); - $("#charger-type-normal").prop("disabled", true); - } else { - $("#electric-charger-normal-label-wrapper") - .removeAttr("data-tooltip") - .removeAttr("data-tooltip-location"); - $("#charger-type-normal").prop("disabled", false); - } - - $(".electric-charger-type-container").css("display", ""); - $(".electric-charger-amount-container").css("display", "none"); - $(".electric-charger-payment-container").css("display", "none"); - $("#electric-charger-container").fadeIn(200); - } else { - if (currentPumpData.pumpModel == "prop_gas_pump_1a" || currentPumpData.pumpModel == "prop_gas_pump_1b" || currentPumpData.pumpModel == "prop_gas_pump_1c" || currentPumpData.pumpModel == "prop_gas_pump_1d") { - $("#gas-pump-container-image").attr("src", `images/${currentPumpData.pumpModel}.png`); - } else { - $("#gas-pump-container-image").attr("src", `images/prop_gas_pump_1b.png`); - } - fuelTypeWarnSent = false; - changeSelectedFuelType(currentPumpData.currentFuelType); - $(".vehicle-fuel").text(Utils.translate("pumpInterface.vehicleFuel").format(`${Utils.numberFormat(currentPumpData.vehicleFuel, 1)} / ${Utils.numberFormat(currentPumpData.vehicleTankSize, 0)}`)); - $(".vehicle-fuel").attr("data-tooltip", Utils.translate("pumpInterface.vehicleFuelTooltip")); - $(".bank-balance").text(Utils.currencyFormat(currentPumpData.bankBalance, 2)); - $(".cash-balance").text(Utils.currencyFormat(currentPumpData.cashBalance, 2)); - - $(".fuel-type-button.regular").text(Utils.translate("pumpInterface.fuelTypes.regular")); - $(".fuel-type-button.plus").text(Utils.translate("pumpInterface.fuelTypes.plus")); - $(".fuel-type-button.premium").text(Utils.translate("pumpInterface.fuelTypes.premium")); - $(".fuel-type-button.diesel").text(Utils.translate("pumpInterface.fuelTypes.diesel")); - $(".confirm-button").text(Utils.translate("pumpInterface.confirm")); - - $("#confirm-refuel-payment-modal-title").text(Utils.translate("confirmRefuelModal.title")); - $("#confirm-refuel-payment-modal-pay-bank").text(Utils.translate("confirmRefuelModal.paymentBank")); - $("#confirm-refuel-payment-modal-pay-cash").text(Utils.translate("confirmRefuelModal.paymentCash")); - - $("#confirm-jerry-can-payment-modal-title").text(Utils.translate("confirmBuyJerryCanModal.title")); - $("#confirm-jerry-can-payment-modal-desc").text(Utils.currencyFormat(currentPumpData.jerryCan.price, 2)); - $("#confirm-jerry-can-payment-modal-pay-bank").text(Utils.translate("confirmBuyJerryCanModal.paymentBank")); - $("#confirm-jerry-can-payment-modal-pay-cash").text(Utils.translate("confirmBuyJerryCanModal.paymentCash")); - - $("#confirm-fuel-type-modal-title").text(Utils.translate("confirmFuelChangeModal.title")); - $("#confirm-fuel-type-modal-desc").text(Utils.translate("confirmFuelChangeModal.description")); - $("#confirm-fuel-type-modal-confirm").text(Utils.translate("confirmation_modal_confirm_button")); - $("#confirm-fuel-type-modal-cancel").text(Utils.translate("confirmation_modal_cancel_button")); - - if (!currentPumpData.jerryCan.enabled) { - $(".gas-pump-interactive-button").css("display", "none"); - } - - updateFuelAmountDisplay(true); - - $("#gas-pump-container").fadeIn(200); - } - } - if (item.hideMainUI) { - $("#gas-pump-container").fadeOut(200); - $("#electric-charger-container").fadeOut(200); - } - if (item.showRefuelDisplay) { - if (item.isElectric) { - let percentageOfTankFilled = (item.currentDisplayFuelAmount / item.currentVehicleTankSize) * 100; - $("#recharge-display-title").text(Utils.translate("rechargerDisplay.title")); - $("#recharge-display-battery-level-span").text(`${Utils.numberFormat(percentageOfTankFilled, 0)}%`); - $("#recharge-display-battery-liquid").css("width", `${percentageOfTankFilled}%`); - $("#recharge-display-remaining-time-title").text(Utils.translate("rechargerDisplay.remainingTimeText")); - updateRechargeDisplay((item.currentVehicleTankSize - item.currentDisplayFuelAmount), item.fuelTypePurchased); - $("#recharge-display").fadeIn(200); - } else { - $("#refuel-display-pump-value").text(Utils.numberFormat(item.remainingFuelAmount, 2)); - $("#refuel-display-car-value").text(`${Utils.numberFormat(item.currentDisplayFuelAmount, 2)}/${Utils.numberFormat(item.currentVehicleTankSize, 2)}`); - $(".refuel-display-liters").text(Utils.translate("pumpRefuelDisplay.liters")); - $("#refuel-display-car-label").text(Utils.translate("pumpRefuelDisplay.carTank")); - $("#refuel-display-pump-label").text(Utils.translate("pumpRefuelDisplay.remaining")); - $("#refuel-display").fadeIn(200); - } - } - if (item.hideRefuelDisplay) { - $("#refuel-display").fadeOut(200); - $("#recharge-display").fadeOut(200); - } - if (item.showFuelConsumptionChart) { - toggleChartFocusShortcut = item.focusShortcut; - isRecording = item.isRecording; - - createFuelConsumptionChartObject(); - openFuelConsumptionChart(); - setFuelConsumptionChartPosition(item.position); - updateFuelConsumptionChart({ fuel: null, speed: null, consumption: null }); - } - if (item.updateFuelConsumptionChart) { - updateFuelConsumptionChart(item.fuelConsumptionData); - } - if (item.hideFuelConsumptionChart) { - updateFuelConsumptionChart({ fuel: null, speed: null, consumption: null }); - $("#chart-dialog").fadeOut(); - // fuelChart.destroy(); - // speedChart.destroy(); - // consumptionChart.destroy(); - } -}); - -function updateRechargeDisplay(remainingFuelAmount, chargerType) { - if (chargerType == "electricfast") chargerType = "fast"; - if (chargerType == "electricnormal") chargerType = "normal"; - if (chargerType && (chargerType === "fast" || chargerType === "normal")) { - // Calculate the time to recharge based on remaining fuel amount and charger type's time per unit - let timeToRecharge = remainingFuelAmount * currentPumpData.electric.chargeTypes[chargerType].time; - - // Convert time to minutes and seconds - let timeToRechargeMinutes = Math.floor(timeToRecharge / 60); - let timeToRechargeSeconds = timeToRecharge % 60; - - // Update the display with calculated time - $("#recharge-display-remaining-time-value").text(Utils.translate("rechargerDisplay.remainingTimeValue").format(Utils.numberFormat(timeToRechargeMinutes, 0), Utils.numberFormat(timeToRechargeSeconds, 0))); - } else { - console.log("Invalid charger type or no charger type selected"); - } -} - -/*================= - FUNCTIONS -=================*/ - -function changeSelectedFuelType(fuelType) { - if (fuelType == "regular" || fuelType == "plus" || fuelType == "premium" || fuelType == "diesel") { - $(".fuel-type-button").removeClass("selected"); - $(`.fuel-type-button.${fuelType}`).addClass("selected"); - - $(".price-per-liter").text(Utils.currencyFormat(currentPumpData.pricePerLiter[fuelType], 2)); - $(".station-stock").text(Utils.translate("pumpInterface.stationStock").format(Utils.numberFormat(currentPumpData.stationStock[fuelType]))); - selectedFuelType = fuelType; - } else { - console.log("Invalid fuel type chosen: " + fuelType); - } -} - -// Show the modal when confirmRefuel is called -function confirmRefuel() { - if (fuelTypeWarnSent == false && currentPumpData.currentFuelType != selectedFuelType && currentPumpData.vehicleFuel > 0) { - fuelTypeWarnSent = true; - $("#confirm-fuel-type-modal").fadeIn(); - } else { - let $input = $("#input-fuel-amount"); - let fuelAmount = parseInt($input.val()); - $("#confirm-refuel-payment-modal-desc").text(Utils.translate("confirmRefuelModal.description").format(fuelAmount, Utils.translate("pumpInterface.fuelTypes."+selectedFuelType), Utils.currencyFormat(fuelAmount * currentPumpData.pricePerLiter[selectedFuelType]))); - $("#confirm-refuel-payment-modal").fadeIn(); - } -} - -// Empty vehicle's tank after user confirm fuel type change -function changeVehicleFuelType() { - closeModal(); - Utils.post("changeVehicleFuelType", { selectedFuelType }); - currentPumpData.vehicleFuel = 0; - $(".vehicle-fuel").text(Utils.translate("pumpInterface.vehicleFuel").format(Utils.numberFormat(currentPumpData.vehicleFuel, 2))); -} - -// Confirm the buy jerry can action -function openBuyJerryCanModal() { - closeModal(); - $("#confirm-jerry-can-payment-modal").fadeIn(); -} - -// Hide the modal -function closeModal() { - $(".modal").fadeOut(); -} - -function confirmRefuelPayment(paymentMethod) { - let $input = $("#input-fuel-amount"); - let fuelAmount = parseInt($input.val()); - Utils.post("confirmRefuel", { selectedFuelType, fuelAmount, paymentMethod }); - closeModal(); -} - -function confirmJerryCanPayment(paymentMethod) { - Utils.post("confirmJerryCanPurchase", { paymentMethod }); - closeModal(); -} - -function increaseZoom() { - // Get the current zoom level - let currentZoom = parseFloat($("#gas-pump-container").css("zoom")) || 1; - - // Increase zoom by 5% - let newZoom = currentZoom + 0.05; - - // Limit the zoom to a maximum of 1.4 (140%) - if (newZoom > 1.4) { - newZoom = 1.4; - } - - // Apply the new zoom level - $("#gas-pump-container").css("zoom", newZoom); -} - -function decreaseZoom() { - // Get the current zoom level - let currentZoom = parseFloat($("#gas-pump-container").css("zoom")) || 1; - - // Decrease zoom by 5% - let newZoom = currentZoom - 0.05; - - // Limit the zoom to a minimum of 0.8 (80%) - if (newZoom < 0.8) { - newZoom = 0.8; - } - - // Apply the new zoom level - $("#gas-pump-container").css("zoom", newZoom); -} - -// Function to update the display with the 'L' suffix -function updateFuelAmountDisplay(setToMax = false) { - let $input = $("#input-fuel-amount"); - let value = parseInt($input.val()); - - // Set value to 1 if it's not a positive number - if (isNaN(value) || value <= 0) { - value = 1; - } - - // Don't let it purchase more L than the vehicle can hold in the tank - if (setToMax || (!isNaN(value) && value > currentPumpData.vehicleTankSize - currentPumpData.vehicleFuel)) { - value = Math.floor(currentPumpData.vehicleTankSize - currentPumpData.vehicleFuel); - } - - $input.val(value + " L"); -} - -// Pagination for electric chargers -function chargerTypeContinue() { - let chargerType = getSelectedChargerType(); - if (chargerType && (chargerType == "fast" || chargerType == "normal")) { - $("#electric-charger-amount-input").val(Math.floor(currentPumpData.vehicleTankSize - currentPumpData.vehicleFuel)); - calculateTimeToRecharge(); - $("#electric-charger-amount-type-selected").text(Utils.translate("electricInterface.chargerAmount.typeSelected").format(Utils.translate(`electricInterface.chargerType.${chargerType}.title`))); - $(".electric-charger-type-container").css("display", "none"); - $(".electric-charger-amount-container").css("display", ""); - } -} - -function chargerAmountContinue() { - let $input = $("#electric-charger-amount-input"); - let currentValue = parseInt($input.val()) || 0; - let newWidthPercentage = ((currentPumpData.vehicleFuel / currentPumpData.vehicleTankSize) * 100) + ((currentValue / currentPumpData.vehicleTankSize) * 100); - - if (currentValue <= 0 || newWidthPercentage > 100) { - return; - } - - let chargerType = getSelectedChargerType(); - $("#electric-charger-pay-button").text(Utils.translate("electricInterface.chargerPayment.payButton").format(Utils.currencyFormat(currentValue * currentPumpData.pricePerLiter["electric"+chargerType], 2))); - $(".electric-charger-amount-container").css("display", "none"); - $(".electric-charger-payment-container").css("display", ""); -} - -function chargerAmountReturn() { - $(".electric-charger-type-container").css("display", ""); - $(".electric-charger-amount-container").css("display", "none"); - $(".electric-charger-payment-container").css("display", "none"); -} - -function confirmRecharge() { - let $input = $("#electric-charger-amount-input"); - let fuelAmount = parseInt($input.val()) || 0; - Utils.post("confirmRefuel", { selectedFuelType: "electric" + getSelectedChargerType(), fuelAmount, paymentMethod: getSelectedElectricPaymentMethod() }); -} - -function chargerPaymentReturn() { - $(".electric-charger-type-container").css("display", "none"); - $(".electric-charger-amount-container").css("display", ""); - $(".electric-charger-payment-container").css("display", "none"); -} - -function getSelectedChargerType() { - const selectedInput = $("input[name='charger-type']:checked"); - return selectedInput.length && !selectedInput.prop("disabled") ? selectedInput.val() : null; -} - -function getSelectedElectricPaymentMethod() { - const selectedInput = $("input[name='charger-payment']:checked"); - return selectedInput.length ? selectedInput.val() : null; -} - -function calculateTimeToRecharge() { - let $input = $("#electric-charger-amount-input"); - let currentValue = parseInt($input.val()); - - // Allow empty input temporarily; validate only non-empty values - if ($input.val().trim() === "" || isNaN(currentValue) || currentValue <= 0) { - currentValue = 0; - } - - if (currentValue > 1000) { - currentValue = 1000; - $input.val(currentValue); - } - - let chargerType = getSelectedChargerType(); - if (chargerType && (chargerType == "fast" || chargerType == "normal")) { - let timeToRecharge = currentValue * currentPumpData.electric.chargeTypes[chargerType].time; - - // Calculate minutes and seconds - let timeToRechargeMinutes = Math.floor(timeToRecharge / 60); - let timeToRechargeSeconds = timeToRecharge % 60; - - $("#electric-time-to-recharge-value").text(Utils.translate("electricInterface.chargerAmount.timeToRechargeValue").format(Utils.numberFormat(timeToRechargeMinutes, 0), Utils.numberFormat(timeToRechargeSeconds, 0))); - - let newWidthPercentage = ((currentPumpData.vehicleFuel / currentPumpData.vehicleTankSize) * 100) + ((currentValue / currentPumpData.vehicleTankSize) * 100); - $("#electric-amount-progress-bar").css("width", newWidthPercentage + "%"); - - if (newWidthPercentage > 100) { - $("#electric-amount-progress-bar").css("background", "red"); - } else { - $("#electric-amount-progress-bar").css("background", ""); - } - } else { - console.log("No charger type selected"); - } -} - -/*=================== - CHART DIALOG -===================*/ - -function openFuelConsumptionChart() { - const $dialog = $("#chart-dialog"); - - $("#chart-dialog-title").text(Utils.translate("fuelConsumptionChart.title")); - $("#chart-dialog-footer-text").text(Utils.translate("fuelConsumptionChart.footer.focus")); - $("#stepper-chart-recording-input").text(Utils.translate("fuelConsumptionChart.footer.recordsLength").format(chartTimestamps[chartTimestampsIndex])); - $("#start-stop-recording-label").text(Utils.translate("fuelConsumptionChart.footer.toggleRecording")); - $("#start-stop-recording").prop("checked", isRecording); - - $dialog.css({ - right: "", - bottom: "", - display: "flex", - }).fadeIn(); - - $dialog.draggable({ - handle: ".dialog-header", - }).resizable({ - minHeight: 450, - minWidth: 300, - maxHeight: $(window).height()*0.8, - maxWidth: $(window).width()*0.8, - }); - speedChart.resize(); - consumptionChart.resize(); - fuelChart.resize(); -} - -function setFuelConsumptionChartPosition(position) { - const $dialog = $("#chart-dialog"); - const windowWidth = $(window).width(); - const dialogWidth = $dialog.outerWidth(); - - let top = 10; - let left; - - // Horizontal position - if (position === "left") { - left = 10; - } else { - left = windowWidth - dialogWidth - 10; - } - - $dialog.css({ - top: `${top}px`, - left: `${left}px`, - }); -} - - -function updateFuelConsumptionChart(latestData) { - const now = Date.now(); - - if (fuelChart && speedChart && consumptionChart) { - fuelChart.data.datasets[0].data.push({ x: now, y: latestData.fuel }); - speedChart.data.datasets[0].data.push({ x: now, y: latestData.speed }); - consumptionChart.data.datasets[0].data.push({ x: now, y: latestData.consumption }); - } - fuelChart.update("quiet"); - speedChart.update("quiet"); - consumptionChart.update("quiet"); -} - -function createFuelConsumptionChartObject() { - if (fuelChart || speedChart || consumptionChart) { return; } - - Chart.defaults.color = "rgba(218, 218, 218, 0.73)"; - Chart.overrides.line.spanGaps = false; - - const baseOptions = { - responsive: true, - maintainAspectRatio: false, - elements: { point: { radius: 0 } }, - animation: false, - interaction: { mode: "index", intersect: false }, - plugins: { - legend: { - position: "bottom", - labels: { boxWidth: 3, boxHeight: 3 }, - }, - streaming: { - frameRate: 10, - }, - }, - scales: { - x: { - type: "realtime", - realtime: { - duration: 30000, - refresh: 1000, - delay: 1000, - }, - }, - y: { - beginAtZero: true, - }, - }, - }; - - // Fuel Chart - - fuelChart = new Chart(document.getElementById("fuel-chart"), { - type: "line", - data: { - datasets: [{ - label: Utils.translate("fuelConsumptionChart.chartLabels.fuel"), - data: [], - borderColor: "#FFD800", - cubicInterpolationMode: "monotone", - }], - }, - options: { - ...baseOptions, - scales: { - ...baseOptions.scales, - y: { - ...baseOptions.scales.y, - suggestedMax: 100, - title: { display: true, text: Utils.translate("fuelConsumptionChart.chartLabels.fuel") }, - }, - }, - }, - }); - - // Speed Chart - - speedChart = new Chart(document.getElementById("speed-chart"), { - type: "line", - data: { - datasets: [{ - label: Utils.translate("fuelConsumptionChart.chartLabels.speed"), - data: [], - borderColor: "#0026FF", - cubicInterpolationMode: "monotone", - }], - }, - options: { - ...baseOptions, - scales: { - ...baseOptions.scales, - y: { - ...baseOptions.scales.y, - suggestedMax: 140, - title: { display: true, text: Utils.translate("fuelConsumptionChart.chartLabels.speed") }, - }, - }, - }, - }); - - // Consumption Chart - - consumptionChart = new Chart(document.getElementById("consumption-chart"), { - type: "line", - data: { - datasets: [{ - label: Utils.translate("fuelConsumptionChart.chartLabels.consumption"), - data: [], - borderColor: "#7F0000", - cubicInterpolationMode: "monotone", - }], - }, - options: { - ...baseOptions, - scales: { - ...baseOptions.scales, - y: { - ...baseOptions.scales.y, - suggestedMax: 0.3, - title: { display: true, text: Utils.translate("fuelConsumptionChart.chartLabels.consumption") }, - }, - }, - }, - }); -} - - -/*================= - LISTENERS -=================*/ - -$(window).click(function(event) { - // Close the modal when clicking outside of it - if ($(event.target).is(".modal")) { - closeModal(); - } -}); - -$(document).on("keydown", function(event) { - // Handle press of Esc key - if (event.key === "Escape" || event.keyCode === 27) { - // Check if the modal is open by checking if it's visible - if ($("#chart-dialog").is(":visible")) { - closeFuelConsumptionChartUI(); - } else if ($(".modal").is(":visible")) { - closeModal(); - } else { - closeUI(); - } - } - if (event.key === toggleChartFocusShortcut - || event.key === "w" || event.key === "W" - || event.key === "a" || event.key === "A" - || event.key === "d" || event.key === "D" - || event.key === "s" || event.key === "S") { - // Check if the modal is open by checking if it's visible - if ($("#chart-dialog").is(":visible")) { - removeFocusFuelConsumptionChartUI(); - } - } -}); - -$(document).ready(function() { - // Handle the add button - $(".refuel-add").click(function() { - let $input = $("#input-fuel-amount"); - let currentValue = parseInt($input.val()) || 0; - if (currentValue < Math.floor(currentPumpData.vehicleTankSize - currentPumpData.vehicleFuel)) { - $input.val((currentValue + 1) + " L"); - } - }); - $(".recharge-add").click(function() { - let $input = $("#electric-charger-amount-input"); - let currentValue = parseInt($input.val()) || 0; - if (currentValue < Math.floor(currentPumpData.vehicleTankSize - currentPumpData.vehicleFuel)) { - $input.val((currentValue + 1)); - calculateTimeToRecharge(); - } - }); - - // Handle the sub button - $(".refuel-sub").click(function() { - let $input = $("#input-fuel-amount"); - let currentValue = parseInt($input.val()) || 0; - if (currentValue > 1) { - $input.val((currentValue - 1) + " L"); - } - }); - $(".recharge-sub").click(function() { - let $input = $("#electric-charger-amount-input"); - let currentValue = parseInt($input.val()) || 0; - if (currentValue > 1) { - $input.val((currentValue - 1)); - calculateTimeToRecharge(); - } - }); - - // Remove 'L' suffix on focus to allow numeric input, and add it back on blur - $("#input-fuel-amount").on("focus", function() { - $(this).val(parseInt($(this).val()) || 1); - }).on("blur", function() { - updateFuelAmountDisplay(); - }); - - // Recalculate time when change input - $("#electric-charger-amount-input").on("input", function() { - calculateTimeToRecharge(); - }); - $("#electric-charger-amount-input").on("blur", function() { - let $input = $(this); - let currentValue = parseInt($input.val()); - - // If invalid, reset to 0 - if (isNaN(currentValue) || currentValue <= 0) { - $input.val(0); - } - }); - - // Handle chart buttons - $("#start-stop-recording").change(function() { - if ($(this).is(":checked")) { - startRecordingGraph(); - } else { - stopRecordingGraph(); - } - }); - - $("#increase-chart-recording").click(function() { - if (chartTimestampsIndex < chartTimestamps.length - 1) { - chartTimestampsIndex++; - $("#stepper-chart-recording-input").text(Utils.translate("fuelConsumptionChart.footer.recordsLength").format(chartTimestamps[chartTimestampsIndex])); - changeRecordingIndexGraph(); - } - }); - - $("#decrease-chart-recording").click(function() { - if (chartTimestampsIndex > 0) { - chartTimestampsIndex--; - $("#stepper-chart-recording-input").text(Utils.translate("fuelConsumptionChart.footer.recordsLength").format(chartTimestamps[chartTimestampsIndex])); - changeRecordingIndexGraph(); - } - }); -}); - - -/*================= - CALLBACKS -=================*/ - -function closeUI(){ - Utils.post("close",""); -} - -function stopRecordingGraph(){ - fuelChart.options.scales.x.realtime.pause = true; - speedChart.options.scales.x.realtime.pause = true; - consumptionChart.options.scales.x.realtime.pause = true; - Utils.post("stopRecordingGraph",""); -} - -function startRecordingGraph(){ - fuelChart.options.scales.x.realtime.pause = false; - speedChart.options.scales.x.realtime.pause = false; - consumptionChart.options.scales.x.realtime.pause = false; - Utils.post("startRecordingGraph",""); -} - -function changeRecordingIndexGraph(){ - fuelChart.options.scales.x.realtime.duration = chartTimestamps[chartTimestampsIndex] * 1000; - speedChart.options.scales.x.realtime.duration = chartTimestamps[chartTimestampsIndex] * 1000; - consumptionChart.options.scales.x.realtime.duration = chartTimestamps[chartTimestampsIndex] * 1000; - Utils.post("changeRecordingIndexGraph",chartTimestampsIndex); -} - -function closeFuelConsumptionChartUI(){ - Utils.post("closeFuelConsumptionChartUI",""); -} - -function removeFocusFuelConsumptionChartUI(){ - Utils.post("removeFocusFuelConsumptionChartUI",""); +let currentPumpData; +let selectedFuelType; +let fuelTypeWarnSent; + +// Fuel Consumption Chart Dialog +let fuelChart, speedChart, consumptionChart; +let toggleChartFocusShortcut; +let chartTimestampsIndex = 1; +let chartTimestamps = [ 15, 30, 60, 90, 120, 150, 180 ]; +let isRecording; + +window.addEventListener("message", async function(event) { + const item = event.data; + if (item.data) { + currentPumpData = item.data; + } + if (item.resourceName) { + Utils.setResourceName(item.resourceName); + } + if (item.utils) { + await Utils.loadLanguageModules(item.utils); + Utils.post("setNuiVariablesLoaded", null, "setNuiVariablesLoaded"); + } + if (item.type === "copyToClipboard") { + const el = document.createElement("textarea"); + el.value = item.text; + document.body.appendChild(el); + el.select(); + document.execCommand("copy"); + document.body.removeChild(el); + } + if (item.openMainUI) { + if (currentPumpData.isElectric) { + $("#electric-time-to-recharge").html(`${Utils.translate("electricInterface.chargerAmount.timeToRechargeText")} `); + + $("#electric-charger-type-title").text(Utils.translate("electricInterface.chargerType.title")); + $("#electric-charger-type-fast").text(Utils.translate("electricInterface.chargerType.fast.title")); + $("#electric-charger-type-normal").text(Utils.translate("electricInterface.chargerType.normal.title")); + $("#electric-charger-type-label-item-fast-price").text(Utils.translate("electricInterface.chargerType.pricePerKWh").format(Utils.currencyFormat(currentPumpData.pricePerLiter.electricfast))); + $("#electric-charger-type-label-item-normal-price").text(Utils.translate("electricInterface.chargerType.pricePerKWh").format(Utils.currencyFormat(currentPumpData.pricePerLiter.electricnormal))); + $("#electric-charger-type-label-item-fast-power").text(Utils.translate("electricInterface.chargerType.fast.power")); + $("#electric-charger-type-label-item-normal-power").text(Utils.translate("electricInterface.chargerType.normal.power")); + $("#electric-charger-continue-type-button").text(Utils.translate("electricInterface.continueButton")); + + $("#electric-charger-amount-title").text(Utils.translate("electricInterface.chargerAmount.title")); + $("#electric-charger-amount-input").attr("placeholder", Utils.translate("electricInterface.chargerAmount.placeholder")); + $("#electric-charger-continue-amount-button").text(Utils.translate("electricInterface.continueButton")); + + $("#electric-charger-payment-title").text(Utils.translate("electricInterface.chargerPayment.title")); + $("#electric-charger-payment-bank").text(Utils.translate("electricInterface.chargerPayment.bank")); + $("#electric-charger-payment-money").text(Utils.translate("electricInterface.chargerPayment.money")); + + if (currentPumpData.stationStock.electricfast == 0) { + $("#electric-charger-fast-label-wrapper") + .attr("data-tooltip", Utils.translate("electricInterface.outOfStock")) + .attr("data-tooltip-location", "top"); + $("#charger-type-fast").prop("disabled", true); + } else { + $("#electric-charger-fast-label-wrapper") + .removeAttr("data-tooltip") + .removeAttr("data-tooltip-location"); + $("#charger-type-fast").prop("disabled", false); + } + + if (currentPumpData.stationStock.electricnormal == 0) { + $("#electric-charger-normal-label-wrapper") + .attr("data-tooltip", Utils.translate("electricInterface.outOfStock")) + .attr("data-tooltip-location", "top"); + $("#charger-type-normal").prop("disabled", true); + } else { + $("#electric-charger-normal-label-wrapper") + .removeAttr("data-tooltip") + .removeAttr("data-tooltip-location"); + $("#charger-type-normal").prop("disabled", false); + } + + $(".electric-charger-type-container").css("display", ""); + $(".electric-charger-amount-container").css("display", "none"); + $(".electric-charger-payment-container").css("display", "none"); + $("#electric-charger-container").fadeIn(200); + } else { + if (currentPumpData.pumpModel == "prop_gas_pump_1a" || currentPumpData.pumpModel == "prop_gas_pump_1b" || currentPumpData.pumpModel == "prop_gas_pump_1c" || currentPumpData.pumpModel == "prop_gas_pump_1d") { + $("#gas-pump-container-image").attr("src", `images/${currentPumpData.pumpModel}.png`); + } else { + $("#gas-pump-container-image").attr("src", `images/prop_gas_pump_1b.png`); + } + fuelTypeWarnSent = false; + changeSelectedFuelType(currentPumpData.currentFuelType); + $(".vehicle-fuel").text(Utils.translate("pumpInterface.vehicleFuel").format(`${Utils.numberFormat(currentPumpData.vehicleFuel, 1)} / ${Utils.numberFormat(currentPumpData.vehicleTankSize, 0)}`)); + $(".vehicle-fuel").attr("data-tooltip", Utils.translate("pumpInterface.vehicleFuelTooltip")); + $(".bank-balance").text(Utils.currencyFormat(currentPumpData.bankBalance, 2)); + $(".cash-balance").text(Utils.currencyFormat(currentPumpData.cashBalance, 2)); + + $(".fuel-type-button.regular").text(Utils.translate("pumpInterface.fuelTypes.regular")); + $(".fuel-type-button.plus").text(Utils.translate("pumpInterface.fuelTypes.plus")); + $(".fuel-type-button.premium").text(Utils.translate("pumpInterface.fuelTypes.premium")); + $(".fuel-type-button.diesel").text(Utils.translate("pumpInterface.fuelTypes.diesel")); + $(".confirm-button").text(Utils.translate("pumpInterface.confirm")); + + $("#confirm-refuel-payment-modal-title").text(Utils.translate("confirmRefuelModal.title")); + $("#confirm-refuel-payment-modal-pay-bank").text(Utils.translate("confirmRefuelModal.paymentBank")); + $("#confirm-refuel-payment-modal-pay-cash").text(Utils.translate("confirmRefuelModal.paymentCash")); + + $("#confirm-jerry-can-payment-modal-title").text(Utils.translate("confirmBuyJerryCanModal.title")); + $("#confirm-jerry-can-payment-modal-desc").text(Utils.currencyFormat(currentPumpData.jerryCan.price, 2)); + $("#confirm-jerry-can-payment-modal-pay-bank").text(Utils.translate("confirmBuyJerryCanModal.paymentBank")); + $("#confirm-jerry-can-payment-modal-pay-cash").text(Utils.translate("confirmBuyJerryCanModal.paymentCash")); + + $("#confirm-fuel-type-modal-title").text(Utils.translate("confirmFuelChangeModal.title")); + $("#confirm-fuel-type-modal-desc").text(Utils.translate("confirmFuelChangeModal.description")); + $("#confirm-fuel-type-modal-confirm").text(Utils.translate("confirmation_modal_confirm_button")); + $("#confirm-fuel-type-modal-cancel").text(Utils.translate("confirmation_modal_cancel_button")); + + if (!currentPumpData.jerryCan.enabled) { + $(".gas-pump-interactive-button").css("display", "none"); + } + + updateFuelAmountDisplay(true); + + $("#gas-pump-container").fadeIn(200); + } + } + if (item.hideMainUI) { + $("#gas-pump-container").fadeOut(200); + $("#electric-charger-container").fadeOut(200); + } + if (item.showRefuelDisplay) { + if (item.isElectric) { + let percentageOfTankFilled = (item.currentDisplayFuelAmount / item.currentVehicleTankSize) * 100; + $("#recharge-display-title").text(Utils.translate("rechargerDisplay.title")); + $("#recharge-display-battery-level-span").text(`${Utils.numberFormat(percentageOfTankFilled, 0)}%`); + $("#recharge-display-battery-liquid").css("width", `${percentageOfTankFilled}%`); + $("#recharge-display-remaining-time-title").text(Utils.translate("rechargerDisplay.remainingTimeText")); + updateRechargeDisplay((item.currentVehicleTankSize - item.currentDisplayFuelAmount), item.fuelTypePurchased); + $("#recharge-display").fadeIn(200); + } else { + $("#refuel-display-pump-value").text(Utils.numberFormat(item.remainingFuelAmount, 2)); + $("#refuel-display-car-value").text(`${Utils.numberFormat(item.currentDisplayFuelAmount, 2)}/${Utils.numberFormat(item.currentVehicleTankSize, 2)}`); + $(".refuel-display-liters").text(Utils.translate("pumpRefuelDisplay.liters")); + $("#refuel-display-car-label").text(Utils.translate("pumpRefuelDisplay.carTank")); + $("#refuel-display-pump-label").text(Utils.translate("pumpRefuelDisplay.remaining")); + $("#refuel-display").fadeIn(200); + } + } + if (item.hideRefuelDisplay) { + $("#refuel-display").fadeOut(200); + $("#recharge-display").fadeOut(200); + } + if (item.showFuelConsumptionChart) { + toggleChartFocusShortcut = item.focusShortcut; + isRecording = item.isRecording; + + createFuelConsumptionChartObject(); + openFuelConsumptionChart(); + setFuelConsumptionChartPosition(item.position); + updateFuelConsumptionChart({ fuel: null, speed: null, consumption: null }); + } + if (item.updateFuelConsumptionChart) { + updateFuelConsumptionChart(item.fuelConsumptionData); + } + if (item.hideFuelConsumptionChart) { + updateFuelConsumptionChart({ fuel: null, speed: null, consumption: null }); + $("#chart-dialog").fadeOut(); + // fuelChart.destroy(); + // speedChart.destroy(); + // consumptionChart.destroy(); + } +}); + +function updateRechargeDisplay(remainingFuelAmount, chargerType) { + if (chargerType == "electricfast") chargerType = "fast"; + if (chargerType == "electricnormal") chargerType = "normal"; + if (chargerType && (chargerType === "fast" || chargerType === "normal")) { + // Calculate the time to recharge based on remaining fuel amount and charger type's time per unit + let timeToRecharge = remainingFuelAmount * currentPumpData.electric.chargeTypes[chargerType].time; + + // Convert time to minutes and seconds + let timeToRechargeMinutes = Math.floor(timeToRecharge / 60); + let timeToRechargeSeconds = timeToRecharge % 60; + + // Update the display with calculated time + $("#recharge-display-remaining-time-value").text(Utils.translate("rechargerDisplay.remainingTimeValue").format(Utils.numberFormat(timeToRechargeMinutes, 0), Utils.numberFormat(timeToRechargeSeconds, 0))); + } else { + console.log("Invalid charger type or no charger type selected"); + } +} + +/*================= + FUNCTIONS +=================*/ + +function changeSelectedFuelType(fuelType) { + if (fuelType == "regular" || fuelType == "plus" || fuelType == "premium" || fuelType == "diesel") { + $(".fuel-type-button").removeClass("selected"); + $(`.fuel-type-button.${fuelType}`).addClass("selected"); + + $(".price-per-liter").text(Utils.currencyFormat(currentPumpData.pricePerLiter[fuelType], 2)); + $(".station-stock").text(Utils.translate("pumpInterface.stationStock").format(Utils.numberFormat(currentPumpData.stationStock[fuelType]))); + selectedFuelType = fuelType; + } else { + console.log("Invalid fuel type chosen: " + fuelType); + } +} + +// Show the modal when confirmRefuel is called +function confirmRefuel() { + if (fuelTypeWarnSent == false && currentPumpData.currentFuelType != selectedFuelType && currentPumpData.vehicleFuel > 0) { + fuelTypeWarnSent = true; + $("#confirm-fuel-type-modal").fadeIn(); + } else { + let $input = $("#input-fuel-amount"); + let fuelAmount = parseInt($input.val()); + $("#confirm-refuel-payment-modal-desc").text(Utils.translate("confirmRefuelModal.description").format(fuelAmount, Utils.translate("pumpInterface.fuelTypes."+selectedFuelType), Utils.currencyFormat(fuelAmount * currentPumpData.pricePerLiter[selectedFuelType]))); + $("#confirm-refuel-payment-modal").fadeIn(); + } +} + +// Empty vehicle's tank after user confirm fuel type change +function changeVehicleFuelType() { + closeModal(); + Utils.post("changeVehicleFuelType", { selectedFuelType }); + currentPumpData.vehicleFuel = 0; + $(".vehicle-fuel").text(Utils.translate("pumpInterface.vehicleFuel").format(Utils.numberFormat(currentPumpData.vehicleFuel, 2))); +} + +// Confirm the buy jerry can action +function openBuyJerryCanModal() { + closeModal(); + $("#confirm-jerry-can-payment-modal").fadeIn(); +} + +// Hide the modal +function closeModal() { + $(".modal").fadeOut(); +} + +function confirmRefuelPayment(paymentMethod) { + let $input = $("#input-fuel-amount"); + let fuelAmount = parseInt($input.val()); + Utils.post("confirmRefuel", { selectedFuelType, fuelAmount, paymentMethod }); + closeModal(); +} + +function confirmJerryCanPayment(paymentMethod) { + Utils.post("confirmJerryCanPurchase", { paymentMethod }); + closeModal(); +} + +function increaseZoom() { + // Get the current zoom level + let currentZoom = parseFloat($("#gas-pump-container").css("zoom")) || 1; + + // Increase zoom by 5% + let newZoom = currentZoom + 0.05; + + // Limit the zoom to a maximum of 1.4 (140%) + if (newZoom > 1.4) { + newZoom = 1.4; + } + + // Apply the new zoom level + $("#gas-pump-container").css("zoom", newZoom); +} + +function decreaseZoom() { + // Get the current zoom level + let currentZoom = parseFloat($("#gas-pump-container").css("zoom")) || 1; + + // Decrease zoom by 5% + let newZoom = currentZoom - 0.05; + + // Limit the zoom to a minimum of 0.8 (80%) + if (newZoom < 0.8) { + newZoom = 0.8; + } + + // Apply the new zoom level + $("#gas-pump-container").css("zoom", newZoom); +} + +// Function to update the display with the 'L' suffix +function updateFuelAmountDisplay(setToMax = false) { + let $input = $("#input-fuel-amount"); + let value = parseInt($input.val()); + + // Set value to 1 if it's not a positive number + if (isNaN(value) || value <= 0) { + value = 1; + } + + // Don't let it purchase more L than the vehicle can hold in the tank + if (setToMax || (!isNaN(value) && value > currentPumpData.vehicleTankSize - currentPumpData.vehicleFuel)) { + value = Math.floor(currentPumpData.vehicleTankSize - currentPumpData.vehicleFuel); + } + + $input.val(value + " L"); +} + +// Pagination for electric chargers +function chargerTypeContinue() { + let chargerType = getSelectedChargerType(); + if (chargerType && (chargerType == "fast" || chargerType == "normal")) { + $("#electric-charger-amount-input").val(Math.floor(currentPumpData.vehicleTankSize - currentPumpData.vehicleFuel)); + calculateTimeToRecharge(); + $("#electric-charger-amount-type-selected").text(Utils.translate("electricInterface.chargerAmount.typeSelected").format(Utils.translate(`electricInterface.chargerType.${chargerType}.title`))); + $(".electric-charger-type-container").css("display", "none"); + $(".electric-charger-amount-container").css("display", ""); + } +} + +function chargerAmountContinue() { + let $input = $("#electric-charger-amount-input"); + let currentValue = parseInt($input.val()) || 0; + let newWidthPercentage = ((currentPumpData.vehicleFuel / currentPumpData.vehicleTankSize) * 100) + ((currentValue / currentPumpData.vehicleTankSize) * 100); + + if (currentValue <= 0 || newWidthPercentage > 100) { + return; + } + + let chargerType = getSelectedChargerType(); + $("#electric-charger-pay-button").text(Utils.translate("electricInterface.chargerPayment.payButton").format(Utils.currencyFormat(currentValue * currentPumpData.pricePerLiter["electric"+chargerType], 2))); + $(".electric-charger-amount-container").css("display", "none"); + $(".electric-charger-payment-container").css("display", ""); +} + +function chargerAmountReturn() { + $(".electric-charger-type-container").css("display", ""); + $(".electric-charger-amount-container").css("display", "none"); + $(".electric-charger-payment-container").css("display", "none"); +} + +function confirmRecharge() { + let $input = $("#electric-charger-amount-input"); + let fuelAmount = parseInt($input.val()) || 0; + Utils.post("confirmRefuel", { selectedFuelType: "electric" + getSelectedChargerType(), fuelAmount, paymentMethod: getSelectedElectricPaymentMethod() }); +} + +function chargerPaymentReturn() { + $(".electric-charger-type-container").css("display", "none"); + $(".electric-charger-amount-container").css("display", ""); + $(".electric-charger-payment-container").css("display", "none"); +} + +function getSelectedChargerType() { + const selectedInput = $("input[name='charger-type']:checked"); + return selectedInput.length && !selectedInput.prop("disabled") ? selectedInput.val() : null; +} + +function getSelectedElectricPaymentMethod() { + const selectedInput = $("input[name='charger-payment']:checked"); + return selectedInput.length ? selectedInput.val() : null; +} + +function calculateTimeToRecharge() { + let $input = $("#electric-charger-amount-input"); + let currentValue = parseInt($input.val()); + + // Allow empty input temporarily; validate only non-empty values + if ($input.val().trim() === "" || isNaN(currentValue) || currentValue <= 0) { + currentValue = 0; + } + + if (currentValue > 1000) { + currentValue = 1000; + $input.val(currentValue); + } + + let chargerType = getSelectedChargerType(); + if (chargerType && (chargerType == "fast" || chargerType == "normal")) { + let timeToRecharge = currentValue * currentPumpData.electric.chargeTypes[chargerType].time; + + // Calculate minutes and seconds + let timeToRechargeMinutes = Math.floor(timeToRecharge / 60); + let timeToRechargeSeconds = timeToRecharge % 60; + + $("#electric-time-to-recharge-value").text(Utils.translate("electricInterface.chargerAmount.timeToRechargeValue").format(Utils.numberFormat(timeToRechargeMinutes, 0), Utils.numberFormat(timeToRechargeSeconds, 0))); + + let newWidthPercentage = ((currentPumpData.vehicleFuel / currentPumpData.vehicleTankSize) * 100) + ((currentValue / currentPumpData.vehicleTankSize) * 100); + $("#electric-amount-progress-bar").css("width", newWidthPercentage + "%"); + + if (newWidthPercentage > 100) { + $("#electric-amount-progress-bar").css("background", "red"); + } else { + $("#electric-amount-progress-bar").css("background", ""); + } + } else { + console.log("No charger type selected"); + } +} + +/*=================== + CHART DIALOG +===================*/ + +function openFuelConsumptionChart() { + const $dialog = $("#chart-dialog"); + + $("#chart-dialog-title").text(Utils.translate("fuelConsumptionChart.title")); + $("#chart-dialog-footer-text").text(Utils.translate("fuelConsumptionChart.footer.focus")); + $("#stepper-chart-recording-input").text(Utils.translate("fuelConsumptionChart.footer.recordsLength").format(chartTimestamps[chartTimestampsIndex])); + $("#start-stop-recording-label").text(Utils.translate("fuelConsumptionChart.footer.toggleRecording")); + $("#start-stop-recording").prop("checked", isRecording); + + $dialog.css({ + right: "", + bottom: "", + display: "flex", + }).fadeIn(); + + $dialog.draggable({ + handle: ".dialog-header", + }).resizable({ + minHeight: 450, + minWidth: 300, + maxHeight: $(window).height()*0.8, + maxWidth: $(window).width()*0.8, + }); + speedChart.resize(); + consumptionChart.resize(); + fuelChart.resize(); +} + +function setFuelConsumptionChartPosition(position) { + const $dialog = $("#chart-dialog"); + const windowWidth = $(window).width(); + const dialogWidth = $dialog.outerWidth(); + + let top = 10; + let left; + + // Horizontal position + if (position === "left") { + left = 10; + } else { + left = windowWidth - dialogWidth - 10; + } + + $dialog.css({ + top: `${top}px`, + left: `${left}px`, + }); +} + + +function updateFuelConsumptionChart(latestData) { + const now = Date.now(); + + if (fuelChart && speedChart && consumptionChart) { + fuelChart.data.datasets[0].data.push({ x: now, y: latestData.fuel }); + speedChart.data.datasets[0].data.push({ x: now, y: latestData.speed }); + consumptionChart.data.datasets[0].data.push({ x: now, y: latestData.consumption }); + } + fuelChart.update("quiet"); + speedChart.update("quiet"); + consumptionChart.update("quiet"); +} + +function createFuelConsumptionChartObject() { + if (fuelChart || speedChart || consumptionChart) { return; } + + Chart.defaults.color = "rgba(218, 218, 218, 0.73)"; + Chart.overrides.line.spanGaps = false; + + const baseOptions = { + responsive: true, + maintainAspectRatio: false, + elements: { point: { radius: 0 } }, + animation: false, + interaction: { mode: "index", intersect: false }, + plugins: { + legend: { + position: "bottom", + labels: { boxWidth: 3, boxHeight: 3 }, + }, + streaming: { + frameRate: 10, + }, + }, + scales: { + x: { + type: "realtime", + realtime: { + duration: 30000, + refresh: 1000, + delay: 1000, + }, + }, + y: { + beginAtZero: true, + }, + }, + }; + + // Fuel Chart + + fuelChart = new Chart(document.getElementById("fuel-chart"), { + type: "line", + data: { + datasets: [{ + label: Utils.translate("fuelConsumptionChart.chartLabels.fuel"), + data: [], + borderColor: "#FFD800", + cubicInterpolationMode: "monotone", + }], + }, + options: { + ...baseOptions, + scales: { + ...baseOptions.scales, + y: { + ...baseOptions.scales.y, + suggestedMax: 100, + title: { display: true, text: Utils.translate("fuelConsumptionChart.chartLabels.fuel") }, + }, + }, + }, + }); + + // Speed Chart + + speedChart = new Chart(document.getElementById("speed-chart"), { + type: "line", + data: { + datasets: [{ + label: Utils.translate("fuelConsumptionChart.chartLabels.speed"), + data: [], + borderColor: "#0026FF", + cubicInterpolationMode: "monotone", + }], + }, + options: { + ...baseOptions, + scales: { + ...baseOptions.scales, + y: { + ...baseOptions.scales.y, + suggestedMax: 140, + title: { display: true, text: Utils.translate("fuelConsumptionChart.chartLabels.speed") }, + }, + }, + }, + }); + + // Consumption Chart + + consumptionChart = new Chart(document.getElementById("consumption-chart"), { + type: "line", + data: { + datasets: [{ + label: Utils.translate("fuelConsumptionChart.chartLabels.consumption"), + data: [], + borderColor: "#7F0000", + cubicInterpolationMode: "monotone", + }], + }, + options: { + ...baseOptions, + scales: { + ...baseOptions.scales, + y: { + ...baseOptions.scales.y, + suggestedMax: 0.3, + title: { display: true, text: Utils.translate("fuelConsumptionChart.chartLabels.consumption") }, + }, + }, + }, + }); +} + + +/*================= + LISTENERS +=================*/ + +$(window).click(function(event) { + // Close the modal when clicking outside of it + if ($(event.target).is(".modal")) { + closeModal(); + } +}); + +$(document).on("keydown", function(event) { + // Handle press of Esc key + if (event.key === "Escape" || event.keyCode === 27) { + // Check if the modal is open by checking if it's visible + if ($("#chart-dialog").is(":visible")) { + closeFuelConsumptionChartUI(); + } else if ($(".modal").is(":visible")) { + closeModal(); + } else { + closeUI(); + } + } + if (event.key === toggleChartFocusShortcut + || event.key === "w" || event.key === "W" + || event.key === "a" || event.key === "A" + || event.key === "d" || event.key === "D" + || event.key === "s" || event.key === "S") { + // Check if the modal is open by checking if it's visible + if ($("#chart-dialog").is(":visible")) { + removeFocusFuelConsumptionChartUI(); + } + } +}); + +$(document).ready(function() { + // Handle the add button + $(".refuel-add").click(function() { + let $input = $("#input-fuel-amount"); + let currentValue = parseInt($input.val()) || 0; + if (currentValue < Math.floor(currentPumpData.vehicleTankSize - currentPumpData.vehicleFuel)) { + $input.val((currentValue + 1) + " L"); + } + }); + $(".recharge-add").click(function() { + let $input = $("#electric-charger-amount-input"); + let currentValue = parseInt($input.val()) || 0; + if (currentValue < Math.floor(currentPumpData.vehicleTankSize - currentPumpData.vehicleFuel)) { + $input.val((currentValue + 1)); + calculateTimeToRecharge(); + } + }); + + // Handle the sub button + $(".refuel-sub").click(function() { + let $input = $("#input-fuel-amount"); + let currentValue = parseInt($input.val()) || 0; + if (currentValue > 1) { + $input.val((currentValue - 1) + " L"); + } + }); + $(".recharge-sub").click(function() { + let $input = $("#electric-charger-amount-input"); + let currentValue = parseInt($input.val()) || 0; + if (currentValue > 1) { + $input.val((currentValue - 1)); + calculateTimeToRecharge(); + } + }); + + // Remove 'L' suffix on focus to allow numeric input, and add it back on blur + $("#input-fuel-amount").on("focus", function() { + $(this).val(parseInt($(this).val()) || 1); + }).on("blur", function() { + updateFuelAmountDisplay(); + }); + + // Recalculate time when change input + $("#electric-charger-amount-input").on("input", function() { + calculateTimeToRecharge(); + }); + $("#electric-charger-amount-input").on("blur", function() { + let $input = $(this); + let currentValue = parseInt($input.val()); + + // If invalid, reset to 0 + if (isNaN(currentValue) || currentValue <= 0) { + $input.val(0); + } + }); + + // Handle chart buttons + $("#start-stop-recording").change(function() { + if ($(this).is(":checked")) { + startRecordingGraph(); + } else { + stopRecordingGraph(); + } + }); + + $("#increase-chart-recording").click(function() { + if (chartTimestampsIndex < chartTimestamps.length - 1) { + chartTimestampsIndex++; + $("#stepper-chart-recording-input").text(Utils.translate("fuelConsumptionChart.footer.recordsLength").format(chartTimestamps[chartTimestampsIndex])); + changeRecordingIndexGraph(); + } + }); + + $("#decrease-chart-recording").click(function() { + if (chartTimestampsIndex > 0) { + chartTimestampsIndex--; + $("#stepper-chart-recording-input").text(Utils.translate("fuelConsumptionChart.footer.recordsLength").format(chartTimestamps[chartTimestampsIndex])); + changeRecordingIndexGraph(); + } + }); +}); + + +/*================= + CALLBACKS +=================*/ + +function closeUI(){ + Utils.post("close",""); +} + +function stopRecordingGraph(){ + fuelChart.options.scales.x.realtime.pause = true; + speedChart.options.scales.x.realtime.pause = true; + consumptionChart.options.scales.x.realtime.pause = true; + Utils.post("stopRecordingGraph",""); +} + +function startRecordingGraph(){ + fuelChart.options.scales.x.realtime.pause = false; + speedChart.options.scales.x.realtime.pause = false; + consumptionChart.options.scales.x.realtime.pause = false; + Utils.post("startRecordingGraph",""); +} + +function changeRecordingIndexGraph(){ + fuelChart.options.scales.x.realtime.duration = chartTimestamps[chartTimestampsIndex] * 1000; + speedChart.options.scales.x.realtime.duration = chartTimestamps[chartTimestampsIndex] * 1000; + consumptionChart.options.scales.x.realtime.duration = chartTimestamps[chartTimestampsIndex] * 1000; + Utils.post("changeRecordingIndexGraph",chartTimestampsIndex); +} + +function closeFuelConsumptionChartUI(){ + Utils.post("closeFuelConsumptionChartUI",""); +} + +function removeFocusFuelConsumptionChartUI(){ + Utils.post("removeFocusFuelConsumptionChartUI",""); } \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/nui/scripts/chartjs-plugin-streaming@2.0.0 b/resources/[carscripts]/lc_fuel/nui/scripts/chartjs-plugin-streaming@2.0.0 index f499cdd0b..3d76e308f 100644 --- a/resources/[carscripts]/lc_fuel/nui/scripts/chartjs-plugin-streaming@2.0.0 +++ b/resources/[carscripts]/lc_fuel/nui/scripts/chartjs-plugin-streaming@2.0.0 @@ -1,7 +1,7 @@ -/*! - * chartjs-plugin-streaming v2.0.0 - * https://nagix.github.io/chartjs-plugin-streaming - * (c) 2017-2021 Akihiko Kusanagi - * Released under the MIT license - */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["chart.js","chart.js/helpers"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).ChartStreaming=t(e.Chart,e.Chart.helpers)}(this,(function(e,t){"use strict";function o(e,t,o){return Math.min(Math.max(e,t),o)}function n(e,o){const n=e.options.realtime,a=e.chart.options.plugins.streaming;return t.valueOrDefault(n[o],a[o])}function a(e,{x:o,y:n},{xAxisID:a,yAxisID:i}){const s={};return t.each(o,(e=>{s[e]={axisId:a}})),t.each(n,(e=>{s[e]={axisId:i}})),s}const i="undefined"==typeof window?t.noop:window.cancelAnimationFrame;function s(e){const t=e.frameRequestID;t&&(i.call(window,t),delete e.frameRequestID)}function r(e){const t=e.refreshTimerID;t&&(clearInterval(t),delete e.refreshTimerID,delete e.refreshInterval)}function l(e,o,n){e.refreshTimerID||(e.refreshTimerID=setInterval((()=>{const n=t.callback(o);e.refreshInterval===n||isNaN(n)||(r(e),l(e,o,n))}),n||0),e.refreshInterval=n||0)}function c(e,o,n){return o="number"==typeof o?o:e.parse(o),t.isFinite(o)?{value:e.getPixelForValue(o),transitionable:!0}:{value:n}}function d(){const t=e.registry.getElement("boxAnnotation"),o=e.registry.getElement("lineAnnotation"),n=e.registry.getElement("pointAnnotation"),a=t.prototype.resolveElementProperties,i=o.prototype.resolveElementProperties,s=n.prototype.resolveElementProperties;t.prototype.resolveElementProperties=function(e,t){return function(e,t,o){const{scales:n,chartArea:a}=t,{xScaleID:i,yScaleID:s,xMin:r,xMax:l,yMin:d,yMax:u}=o,m=n[i],p=n[s],{top:f,left:h,bottom:g,right:y}=a,x=e.$streaming={};if(m){const e=c(m,r,h),t=c(m,l,y),o=e.value>t.value;e.transitionable&&(x[o?"x2":"x"]={axisId:i}),t.transitionable&&(x[o?"x":"x2"]={axisId:i}),e.transitionable!==t.transitionable&&(x.width={axisId:i,reverse:e.transitionable})}if(p){const e=c(p,d,f),t=c(p,u,g),o=e.value>t.value;e.transitionable&&(x[o?"y2":"y"]={axisId:s}),t.transitionable&&(x[o?"y":"y2"]={axisId:s}),e.transitionable!==t.transitionable&&(x.height={axisId:s,reverse:e.transitionable})}}(this,e,t),a.call(this,e,t)},o.prototype.resolveElementProperties=function(e,t){const o=e.chartArea;e.chartArea=function(e,t,o){const{scales:n,chartArea:a}=t,{scaleID:i,value:s}=o,r=n[i],{top:l,left:d,bottom:u,right:m}=a,p=e.$streaming={};if(r){const e=r.isHorizontal();return c(r,s).transitionable&&(p[e?"x":"y"]={axisId:i},p[e?"x2":"y2"]={axisId:i}),e?{top:l,bottom:u}:{left:d,right:m}}const{xScaleID:f,yScaleID:h,xMin:g,xMax:y,yMin:x,yMax:b}=o,v=n[f],I=n[h],D={};if(v){const e=c(v,g),t=c(v,y);e.transitionable?p.x={axisId:f}:D.left=d,t.transitionable?p.x2={axisId:f}:D.right=m}if(I){const e=c(I,x),t=c(I,b);e.transitionable?p.y={axisId:h}:D.top=l,t.transitionable?p.y2={axisId:h}:D.bottom=u}return D}(this,e,t);const n=i.call(this,e,t);return e.chartArea=o,n},n.prototype.resolveElementProperties=function(e,t){return function(e,t,o){const n=t.scales,{xScaleID:a,yScaleID:i,xValue:s,yValue:r}=o,l=n[a],d=n[i],u=e.$streaming={};l&&c(l,s).transitionable&&(u.x={axisId:a});d&&c(d,r).transitionable&&(u.y={axisId:i})}(this,e,t),s.call(this,e,t)}}const u={x:["x","caretX"],y:["y","caretY"]};function m(...e){const t=this,o=t.getActiveElements()[0];if(o){const e=t.chart.getDatasetMeta(o.datasetIndex);t.$streaming=a(0,u,e)}else t.$streaming={};t.constructor.prototype.update.call(t,...e)}const p=new WeakMap;function f(e){const{originalScaleOptions:o}=function(e){let t=p.get(e);return t||(t={originalScaleOptions:{}},p.set(e,t)),t}(e),a=e.scales;return t.each(a,(e=>{const t=e.id;o[t]||(o[t]={duration:n(e,"duration"),delay:n(e,"delay")})})),t.each(o,((e,t)=>{a[t]||delete o[t]})),o}function h(e,t,a,i){const{chart:s,axis:r}=e,{minDuration:l=0,maxDuration:c=1/0,minDelay:d=-1/0,maxDelay:u=1/0}=i&&i[r]||{},m=e.options.realtime,p=n(e,"duration"),h=n(e,"delay"),g=o(p*(2-t),l,c);let y,x;return f(s),y=e.isHorizontal()?(e.right-a.x)/(e.right-e.left):(e.bottom-a.y)/(e.bottom-e.top),x=h+y*(p-g),m.duration=g,m.delay=o(x,d,u),g!==e.max-e.min}function g(e,t,a){const{chart:i,axis:s}=e,{minDelay:r=-1/0,maxDelay:l=1/0}=a&&a[s]||{},c=n(e,"delay")+(e.getValueForPixel(t)-e.getValueForPixel(0));return f(i),e.options.realtime.delay=o(c,r,l),!0}function y(e,o){const n=o.$streaming;if(n.zoomPlugin!==e){const a=n.resetZoom=o.resetZoom;!function(e){e.zoomFunctions.realtime=h,e.panFunctions.realtime=g}(e),o.resetZoom=e=>{!function(e){const o=f(e);t.each(e.scales,(e=>{const t=e.options.realtime;if(t){const n=o[e.id];n?(t.duration=n.duration,t.delay=n.delay):(delete t.duration,delete t.delay)}}))}(o),a(e)},n.zoomPlugin=e}}function x(e){const t=e.$streaming;t.zoomPlugin&&(e.resetZoom=t.resetZoom,function(e){p.delete(e)}(e),delete t.resetZoom,delete t.zoomPlugin)}const b={millisecond:{common:!0,size:1,steps:[1,2,5,10,20,50,100,250,500]},second:{common:!0,size:1e3,steps:[1,2,5,10,15,30]},minute:{common:!0,size:6e4,steps:[1,2,5,10,15,30]},hour:{common:!0,size:36e5,steps:[1,2,3,6,12]},day:{common:!0,size:864e5,steps:[1,2,5]},week:{common:!1,size:6048e5,steps:[1,2,3,4]},month:{common:!0,size:2628e6,steps:[1,2,3]},quarter:{common:!1,size:7884e6,steps:[1,2,3,4]},year:{common:!0,size:3154e7}},v=Object.keys(b);function I(e,o,n){if(n){if(n.length){const{lo:a,hi:i}=t._lookup(n,o);e[n[a]>=o?n[a]:n[i]]=!0}}else e[o]=!0}const D=["pointBackgroundColor","pointBorderColor","pointBorderWidth","pointRadius","pointRotation","pointStyle","pointHitRadius","pointHoverBackgroundColor","pointHoverBorderColor","pointHoverBorderWidth","pointHoverRadius","backgroundColor","borderColor","borderSkipped","borderWidth","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth","hoverRadius","hitRadius","radius","rotation"];function k(e,o,n){const a=e.$animations||{};t.each(e.$streaming,((i,s)=>{if(i.axisId===o){const o=i.reverse?-n:n,r=a[s];t.isFinite(e[s])&&(e[s]-=o),r&&(r._from-=o,r._to-=o)}}))}class w extends e.TimeScale{constructor(e){super(e),this.$realtime=this.$realtime||{}}init(e,o){const a=this;super.init(e,o),l(a.$realtime,(()=>{const e=a.chart,o=n(a,"onRefresh");return t.callback(o,[e],a),function(e){const{chart:o,id:a,max:i}=e,s=n(e,"duration"),r=n(e,"delay"),l=n(e,"ttl"),c=n(e,"pause"),d=Date.now()-(isNaN(l)?s+r:l);let u,m,p,f;t.each(o.data.datasets,((e,n)=>{const s=o.getDatasetMeta(n),r=a===s.xAxisID?"x":a===s.yAxisID&&"y";if(r){const a=s.controller,h=e.data,g=h.length;if(c){for(u=0;u{t.isArray(e[o])&&e[o].splice(m,p)})),t.each(e.datalabels,(e=>{t.isArray(e)&&e.splice(m,p)})),"object"!=typeof h[0]&&(f={start:m,count:p}),t.each(o._active,((e,t)=>{e.datasetIndex===n&&e.index>=m&&(e.index>=m+p?e.index-=p:o._active.splice(t,1))}),null,!0)}})),f&&o.data.labels.splice(f.start,f.count)}(a),e.update("quiet"),n(a,"refresh")}))}update(e,o,a){const i=this,{$realtime:r,options:l}=i,{bounds:c,offset:d,ticks:u}=l,{autoSkip:m,source:p,major:f}=u,h=f.enabled;n(i,"pause")?s(r):(r.frameRequestID||(r.head=Date.now()),function(e,o){if(!e.frameRequestID){const n=()=>{const a=e.nextRefresh||0,i=Date.now();if(a<=i){const n=t.callback(o),a=1e3/(Math.max(n,0)||30),s=e.nextRefresh+a||0;e.nextRefresh=s>i?s:i+a}e.frameRequestID=t.requestAnimFrame.call(window,n)};e.frameRequestID=t.requestAnimFrame.call(window,n)}}(r,(()=>{const e=i.chart,o=e.$streaming;return function(e){const{chart:o,id:a,$realtime:i}=e,s=n(e,"duration"),r=n(e,"delay"),l=e.isHorizontal(),c=l?e.width:e.height,d=Date.now(),u=o.tooltip,m=function(e){const t=e.$streaming.annotationPlugin;if(t){const o=t._getState(e);return o&&o.elements||[]}return[]}(o);let p=c*(d-i.head)/s;l===!!e.options.reverse&&(p=-p),t.each(o.data.datasets,((e,t)=>{const n=o.getDatasetMeta(t),{data:i=[],dataset:s}=n;for(let e=0,t=i.length;el.shift(),set:t.noop}),Object.defineProperty(e,"max",{get:()=>r.shift(),set:t.noop});const c=super.buildTicks();return delete e.min,delete e.max,e.min=s,e.max=i,c}calculateLabelRotation(){const e=this.options.ticks,t=e.maxRotation;e.maxRotation=e.minRotation||0,super.calculateLabelRotation(),e.maxRotation=t}fit(){const e=this,t=e.options;super.fit(),t.ticks.display&&t.display&&e.isHorizontal()&&(e.paddingLeft=3,e.paddingRight=3,e._handleMargins())}draw(e){const o=this,{chart:n,ctx:a}=o,i=o.isHorizontal()?{left:e.left,top:0,right:e.right,bottom:n.height}:{left:0,top:e.top,right:n.width,bottom:e.bottom};o._gridLineItems=null,o._labelItems=null,t.clipArea(a,i),super.draw(e),t.unclipArea(a)}destroy(){const e=this.$realtime;s(e),r(e)}_generate(){const e=this,o=e._adapter,a=n(e,"duration"),i=n(e,"delay"),s=n(e,"refresh"),r=e.$realtime.head-i,l=r-a,c=e._getLabelCapacity(l),{time:d,ticks:u}=e.options,m=d.unit||function(e,t,o,n){const a=o-t,i=v.length;for(let t=v.indexOf(e);t1e5*f)throw new Error(l+" and "+r+" are too far apart with stepSize of "+f+" "+m);k=R,g&&p&&!y&&!d.round&&(k=+o.startOf(k,p),k=+o.add(k,~~((R-k)/(x.size*f))*f,m));const $="data"===u.source&&e.getDataTimestamps();for(w=0;ke-t)).map((e=>+e))}}w.id="realtime",w.defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},realtime:{},ticks:{autoSkip:!1,source:"auto",major:{enabled:!0}}},e.defaults.describe("scale.realtime",{_scriptable:e=>"onRefresh"!==e});e.defaults.set("transitions",{quiet:{animation:{duration:0}}});const R={x:["x","cp1x","cp2x"],y:["y","cp1y","cp2y"]};function $(o){const n=this;"quiet"===o&&t.each(n.data.datasets,((t,o)=>{n.getDatasetMeta(o).controller._setStyle=function(t,o,n,a){e.DatasetController.prototype._setStyle.call(this,t,o,"quiet",a)}})),e.Chart.prototype.update.call(n,o),"quiet"===o&&t.each(n.data.datasets,((e,t)=>{delete n.getDatasetMeta(t).controller._setStyle}))}function E(e){const t=e.$streaming;e.render(),t.lastMouseEvent&&setTimeout((()=>{const o=t.lastMouseEvent;o&&e._eventHandler(o)}),0)}const M=[{id:"streaming",version:"2.0.0",beforeInit(e){const o=e.$streaming=e.$streaming||{render:E},n=o.canvas=e.canvas,a=o.mouseEventListener=n=>{const a=t.getRelativePosition(n,e);o.lastMouseEvent={type:"mousemove",chart:e,native:n,x:a.x,y:a.y}};n.addEventListener("mousedown",a),n.addEventListener("mouseup",a)},afterInit(e){e.update=$},beforeUpdate(o){const{scales:n,elements:a}=o.options,i=o.tooltip;t.each(n,(({type:e})=>{"realtime"===e&&(a.line.capBezierPoints=!1)})),i&&(i.update=m);try{!function(e,t){const o=t.$streaming;if(o.annotationPlugin!==e){const t=e.afterUpdate;d(),o.annotationPlugin=e,e.afterUpdate=(e,o,n)=>{const a=o.mode,i=n.animation;"quiet"===a&&(n.animation=!1),t.call(this,e,o,n),"quiet"===a&&(n.animation=i)}}}(e.registry.getPlugin("annotation"),o)}catch(e){!function(e){delete e.$streaming.annotationPlugin}(o)}try{y(e.registry.getPlugin("zoom"),o)}catch(e){x(o)}},beforeDatasetUpdate(e,o){const{meta:n,mode:a}=o;if("quiet"===a){const{controller:e,$animations:o}=n;o&&o.visible&&o.visible._active&&(e.updateElement=t.noop,e.updateSharedOptions=t.noop)}},afterDatasetUpdate(e,t){const{meta:o,mode:n}=t,{data:i=[],dataset:s,controller:r}=o;for(let e=0,t=i.length;e{e instanceof w&&e.destroy()}))},defaults:{duration:1e4,delay:0,frameRate:30,refresh:1e3,onRefresh:null,pause:!1,ttl:void 0},descriptors:{_scriptable:e=>"onRefresh"!==e}},w];return e.Chart.register(M),M})); +/*! + * chartjs-plugin-streaming v2.0.0 + * https://nagix.github.io/chartjs-plugin-streaming + * (c) 2017-2021 Akihiko Kusanagi + * Released under the MIT license + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["chart.js","chart.js/helpers"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).ChartStreaming=t(e.Chart,e.Chart.helpers)}(this,(function(e,t){"use strict";function o(e,t,o){return Math.min(Math.max(e,t),o)}function n(e,o){const n=e.options.realtime,a=e.chart.options.plugins.streaming;return t.valueOrDefault(n[o],a[o])}function a(e,{x:o,y:n},{xAxisID:a,yAxisID:i}){const s={};return t.each(o,(e=>{s[e]={axisId:a}})),t.each(n,(e=>{s[e]={axisId:i}})),s}const i="undefined"==typeof window?t.noop:window.cancelAnimationFrame;function s(e){const t=e.frameRequestID;t&&(i.call(window,t),delete e.frameRequestID)}function r(e){const t=e.refreshTimerID;t&&(clearInterval(t),delete e.refreshTimerID,delete e.refreshInterval)}function l(e,o,n){e.refreshTimerID||(e.refreshTimerID=setInterval((()=>{const n=t.callback(o);e.refreshInterval===n||isNaN(n)||(r(e),l(e,o,n))}),n||0),e.refreshInterval=n||0)}function c(e,o,n){return o="number"==typeof o?o:e.parse(o),t.isFinite(o)?{value:e.getPixelForValue(o),transitionable:!0}:{value:n}}function d(){const t=e.registry.getElement("boxAnnotation"),o=e.registry.getElement("lineAnnotation"),n=e.registry.getElement("pointAnnotation"),a=t.prototype.resolveElementProperties,i=o.prototype.resolveElementProperties,s=n.prototype.resolveElementProperties;t.prototype.resolveElementProperties=function(e,t){return function(e,t,o){const{scales:n,chartArea:a}=t,{xScaleID:i,yScaleID:s,xMin:r,xMax:l,yMin:d,yMax:u}=o,m=n[i],p=n[s],{top:f,left:h,bottom:g,right:y}=a,x=e.$streaming={};if(m){const e=c(m,r,h),t=c(m,l,y),o=e.value>t.value;e.transitionable&&(x[o?"x2":"x"]={axisId:i}),t.transitionable&&(x[o?"x":"x2"]={axisId:i}),e.transitionable!==t.transitionable&&(x.width={axisId:i,reverse:e.transitionable})}if(p){const e=c(p,d,f),t=c(p,u,g),o=e.value>t.value;e.transitionable&&(x[o?"y2":"y"]={axisId:s}),t.transitionable&&(x[o?"y":"y2"]={axisId:s}),e.transitionable!==t.transitionable&&(x.height={axisId:s,reverse:e.transitionable})}}(this,e,t),a.call(this,e,t)},o.prototype.resolveElementProperties=function(e,t){const o=e.chartArea;e.chartArea=function(e,t,o){const{scales:n,chartArea:a}=t,{scaleID:i,value:s}=o,r=n[i],{top:l,left:d,bottom:u,right:m}=a,p=e.$streaming={};if(r){const e=r.isHorizontal();return c(r,s).transitionable&&(p[e?"x":"y"]={axisId:i},p[e?"x2":"y2"]={axisId:i}),e?{top:l,bottom:u}:{left:d,right:m}}const{xScaleID:f,yScaleID:h,xMin:g,xMax:y,yMin:x,yMax:b}=o,v=n[f],I=n[h],D={};if(v){const e=c(v,g),t=c(v,y);e.transitionable?p.x={axisId:f}:D.left=d,t.transitionable?p.x2={axisId:f}:D.right=m}if(I){const e=c(I,x),t=c(I,b);e.transitionable?p.y={axisId:h}:D.top=l,t.transitionable?p.y2={axisId:h}:D.bottom=u}return D}(this,e,t);const n=i.call(this,e,t);return e.chartArea=o,n},n.prototype.resolveElementProperties=function(e,t){return function(e,t,o){const n=t.scales,{xScaleID:a,yScaleID:i,xValue:s,yValue:r}=o,l=n[a],d=n[i],u=e.$streaming={};l&&c(l,s).transitionable&&(u.x={axisId:a});d&&c(d,r).transitionable&&(u.y={axisId:i})}(this,e,t),s.call(this,e,t)}}const u={x:["x","caretX"],y:["y","caretY"]};function m(...e){const t=this,o=t.getActiveElements()[0];if(o){const e=t.chart.getDatasetMeta(o.datasetIndex);t.$streaming=a(0,u,e)}else t.$streaming={};t.constructor.prototype.update.call(t,...e)}const p=new WeakMap;function f(e){const{originalScaleOptions:o}=function(e){let t=p.get(e);return t||(t={originalScaleOptions:{}},p.set(e,t)),t}(e),a=e.scales;return t.each(a,(e=>{const t=e.id;o[t]||(o[t]={duration:n(e,"duration"),delay:n(e,"delay")})})),t.each(o,((e,t)=>{a[t]||delete o[t]})),o}function h(e,t,a,i){const{chart:s,axis:r}=e,{minDuration:l=0,maxDuration:c=1/0,minDelay:d=-1/0,maxDelay:u=1/0}=i&&i[r]||{},m=e.options.realtime,p=n(e,"duration"),h=n(e,"delay"),g=o(p*(2-t),l,c);let y,x;return f(s),y=e.isHorizontal()?(e.right-a.x)/(e.right-e.left):(e.bottom-a.y)/(e.bottom-e.top),x=h+y*(p-g),m.duration=g,m.delay=o(x,d,u),g!==e.max-e.min}function g(e,t,a){const{chart:i,axis:s}=e,{minDelay:r=-1/0,maxDelay:l=1/0}=a&&a[s]||{},c=n(e,"delay")+(e.getValueForPixel(t)-e.getValueForPixel(0));return f(i),e.options.realtime.delay=o(c,r,l),!0}function y(e,o){const n=o.$streaming;if(n.zoomPlugin!==e){const a=n.resetZoom=o.resetZoom;!function(e){e.zoomFunctions.realtime=h,e.panFunctions.realtime=g}(e),o.resetZoom=e=>{!function(e){const o=f(e);t.each(e.scales,(e=>{const t=e.options.realtime;if(t){const n=o[e.id];n?(t.duration=n.duration,t.delay=n.delay):(delete t.duration,delete t.delay)}}))}(o),a(e)},n.zoomPlugin=e}}function x(e){const t=e.$streaming;t.zoomPlugin&&(e.resetZoom=t.resetZoom,function(e){p.delete(e)}(e),delete t.resetZoom,delete t.zoomPlugin)}const b={millisecond:{common:!0,size:1,steps:[1,2,5,10,20,50,100,250,500]},second:{common:!0,size:1e3,steps:[1,2,5,10,15,30]},minute:{common:!0,size:6e4,steps:[1,2,5,10,15,30]},hour:{common:!0,size:36e5,steps:[1,2,3,6,12]},day:{common:!0,size:864e5,steps:[1,2,5]},week:{common:!1,size:6048e5,steps:[1,2,3,4]},month:{common:!0,size:2628e6,steps:[1,2,3]},quarter:{common:!1,size:7884e6,steps:[1,2,3,4]},year:{common:!0,size:3154e7}},v=Object.keys(b);function I(e,o,n){if(n){if(n.length){const{lo:a,hi:i}=t._lookup(n,o);e[n[a]>=o?n[a]:n[i]]=!0}}else e[o]=!0}const D=["pointBackgroundColor","pointBorderColor","pointBorderWidth","pointRadius","pointRotation","pointStyle","pointHitRadius","pointHoverBackgroundColor","pointHoverBorderColor","pointHoverBorderWidth","pointHoverRadius","backgroundColor","borderColor","borderSkipped","borderWidth","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth","hoverRadius","hitRadius","radius","rotation"];function k(e,o,n){const a=e.$animations||{};t.each(e.$streaming,((i,s)=>{if(i.axisId===o){const o=i.reverse?-n:n,r=a[s];t.isFinite(e[s])&&(e[s]-=o),r&&(r._from-=o,r._to-=o)}}))}class w extends e.TimeScale{constructor(e){super(e),this.$realtime=this.$realtime||{}}init(e,o){const a=this;super.init(e,o),l(a.$realtime,(()=>{const e=a.chart,o=n(a,"onRefresh");return t.callback(o,[e],a),function(e){const{chart:o,id:a,max:i}=e,s=n(e,"duration"),r=n(e,"delay"),l=n(e,"ttl"),c=n(e,"pause"),d=Date.now()-(isNaN(l)?s+r:l);let u,m,p,f;t.each(o.data.datasets,((e,n)=>{const s=o.getDatasetMeta(n),r=a===s.xAxisID?"x":a===s.yAxisID&&"y";if(r){const a=s.controller,h=e.data,g=h.length;if(c){for(u=0;u{t.isArray(e[o])&&e[o].splice(m,p)})),t.each(e.datalabels,(e=>{t.isArray(e)&&e.splice(m,p)})),"object"!=typeof h[0]&&(f={start:m,count:p}),t.each(o._active,((e,t)=>{e.datasetIndex===n&&e.index>=m&&(e.index>=m+p?e.index-=p:o._active.splice(t,1))}),null,!0)}})),f&&o.data.labels.splice(f.start,f.count)}(a),e.update("quiet"),n(a,"refresh")}))}update(e,o,a){const i=this,{$realtime:r,options:l}=i,{bounds:c,offset:d,ticks:u}=l,{autoSkip:m,source:p,major:f}=u,h=f.enabled;n(i,"pause")?s(r):(r.frameRequestID||(r.head=Date.now()),function(e,o){if(!e.frameRequestID){const n=()=>{const a=e.nextRefresh||0,i=Date.now();if(a<=i){const n=t.callback(o),a=1e3/(Math.max(n,0)||30),s=e.nextRefresh+a||0;e.nextRefresh=s>i?s:i+a}e.frameRequestID=t.requestAnimFrame.call(window,n)};e.frameRequestID=t.requestAnimFrame.call(window,n)}}(r,(()=>{const e=i.chart,o=e.$streaming;return function(e){const{chart:o,id:a,$realtime:i}=e,s=n(e,"duration"),r=n(e,"delay"),l=e.isHorizontal(),c=l?e.width:e.height,d=Date.now(),u=o.tooltip,m=function(e){const t=e.$streaming.annotationPlugin;if(t){const o=t._getState(e);return o&&o.elements||[]}return[]}(o);let p=c*(d-i.head)/s;l===!!e.options.reverse&&(p=-p),t.each(o.data.datasets,((e,t)=>{const n=o.getDatasetMeta(t),{data:i=[],dataset:s}=n;for(let e=0,t=i.length;el.shift(),set:t.noop}),Object.defineProperty(e,"max",{get:()=>r.shift(),set:t.noop});const c=super.buildTicks();return delete e.min,delete e.max,e.min=s,e.max=i,c}calculateLabelRotation(){const e=this.options.ticks,t=e.maxRotation;e.maxRotation=e.minRotation||0,super.calculateLabelRotation(),e.maxRotation=t}fit(){const e=this,t=e.options;super.fit(),t.ticks.display&&t.display&&e.isHorizontal()&&(e.paddingLeft=3,e.paddingRight=3,e._handleMargins())}draw(e){const o=this,{chart:n,ctx:a}=o,i=o.isHorizontal()?{left:e.left,top:0,right:e.right,bottom:n.height}:{left:0,top:e.top,right:n.width,bottom:e.bottom};o._gridLineItems=null,o._labelItems=null,t.clipArea(a,i),super.draw(e),t.unclipArea(a)}destroy(){const e=this.$realtime;s(e),r(e)}_generate(){const e=this,o=e._adapter,a=n(e,"duration"),i=n(e,"delay"),s=n(e,"refresh"),r=e.$realtime.head-i,l=r-a,c=e._getLabelCapacity(l),{time:d,ticks:u}=e.options,m=d.unit||function(e,t,o,n){const a=o-t,i=v.length;for(let t=v.indexOf(e);t1e5*f)throw new Error(l+" and "+r+" are too far apart with stepSize of "+f+" "+m);k=R,g&&p&&!y&&!d.round&&(k=+o.startOf(k,p),k=+o.add(k,~~((R-k)/(x.size*f))*f,m));const $="data"===u.source&&e.getDataTimestamps();for(w=0;ke-t)).map((e=>+e))}}w.id="realtime",w.defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},realtime:{},ticks:{autoSkip:!1,source:"auto",major:{enabled:!0}}},e.defaults.describe("scale.realtime",{_scriptable:e=>"onRefresh"!==e});e.defaults.set("transitions",{quiet:{animation:{duration:0}}});const R={x:["x","cp1x","cp2x"],y:["y","cp1y","cp2y"]};function $(o){const n=this;"quiet"===o&&t.each(n.data.datasets,((t,o)=>{n.getDatasetMeta(o).controller._setStyle=function(t,o,n,a){e.DatasetController.prototype._setStyle.call(this,t,o,"quiet",a)}})),e.Chart.prototype.update.call(n,o),"quiet"===o&&t.each(n.data.datasets,((e,t)=>{delete n.getDatasetMeta(t).controller._setStyle}))}function E(e){const t=e.$streaming;e.render(),t.lastMouseEvent&&setTimeout((()=>{const o=t.lastMouseEvent;o&&e._eventHandler(o)}),0)}const M=[{id:"streaming",version:"2.0.0",beforeInit(e){const o=e.$streaming=e.$streaming||{render:E},n=o.canvas=e.canvas,a=o.mouseEventListener=n=>{const a=t.getRelativePosition(n,e);o.lastMouseEvent={type:"mousemove",chart:e,native:n,x:a.x,y:a.y}};n.addEventListener("mousedown",a),n.addEventListener("mouseup",a)},afterInit(e){e.update=$},beforeUpdate(o){const{scales:n,elements:a}=o.options,i=o.tooltip;t.each(n,(({type:e})=>{"realtime"===e&&(a.line.capBezierPoints=!1)})),i&&(i.update=m);try{!function(e,t){const o=t.$streaming;if(o.annotationPlugin!==e){const t=e.afterUpdate;d(),o.annotationPlugin=e,e.afterUpdate=(e,o,n)=>{const a=o.mode,i=n.animation;"quiet"===a&&(n.animation=!1),t.call(this,e,o,n),"quiet"===a&&(n.animation=i)}}}(e.registry.getPlugin("annotation"),o)}catch(e){!function(e){delete e.$streaming.annotationPlugin}(o)}try{y(e.registry.getPlugin("zoom"),o)}catch(e){x(o)}},beforeDatasetUpdate(e,o){const{meta:n,mode:a}=o;if("quiet"===a){const{controller:e,$animations:o}=n;o&&o.visible&&o.visible._active&&(e.updateElement=t.noop,e.updateSharedOptions=t.noop)}},afterDatasetUpdate(e,t){const{meta:o,mode:n}=t,{data:i=[],dataset:s,controller:r}=o;for(let e=0,t=i.length;e{e instanceof w&&e.destroy()}))},defaults:{duration:1e4,delay:0,frameRate:30,refresh:1e3,onRefresh:null,pause:!1,ttl:void 0},descriptors:{_scriptable:e=>"onRefresh"!==e}},w];return e.Chart.register(M),M})); diff --git a/resources/[carscripts]/lc_fuel/nui/ui.html b/resources/[carscripts]/lc_fuel/nui/ui.html index d17eacf02..79dd2469b 100644 --- a/resources/[carscripts]/lc_fuel/nui/ui.html +++ b/resources/[carscripts]/lc_fuel/nui/ui.html @@ -1,279 +1,279 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/[carscripts]/lc_fuel/server/server.lua b/resources/[carscripts]/lc_fuel/server/server.lua index 44eef9155..9642bf60f 100644 --- a/resources/[carscripts]/lc_fuel/server/server.lua +++ b/resources/[carscripts]/lc_fuel/server/server.lua @@ -1,579 +1,582 @@ - ------------------------------------------------------------------------------------------------------------------------------------------ --- Versioning ------------------------------------------------------------------------------------------------------------------------------------------ - -version = '' -subversion = '' -api_response = {} -local utils_required_version = '1.2.1' -local utils_outdated = false - -function checkVersion() - CreateThread(function() - local connected = false - local attempts = 0 - while not connected and attempts < 3 do - attempts = attempts + 1 - - PerformHttpRequest("https://raw.githubusercontent.com/LeonardoSoares98/lc_fuel/main/version", function(errorCode, resultData) - if errorCode == 200 and resultData then - connected = true - local latest_version = Utils.Math.trim(resultData) - - api_response.latest_version = latest_version - if Utils.Math.checkIfCurrentVersionisOutdated(latest_version, version) then - api_response.has_update = true - print("^4["..GetCurrentResourceName().."] An update is available, download it in https://github.com/LeonardoSoares98/lc_fuel/releases/latest/download/lc_fuel.zip^7 ^3[v"..api_response.latest_version.."]^7") - else - api_response.has_update = false - end - end - end, "GET", "", {}) - - Wait(10000) - end - end) -end - ------------------------------------------------------------------------------------------------------------------------------------------ --- Script global variables ------------------------------------------------------------------------------------------------------------------------------------------ - -Utils = Utils or exports['lc_utils']:GetUtils() -local cooldown = {} -local playerVehiclesFuelType = {} -local fuelPurchased = {} - ------------------------------------------------------------------------------------------------------------------------------------------ --- Script functions ------------------------------------------------------------------------------------------------------------------------------------------ - -RegisterServerEvent("lc_fuel:serverOpenUI") -AddEventHandler("lc_fuel:serverOpenUI",function(isElectric, pumpModel, vehicleFuel, vehicleTankSize, vehiclePlate) - local source = source - Wrapper(source,function(user_id) - local gasStationId = getCurrentGasStationId(source) - serverOpenUI(source, isElectric, pumpModel, gasStationId, vehicleFuel, vehicleTankSize, vehiclePlate) - end) -end) - -RegisterServerEvent("lc_fuel:confirmRefuel") -AddEventHandler("lc_fuel:confirmRefuel",function(data) - local source = source - Wrapper(source,function(user_id) - if not data or data.fuelAmount <= 0 or not isFuelTypeValid(data.selectedFuelType) or not isPaymentMethodValid(data.paymentMethod) then - TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('invalid_value')) - return - end - - local gasStationId = getCurrentGasStationId(source) - local stationData = getStationData(gasStationId) - local pricePerLiter = stationData.pricePerLiter[data.selectedFuelType] - local initialPrice = pricePerLiter * data.fuelAmount - - local discount = getPlayerDiscountAmount(source) - local finalPrice = initialPrice * (1 - (discount / 100)) - if Utils.Framework.getPlayerAccountMoney(source, Config.Accounts[data.paymentMethod]) < finalPrice then - TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('not_enough_money'):format(Utils.numberFormat(finalPrice))) - return - end - - if not removeStockFromStation(gasStationId, finalPrice, data.fuelAmount, data.selectedFuelType, false) then - TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('not_enough_stock')) - return - end - - Utils.Framework.tryRemoveAccountMoney(source, finalPrice, Config.Accounts[data.paymentMethod]) - - fuelPurchased[source] = { - finalPrice = finalPrice, - account = Config.Accounts[data.paymentMethod], - selectedFuelType = data.selectedFuelType, - fuelAmount = data.fuelAmount, - pricePerLiter = pricePerLiter, - } - - TriggerClientEvent("lc_fuel:getPumpNozzle", source, data.fuelAmount, data.selectedFuelType) - TriggerClientEvent("lc_fuel:Notify", source, "success", Utils.translate('refuel_paid'):format(Utils.numberFormat(finalPrice))) - end) -end) - - -RegisterServerEvent("lc_fuel:returnNozzle") -AddEventHandler("lc_fuel:returnNozzle",function(remainingFuel, isElectric) - local source = source - Wrapper(source,function(user_id) - - if not Config.ReturnNozzleRefund then - return - end - - if not fuelPurchased[source] then - return - end - - if remainingFuel < 1 then - fuelPurchased[source] = nil - return - end - - local discount = getPlayerDiscountAmount(source) - local amountToReturn = math.floor(remainingFuel * (fuelPurchased[source].pricePerLiter * (1 - (discount / 100)))) - - if amountToReturn > fuelPurchased[source].finalPrice or remainingFuel > fuelPurchased[source].fuelAmount then - print("User "..user_id.." initially purchased "..fuelPurchased[source].fuelAmount.."L of fuel but now is returning "..remainingFuel.."L. Is this user trying to glitch something?") - print("User coords: "..GetEntityCoords(GetPlayerPed(source))) - fuelPurchased[source] = nil - return - end - - local gasStationId = getCurrentGasStationId(source) - local _, returnedAmount = returnStockToGasStation(gasStationId, amountToReturn, remainingFuel, fuelPurchased[source].selectedFuelType) - - if isElectric then - TriggerClientEvent("lc_fuel:Notify", source, "success", Utils.translate('returned_charge'):format(Utils.Math.round(remainingFuel, 1), returnedAmount)) - else - TriggerClientEvent("lc_fuel:Notify", source, "success", Utils.translate('returned_fuel'):format(Utils.Math.round(remainingFuel, 1), returnedAmount)) - end - Utils.Framework.giveAccountMoney(source, returnedAmount, fuelPurchased[source].account) - - fuelPurchased[source] = nil - end) -end) - -RegisterServerEvent("lc_fuel:confirmJerryCanPurchase") -AddEventHandler("lc_fuel:confirmJerryCanPurchase",function(data) - if not Config.JerryCan.enabled then return end - local source = source - Wrapper(source,function(user_id) - if not isPaymentMethodValid(data.paymentMethod) then - TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('invalid_value')) - return - end - - local gasStationId = getCurrentGasStationId(source) - local stationData = getStationData(gasStationId) - local fuelType = nil - -- Check which type has enough stock - if stationData.stationStock.regular >= Config.JerryCan.requiredStock then - fuelType = "regular" - elseif stationData.stationStock.plus >= Config.JerryCan.requiredStock then - fuelType = "plus" - elseif stationData.stationStock.premium >= Config.JerryCan.requiredStock then - fuelType = "premium" - elseif stationData.stationStock.diesel >= Config.JerryCan.requiredStock then - fuelType = "diesel" - end - - if fuelType == nil then - TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('not_enough_stock')) - return - end - - if Utils.Framework.getPlayerAccountMoney(source, Config.Accounts[data.paymentMethod]) < Config.JerryCan.price then - TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('not_enough_money'):format(Config.JerryCan.price)) - return - end - - if not removeStockFromStation(gasStationId, Config.JerryCan.price, Config.JerryCan.requiredStock, fuelType, true) then - TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('not_enough_stock')) - return - end - Utils.Framework.tryRemoveAccountMoney(source, Config.JerryCan.price, Config.Accounts[data.paymentMethod]) - - -- Gives the jerry can to the player - if Config.JerryCan.giveAsWeapon then - Utils.Framework.givePlayerWeapon(source, Config.JerryCan.item, 1, Config.JerryCan.metadata) - else - Utils.Framework.givePlayerItem(source, Config.JerryCan.item, 1, Config.JerryCan.metadata) - end - - TriggerClientEvent("lc_fuel:Notify", source, "success", Utils.translate('jerry_can_paid'):format(Config.JerryCan.price)) - TriggerClientEvent("lc_fuel:closeUI", source, data.fuelAmount, data.selectedFuelType) - end) -end) - -function serverOpenUI(source, isElectric, pumpModel, gasStationId, vehicleFuel, vehicleTankSize, vehiclePlate) - local stationData = getStationData(gasStationId) - local discount = getPlayerDiscountAmount(source) - - local data = { - pricePerLiter = stationData.pricePerLiter, - stationStock = stationData.stationStock, - currentFuelType = getVehicleFuelType(vehiclePlate), - vehicleFuel = vehicleFuel or 0, - vehicleTankSize = vehicleTankSize or 100, - cashBalance = Utils.Framework.getPlayerAccountMoney(source, Config.Accounts.account1), - bankBalance = Utils.Framework.getPlayerAccountMoney(source, Config.Accounts.account2), - jerryCan = Config.JerryCan, - isElectric = isElectric, - electric = Config.Electric, - pumpModel = pumpModel, - } - - -- Apply the discount to each fuel type price based on player job - for fuelType, price in pairs(stationData.pricePerLiter) do - data.pricePerLiter[fuelType] = price * (1 - (discount / 100)) - end - - TriggerClientEvent("lc_fuel:clientOpenUI", source, data) -end - ------------------------------------------------------------------------------------------------------------------------------------------ --- Gas Station stock functions ------------------------------------------------------------------------------------------------------------------------------------------ - -function getCurrentGasStationId(source) - if not Config.PlayerOwnedGasStations.enabled then return nil end - local playerCoords = GetEntityCoords(GetPlayerPed(source)) - for k,v in pairs(Config.PlayerOwnedGasStations.gasStations) do - if #(v.vector - playerCoords) <= v.radius then - return k - end - end - return nil -end - -function getStationData(gasStationId) - local stationData = getStationDataFromConfig() - if not gasStationId then - return stationData - end - - local sql = "SELECT price, stock, price_plus, stock_plus, price_premium, stock_premium, price_diesel, stock_diesel, price_electricfast, stock_electricfast, price_electricnormal, stock_electricnormal FROM gas_station_business WHERE gas_station_id = @gas_station_id"; - local query = Utils.Database.fetchAll(sql, {['@gas_station_id'] = gasStationId})[1]; - - if not query then - return stationData - end - - local sql = "UPDATE `gas_station_business` SET total_visits = total_visits + 1 WHERE gas_station_id = @gas_station_id"; - Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId}); - - stationData = { - pricePerLiter = { - regular = query.price/100, - plus = query.price_plus/100, - premium = query.price_premium/100, - diesel = query.price_diesel/100, - electricfast = query.price_electricfast/100, - electricnormal = query.price_electricnormal/100, - }, - stationStock = { - regular = query.stock, - plus = query.stock_plus, - premium = query.stock_premium, - diesel = query.stock_diesel, - electricfast = query.stock_electricfast == 1 and 100 or 0, - electricnormal = query.stock_electricnormal == 1 and 100 or 0, - } - } - - return stationData -end - -function removeStockFromStation(gasStationId, pricePaid, fuelAmount, fuelType, isJerryCan) - pricePaid = math.floor(pricePaid) - - if not isFuelTypeValid(fuelType) then - print("Invalid fuel type: "..(fuelType or "nil")) - return false - end - - -- If not owned - local stationData = getStationDataFromConfig() - if not gasStationId then - if stationData.stationStock[fuelType] >= fuelAmount then - -- If set in config to unowed gas stations have stock - return true - else - return false - end - end - - local column = "stock_"..fuelType - if fuelType == "regular" then - column = "stock" - end - - local sql = "SELECT "..column.." as stock FROM gas_station_business WHERE gas_station_id = @gas_station_id"; - local query = Utils.Database.fetchAll(sql, {['@gas_station_id'] = gasStationId})[1]; - - if not query then - if stationData.stationStock[fuelType] >= fuelAmount then - -- If set in config to unowed gas stations have stock - return true - else - return false - end - end - - if isFuelTypeElectric(fuelType) then - if query.stock < 1 then - return false - end - - local sql = "UPDATE `gas_station_business` SET customers = customers + 1, money = money + @price, total_money_earned = total_money_earned + @price WHERE gas_station_id = @gas_station_id"; - Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId, ['@price'] = pricePaid, ['@amount'] = fuelAmount}); - - if isJerryCan then - print("Jerry can in electric charger???") - return false - else - local sql = "INSERT INTO `gas_station_balance` (gas_station_id,income,title,amount,date) VALUES (@gas_station_id,@income,@title,@amount,@date)"; - Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId, ['@income'] = 0, ['@title'] = Utils.translate('owned_gas_stations.balance_electric'):format(fuelAmount), ['@amount'] = pricePaid, ['@date'] = os.time()}); - end - else - if query.stock < fuelAmount then - return false - end - - local sql = "UPDATE `gas_station_business` SET "..column.." = @stock, customers = customers + 1, money = money + @price, total_money_earned = total_money_earned + @price, gas_sold = gas_sold + @amount WHERE gas_station_id = @gas_station_id"; - Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId, ['@stock'] = (query.stock - fuelAmount), ['@price'] = pricePaid, ['@amount'] = fuelAmount}); - - if isJerryCan then - local sql = "INSERT INTO `gas_station_balance` (gas_station_id,income,title,amount,date) VALUES (@gas_station_id,@income,@title,@amount,@date)"; - Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId, ['@income'] = 0, ['@title'] = Utils.translate('owned_gas_stations.balance_jerry_can'):format(fuelAmount), ['@amount'] = pricePaid, ['@date'] = os.time()}); - else - local sql = "INSERT INTO `gas_station_balance` (gas_station_id,income,title,amount,date) VALUES (@gas_station_id,@income,@title,@amount,@date)"; - Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId, ['@income'] = 0, ['@title'] = Utils.translate('owned_gas_stations.balance_fuel'):format(fuelAmount), ['@amount'] = pricePaid, ['@date'] = os.time()}); - end - end - return true -end - -function returnStockToGasStation(gasStationId, priceRefunded, fuelAmount, fuelType) - priceRefunded = math.floor(priceRefunded) - - if not isFuelTypeValid(fuelType) then - print("Invalid fuel type: "..(fuelType or "nil")) - return false, 0 - end - - -- If not owned - local stationData = getStationDataFromConfig() - if not gasStationId then - if stationData.stationStock[fuelType] >= fuelAmount then - -- If set in config to unowed gas stations have stock - return true, priceRefunded - else - return false, 0 - end - end - - local column = "stock_"..fuelType - if fuelType == "regular" then - column = "stock" - end - - local sql = "SELECT "..column.." as stock, money, total_money_earned, gas_sold FROM gas_station_business WHERE gas_station_id = @gas_station_id"; - local query = Utils.Database.fetchAll(sql, {['@gas_station_id'] = gasStationId})[1]; - - if not query then - if stationData.stationStock[fuelType] >= fuelAmount then - -- If set in config to unowed gas stations have stock - return true, priceRefunded - else - return false, 0 - end - end - - local actualRefund = 0 - - if isFuelTypeElectric(fuelType) then - actualRefund = math.min(priceRefunded, query.money) - - local newMoney = math.max(query.money - actualRefund, 0) - local newTotalEarned = math.max(query.total_money_earned - actualRefund, 0) - - local sql = "UPDATE `gas_station_business` SET money = @money, total_money_earned = @total WHERE gas_station_id = @gas_station_id"; - Utils.Database.execute(sql, { ['@gas_station_id'] = gasStationId, ['@money'] = newMoney, ['@total'] = newTotalEarned }) - - local sql = "INSERT INTO `gas_station_balance` (gas_station_id,income,title,amount,date) VALUES (@gas_station_id,@income,@title,@amount,@date)"; - Utils.Database.execute(sql, { ['@gas_station_id'] = gasStationId, ['@income'] = 1, ['@title'] = Utils.translate('owned_gas_stations.refund_electric'):format(fuelAmount), ['@amount'] = actualRefund, ['@date'] = os.time() }) - else - actualRefund = math.min(priceRefunded, query.money) - - local newStock = query.stock + fuelAmount - local newMoney = math.max(query.money - actualRefund, 0) - local newTotalEarned = math.max(query.total_money_earned - actualRefund, 0) - local newGasSold = math.max((query.gas_sold or 0) - fuelAmount, 0) - - local sql = "UPDATE `gas_station_business` SET "..column.." = @stock, money = @money, total_money_earned = @total, gas_sold = @sold WHERE gas_station_id = @gas_station_id"; - Utils.Database.execute(sql, { ['@gas_station_id'] = gasStationId, ['@stock'] = newStock, ['@money'] = newMoney, ['@total'] = newTotalEarned, ['@sold'] = newGasSold }) - - local sql = "INSERT INTO `gas_station_balance` (gas_station_id,income,title,amount,date) VALUES (@gas_station_id,@income,@title,@amount,@date)"; - Utils.Database.execute(sql, { ['@gas_station_id'] = gasStationId, ['@income'] = 1, ['@title'] = Utils.translate('owned_gas_stations.refund_fuel'):format(fuelAmount), ['@amount'] = actualRefund, ['@date'] = os.time() }) - end - return true, actualRefund -end - ------------------------------------------------------------------------------------------------------------------------------------------ --- Utils functions ------------------------------------------------------------------------------------------------------------------------------------------ - -function getStationDataFromConfig() - return { - pricePerLiter = { - regular = Config.DefaultValues.fuelPrice.regular, - plus = Config.DefaultValues.fuelPrice.plus, - premium = Config.DefaultValues.fuelPrice.premium, - diesel = Config.DefaultValues.fuelPrice.diesel, - electricfast = Config.Electric.chargeTypes.fast.price, - electricnormal = Config.Electric.chargeTypes.normal.price, - }, - stationStock = { - regular = Config.DefaultValues.fuelStock.regular and 1000 or 0, - plus = Config.DefaultValues.fuelStock.plus and 1000 or 0, - premium = Config.DefaultValues.fuelStock.premium and 1000 or 0, - diesel = Config.DefaultValues.fuelStock.diesel and 1000 or 0, - electricfast = Config.Electric.chargeTypes.fast.stock and 1000 or 0, - electricnormal = Config.Electric.chargeTypes.normal.stock and 1000 or 0, - } - } -end - -function isFuelTypeValid(fuelType) - return Utils.Table.contains({"regular", "plus", "premium", "diesel", "electricfast", "electricnormal"}, fuelType) -end - -function isFuelTypeElectric(fuelType) - return Utils.Table.contains({"electricnormal", "electricfast"}, fuelType) -end - -function isPaymentMethodValid(paymentMethod) - return Utils.Table.contains({"account1", "account2"}, paymentMethod) -- These are not really the account, but the index to the Config.Accounts -end - -function getPlayerDiscountAmount(source) - local job, onDuty = Utils.Framework.getPlayerJob(source) - if onDuty and job and Config.JobDiscounts[job] then - return Config.JobDiscounts[job] - end - return 0 -end - -Utils.Callback.RegisterServerCallback('lc_fuel:getVehicleFuelType', function(source, cb, plate) - cb(getVehicleFuelType(plate)) -end) - -RegisterServerEvent("lc_fuel:setVehicleFuelType") -AddEventHandler("lc_fuel:setVehicleFuelType",function(plate, fuelType) - setVehicleFuelType(plate, fuelType) -end) - -function getVehicleFuelType(plate) - return playerVehiclesFuelType[plate] or "default" -end - -function setVehicleFuelType(plate, fuelType) - if not isFuelTypeValid(fuelType) then - fuelType = "regular" - print("Invalid fuel type ("..fuelType..") set to vehicle ("..plate..")") - end - - playerVehiclesFuelType[plate] = fuelType - local sql = [[ - INSERT INTO `player_vehicles_fuel_type` (plate, fuelType) - VALUES (@plate, @fuelType) - ON DUPLICATE KEY UPDATE fuelType = @fuelType - ]]; - Utils.Database.execute(sql, {['@plate'] = plate, ['@fuelType'] = fuelType}) -end - -function cacheplayerVehiclesFuelTypeType() - local sql = "SELECT * FROM player_vehicles_fuel_type"; - local queryData = Utils.Database.fetchAll(sql, {}); - for _, value in pairs(queryData) do - playerVehiclesFuelType[value.plate] = value.fuelType - end - print("^2[lc_fuel] Fuel types successfully fetched from database^7") -end - -function Wrapper(source,cb) - if utils_outdated then - TriggerClientEvent("lc_fuel:Notify",source,"error","The script requires 'lc_utils' in version "..utils_required_version..", but you currently have version "..Utils.Version..". Please update your 'lc_utils' script to the latest version: https://github.com/LeonardoSoares98/lc_utils/releases/latest/download/lc_utils.zip") - return - end - - if cooldown[source] == nil then - cooldown[source] = true - local user_id = Utils.Framework.getPlayerId(source) - if user_id then - cb(user_id) - else - print("User not found: "..source) - end - SetTimeout(100,function() - cooldown[source] = nil - end) - end -end - - -Citizen.CreateThread(function() - Wait(1000) - - -- Load version number from file - local versionFile = LoadResourceFile("lc_fuel", "version") - if versionFile then - version = Utils.Math.trim(versionFile) - print("^2[lc_fuel] Loaded! Support discord: https://discord.gg/U5YDgbh ^3[v"..version..subversion.."]^7") - else - error("^1[lc_fuel] Warning: Could not load the version file.^7") - end - - checkIfFrameworkWasLoaded() - checkScriptName() - - -- Startup queries - runCreateTableQueries() - cacheplayerVehiclesFuelTypeType() - - -- Config checker - assert(Config.FuelConsumptionPerClass, "^3You have errors in your config file, consider fixing it or redownload the original config.^7") - - -- Check lc_utils dependency - assert(GetResourceState('lc_utils') == 'started', "^3The '^1lc_utils^3' file is missing. Please refer to the documentation for installation instructions: ^7https://docs.lixeirocharmoso.com/fuel/installation^7") - - if Utils.Math.checkIfCurrentVersionisOutdated(utils_required_version, Utils.Version) then - utils_outdated = true - error("^3The script requires 'lc_utils' in version ^1"..utils_required_version.."^3, but you currently have version ^1"..Utils.Version.."^3. Please update your 'lc_utils' script to the latest version: https://github.com/LeonardoSoares98/lc_utils/releases/latest/download/lc_utils.zip^7") - end - - -- Load langs - Utils.loadLanguageFile(Lang) - - -- Config validator - local configs_to_validate = { - { config_path = {"Blips", "onlyShowNearestBlip"}, default_value = false }, - { config_path = {"ReturnNozzleRefund"}, default_value = true }, - } - Config = Utils.validateConfig(Config, configs_to_validate) - - Wait(1000) - - checkVersion() -end) - -function checkIfFrameworkWasLoaded() - assert(Utils.Framework.getPlayerId, "^3The framework wasn't loaded in the '^1lc_utils^3' resource. Please check if the '^1Config.framework^3' is correctly set to your framework, and make sure there are no errors in your file. For more information, refer to the documentation at '^7https://docs.lixeirocharmoso.com/^3'.^7") -end - -function checkScriptName() - assert(GetCurrentResourceName() == "lc_fuel", "^3The script name does not match the expected resource name. Please ensure that the current resource name is set to '^1lc_fuel^7'.") -end - -function runCreateTableQueries() - Utils.Database.execute([[ - CREATE TABLE IF NOT EXISTS `player_vehicles_fuel_type` ( - `plate` VARCHAR(20) NOT NULL COLLATE 'utf8_general_ci', - `fuelType` VARCHAR(20) NOT NULL COLLATE 'utf8_general_ci', - PRIMARY KEY (`plate`) USING BTREE - ) - COLLATE='utf8_general_ci' - ENGINE=InnoDB - ; - ]]) -end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Versioning +----------------------------------------------------------------------------------------------------------------------------------------- + +version = '' +subversion = '' +api_response = {} +local utils_required_version = '1.2.1' +local utils_outdated = false + +function checkVersion() + CreateThread(function() + local connected = false + local attempts = 0 + while not connected and attempts < 3 do + attempts = attempts + 1 + + PerformHttpRequest("https://raw.githubusercontent.com/LeonardoSoares98/lc_fuel/main/version", function(errorCode, resultData) + if errorCode == 200 and resultData then + connected = true + local latest_version = Utils.Math.trim(resultData) + + api_response.latest_version = latest_version + if Utils.Math.checkIfCurrentVersionisOutdated(latest_version, version) then + api_response.has_update = true + print("^4["..GetCurrentResourceName().."] An update is available, download it in https://github.com/LeonardoSoares98/lc_fuel/releases/latest/download/lc_fuel.zip^7 ^3[v"..api_response.latest_version.."]^7") + else + api_response.has_update = false + end + end + end, "GET", "", {}) + + Wait(10000) + end + end) +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Script global variables +----------------------------------------------------------------------------------------------------------------------------------------- + +Utils = Utils or exports['lc_utils']:GetUtils() +local cooldown = {} +local playerVehiclesFuelType = {} +local fuelPurchased = {} + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Script functions +----------------------------------------------------------------------------------------------------------------------------------------- + +RegisterServerEvent("lc_fuel:serverOpenUI") +AddEventHandler("lc_fuel:serverOpenUI",function(isElectric, pumpModel, vehicleFuel, vehicleTankSize, vehiclePlate) + local source = source + Wrapper(source,function(user_id) + local gasStationId = getCurrentGasStationId(source) + serverOpenUI(source, isElectric, pumpModel, gasStationId, vehicleFuel, vehicleTankSize, vehiclePlate) + end) +end) + +RegisterServerEvent("lc_fuel:confirmRefuel") +AddEventHandler("lc_fuel:confirmRefuel",function(data) + local source = source + Wrapper(source,function(user_id) + if not data or data.fuelAmount <= 0 or not isFuelTypeValid(data.selectedFuelType) or not isPaymentMethodValid(data.paymentMethod) then + TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('invalid_value')) + return + end + + local gasStationId = getCurrentGasStationId(source) + local stationData = getStationData(gasStationId) + local pricePerLiter = stationData.pricePerLiter[data.selectedFuelType] + local initialPrice = pricePerLiter * data.fuelAmount + + local discount = getPlayerDiscountAmount(source) + local finalPrice = initialPrice * (1 - (discount / 100)) + if Utils.Framework.getPlayerAccountMoney(source, Config.Accounts[data.paymentMethod]) < finalPrice then + TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('not_enough_money'):format(Utils.numberFormat(finalPrice))) + return + end + + if not removeStockFromStation(gasStationId, finalPrice, data.fuelAmount, data.selectedFuelType, false) then + TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('not_enough_stock')) + return + end + + Utils.Framework.tryRemoveAccountMoney(source, finalPrice, Config.Accounts[data.paymentMethod]) + + fuelPurchased[source] = { + finalPrice = finalPrice, + account = Config.Accounts[data.paymentMethod], + selectedFuelType = data.selectedFuelType, + fuelAmount = data.fuelAmount, + pricePerLiter = pricePerLiter, + } + + TriggerClientEvent("lc_fuel:getPumpNozzle", source, data.fuelAmount, data.selectedFuelType) + TriggerClientEvent("lc_fuel:Notify", source, "success", Utils.translate('refuel_paid'):format(Utils.numberFormat(finalPrice))) + end) +end) + + +RegisterServerEvent("lc_fuel:returnNozzle") +AddEventHandler("lc_fuel:returnNozzle",function(remainingFuel, isElectric) + local source = source + Wrapper(source,function(user_id) + + if not Config.ReturnNozzleRefund then + return + end + + if not fuelPurchased[source] then + return + end + + if remainingFuel < 1 then + fuelPurchased[source] = nil + return + end + + local discount = getPlayerDiscountAmount(source) + local amountToReturn = math.floor(remainingFuel * (fuelPurchased[source].pricePerLiter * (1 - (discount / 100)))) + + if amountToReturn > fuelPurchased[source].finalPrice or remainingFuel > fuelPurchased[source].fuelAmount then + print("User "..user_id.." initially purchased "..fuelPurchased[source].fuelAmount.."L of fuel but now is returning "..remainingFuel.."L. Is this user trying to glitch something?") + print("User coords: "..GetEntityCoords(GetPlayerPed(source))) + fuelPurchased[source] = nil + return + end + + local gasStationId = getCurrentGasStationId(source) + local _, returnedAmount = returnStockToGasStation(gasStationId, amountToReturn, remainingFuel, fuelPurchased[source].selectedFuelType) + + if isElectric then + TriggerClientEvent("lc_fuel:Notify", source, "success", Utils.translate('returned_charge'):format(Utils.Math.round(remainingFuel, 1), returnedAmount)) + else + TriggerClientEvent("lc_fuel:Notify", source, "success", Utils.translate('returned_fuel'):format(Utils.Math.round(remainingFuel, 1), returnedAmount)) + end + Utils.Framework.giveAccountMoney(source, returnedAmount, fuelPurchased[source].account) + + fuelPurchased[source] = nil + end) +end) + +RegisterServerEvent("lc_fuel:confirmJerryCanPurchase") +AddEventHandler("lc_fuel:confirmJerryCanPurchase",function(data) + if not Config.JerryCan.enabled then return end + local source = source + Wrapper(source,function(user_id) + if not isPaymentMethodValid(data.paymentMethod) then + TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('invalid_value')) + return + end + + local gasStationId = getCurrentGasStationId(source) + local stationData = getStationData(gasStationId) + local fuelType = nil + -- Check which type has enough stock + if stationData.stationStock.regular >= Config.JerryCan.requiredStock then + fuelType = "regular" + elseif stationData.stationStock.plus >= Config.JerryCan.requiredStock then + fuelType = "plus" + elseif stationData.stationStock.premium >= Config.JerryCan.requiredStock then + fuelType = "premium" + elseif stationData.stationStock.diesel >= Config.JerryCan.requiredStock then + fuelType = "diesel" + end + + if fuelType == nil then + TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('not_enough_stock')) + return + end + + if Utils.Framework.getPlayerAccountMoney(source, Config.Accounts[data.paymentMethod]) < Config.JerryCan.price then + TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('not_enough_money'):format(Config.JerryCan.price)) + return + end + + if not removeStockFromStation(gasStationId, Config.JerryCan.price, Config.JerryCan.requiredStock, fuelType, true) then + TriggerClientEvent("lc_fuel:Notify", source, "error", Utils.translate('not_enough_stock')) + return + end + Utils.Framework.tryRemoveAccountMoney(source, Config.JerryCan.price, Config.Accounts[data.paymentMethod]) + + -- Gives the jerry can to the player + if Config.JerryCan.giveAsWeapon then + Utils.Framework.givePlayerWeapon(source, Config.JerryCan.item, 1, Config.JerryCan.metadata) + else + Utils.Framework.givePlayerItem(source, Config.JerryCan.item, 1, Config.JerryCan.metadata) + end + + TriggerClientEvent("lc_fuel:Notify", source, "success", Utils.translate('jerry_can_paid'):format(Config.JerryCan.price)) + TriggerClientEvent("lc_fuel:closeUI", source, data.fuelAmount, data.selectedFuelType) + end) +end) + +function serverOpenUI(source, isElectric, pumpModel, gasStationId, vehicleFuel, vehicleTankSize, vehiclePlate) + local stationData = getStationData(gasStationId) + local discount = getPlayerDiscountAmount(source) + + local data = { + pricePerLiter = stationData.pricePerLiter, + stationStock = stationData.stationStock, + currentFuelType = getVehicleFuelType(vehiclePlate), + vehicleFuel = vehicleFuel or 0, + vehicleTankSize = vehicleTankSize or 100, + cashBalance = Utils.Framework.getPlayerAccountMoney(source, Config.Accounts.account1), + bankBalance = Utils.Framework.getPlayerAccountMoney(source, Config.Accounts.account2), + jerryCan = Config.JerryCan, + isElectric = isElectric, + electric = Config.Electric, + pumpModel = pumpModel, + } + + -- Apply the discount to each fuel type price based on player job + for fuelType, price in pairs(stationData.pricePerLiter) do + data.pricePerLiter[fuelType] = price * (1 - (discount / 100)) + end + + TriggerClientEvent("lc_fuel:clientOpenUI", source, data) +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Gas Station stock functions +----------------------------------------------------------------------------------------------------------------------------------------- + +function getCurrentGasStationId(source) + if not Config.PlayerOwnedGasStations.enabled then return nil end + local playerCoords = GetEntityCoords(GetPlayerPed(source)) + for k,v in pairs(Config.PlayerOwnedGasStations.gasStations) do + if #(v.vector - playerCoords) <= v.radius then + return k + end + end + return nil +end + +function getStationData(gasStationId) + local stationData = getStationDataFromConfig() + if not gasStationId then + return stationData + end + + local sql = "SELECT price, stock, price_plus, stock_plus, price_premium, stock_premium, price_diesel, stock_diesel, price_electricfast, stock_electricfast, price_electricnormal, stock_electricnormal FROM gas_station_business WHERE gas_station_id = @gas_station_id"; + local query = Utils.Database.fetchAll(sql, {['@gas_station_id'] = gasStationId})[1]; + + if not query then + return stationData + end + + local sql = "UPDATE `gas_station_business` SET total_visits = total_visits + 1 WHERE gas_station_id = @gas_station_id"; + Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId}); + + stationData = { + pricePerLiter = { + regular = query.price/100, + plus = query.price_plus/100, + premium = query.price_premium/100, + diesel = query.price_diesel/100, + electricfast = query.price_electricfast/100, + electricnormal = query.price_electricnormal/100, + }, + stationStock = { + regular = query.stock, + plus = query.stock_plus, + premium = query.stock_premium, + diesel = query.stock_diesel, + electricfast = query.stock_electricfast == 1 and 100 or 0, + electricnormal = query.stock_electricnormal == 1 and 100 or 0, + } + } + + return stationData +end + +function removeStockFromStation(gasStationId, pricePaid, fuelAmount, fuelType, isJerryCan) + pricePaid = math.floor(pricePaid) + + if not isFuelTypeValid(fuelType) then + print("Invalid fuel type: "..(fuelType or "nil")) + return false + end + + -- If not owned + local stationData = getStationDataFromConfig() + if not gasStationId then + if stationData.stationStock[fuelType] >= fuelAmount then + -- If set in config to unowed gas stations have stock + return true + else + return false + end + end + + local column = "stock_"..fuelType + if fuelType == "regular" then + column = "stock" + end + + local sql = "SELECT "..column.." as stock FROM gas_station_business WHERE gas_station_id = @gas_station_id"; + local query = Utils.Database.fetchAll(sql, {['@gas_station_id'] = gasStationId})[1]; + + if not query then + if stationData.stationStock[fuelType] >= fuelAmount then + -- If set in config to unowed gas stations have stock + return true + else + return false + end + end + + if isFuelTypeElectric(fuelType) then + if query.stock < 1 then + return false + end + + local sql = "UPDATE `gas_station_business` SET customers = customers + 1, money = money + @price, total_money_earned = total_money_earned + @price WHERE gas_station_id = @gas_station_id"; + Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId, ['@price'] = pricePaid, ['@amount'] = fuelAmount}); + + if isJerryCan then + print("Jerry can in electric charger???") + return false + else + local sql = "INSERT INTO `gas_station_balance` (gas_station_id,income,title,amount,date) VALUES (@gas_station_id,@income,@title,@amount,@date)"; + Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId, ['@income'] = 0, ['@title'] = Utils.translate('owned_gas_stations.balance_electric'):format(fuelAmount), ['@amount'] = pricePaid, ['@date'] = os.time()}); + end + else + if query.stock < fuelAmount then + return false + end + + local sql = "UPDATE `gas_station_business` SET "..column.." = @stock, customers = customers + 1, money = money + @price, total_money_earned = total_money_earned + @price, gas_sold = gas_sold + @amount WHERE gas_station_id = @gas_station_id"; + Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId, ['@stock'] = (query.stock - fuelAmount), ['@price'] = pricePaid, ['@amount'] = fuelAmount}); + + if isJerryCan then + local sql = "INSERT INTO `gas_station_balance` (gas_station_id,income,title,amount,date) VALUES (@gas_station_id,@income,@title,@amount,@date)"; + Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId, ['@income'] = 0, ['@title'] = Utils.translate('owned_gas_stations.balance_jerry_can'):format(fuelAmount), ['@amount'] = pricePaid, ['@date'] = os.time()}); + else + local sql = "INSERT INTO `gas_station_balance` (gas_station_id,income,title,amount,date) VALUES (@gas_station_id,@income,@title,@amount,@date)"; + Utils.Database.execute(sql, {['@gas_station_id'] = gasStationId, ['@income'] = 0, ['@title'] = Utils.translate('owned_gas_stations.balance_fuel'):format(fuelAmount), ['@amount'] = pricePaid, ['@date'] = os.time()}); + end + end + return true +end + +function returnStockToGasStation(gasStationId, priceRefunded, fuelAmount, fuelType) + priceRefunded = math.floor(priceRefunded) + + if not isFuelTypeValid(fuelType) then + print("Invalid fuel type: "..(fuelType or "nil")) + return false, 0 + end + + -- If not owned + local stationData = getStationDataFromConfig() + if not gasStationId then + if stationData.stationStock[fuelType] >= fuelAmount then + -- If set in config to unowed gas stations have stock + return true, priceRefunded + else + return false, 0 + end + end + + local column = "stock_"..fuelType + if fuelType == "regular" then + column = "stock" + end + + local sql = "SELECT "..column.." as stock, money, total_money_earned, gas_sold FROM gas_station_business WHERE gas_station_id = @gas_station_id"; + local query = Utils.Database.fetchAll(sql, {['@gas_station_id'] = gasStationId})[1]; + + if not query then + if stationData.stationStock[fuelType] >= fuelAmount then + -- If set in config to unowed gas stations have stock + return true, priceRefunded + else + return false, 0 + end + end + + local actualRefund = 0 + + if isFuelTypeElectric(fuelType) then + actualRefund = math.min(priceRefunded, query.money) + + local newMoney = math.max(query.money - actualRefund, 0) + local newTotalEarned = math.max(query.total_money_earned - actualRefund, 0) + + local sql = "UPDATE `gas_station_business` SET money = @money, total_money_earned = @total WHERE gas_station_id = @gas_station_id"; + Utils.Database.execute(sql, { ['@gas_station_id'] = gasStationId, ['@money'] = newMoney, ['@total'] = newTotalEarned }) + + local sql = "INSERT INTO `gas_station_balance` (gas_station_id,income,title,amount,date) VALUES (@gas_station_id,@income,@title,@amount,@date)"; + Utils.Database.execute(sql, { ['@gas_station_id'] = gasStationId, ['@income'] = 1, ['@title'] = Utils.translate('owned_gas_stations.refund_electric'):format(fuelAmount), ['@amount'] = actualRefund, ['@date'] = os.time() }) + else + actualRefund = math.min(priceRefunded, query.money) + + local newStock = query.stock + fuelAmount + local newMoney = math.max(query.money - actualRefund, 0) + local newTotalEarned = math.max(query.total_money_earned - actualRefund, 0) + local newGasSold = math.max((query.gas_sold or 0) - fuelAmount, 0) + + local sql = "UPDATE `gas_station_business` SET "..column.." = @stock, money = @money, total_money_earned = @total, gas_sold = @sold WHERE gas_station_id = @gas_station_id"; + Utils.Database.execute(sql, { ['@gas_station_id'] = gasStationId, ['@stock'] = newStock, ['@money'] = newMoney, ['@total'] = newTotalEarned, ['@sold'] = newGasSold }) + + local sql = "INSERT INTO `gas_station_balance` (gas_station_id,income,title,amount,date) VALUES (@gas_station_id,@income,@title,@amount,@date)"; + Utils.Database.execute(sql, { ['@gas_station_id'] = gasStationId, ['@income'] = 1, ['@title'] = Utils.translate('owned_gas_stations.refund_fuel'):format(fuelAmount), ['@amount'] = actualRefund, ['@date'] = os.time() }) + end + return true, actualRefund +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Utils functions +----------------------------------------------------------------------------------------------------------------------------------------- + +function getStationDataFromConfig() + return { + pricePerLiter = { + regular = Config.DefaultValues.fuelPrice.regular, + plus = Config.DefaultValues.fuelPrice.plus, + premium = Config.DefaultValues.fuelPrice.premium, + diesel = Config.DefaultValues.fuelPrice.diesel, + electricfast = Config.Electric.chargeTypes.fast.price, + electricnormal = Config.Electric.chargeTypes.normal.price, + }, + stationStock = { + regular = Config.DefaultValues.fuelStock.regular and 1000 or 0, + plus = Config.DefaultValues.fuelStock.plus and 1000 or 0, + premium = Config.DefaultValues.fuelStock.premium and 1000 or 0, + diesel = Config.DefaultValues.fuelStock.diesel and 1000 or 0, + electricfast = Config.Electric.chargeTypes.fast.stock and 1000 or 0, + electricnormal = Config.Electric.chargeTypes.normal.stock and 1000 or 0, + } + } +end + +function isFuelTypeValid(fuelType) + return Utils.Table.contains({"regular", "plus", "premium", "diesel", "electricfast", "electricnormal"}, fuelType) +end + +function isFuelTypeElectric(fuelType) + return Utils.Table.contains({"electricnormal", "electricfast"}, fuelType) +end + +function isPaymentMethodValid(paymentMethod) + return Utils.Table.contains({"account1", "account2"}, paymentMethod) -- These are not really the account, but the index to the Config.Accounts +end + +function getPlayerDiscountAmount(source) + local job, onDuty = Utils.Framework.getPlayerJob(source) + if onDuty and job and Config.JobDiscounts[job] then + return Config.JobDiscounts[job] + end + return 0 +end + +Utils.Callback.RegisterServerCallback('lc_fuel:getVehicleFuelType', function(source, cb, plate) + cb(getVehicleFuelType(plate)) +end) + +RegisterServerEvent("lc_fuel:setVehicleFuelType") +AddEventHandler("lc_fuel:setVehicleFuelType",function(plate, fuelType) + setVehicleFuelType(plate, fuelType) +end) + +function getVehicleFuelType(plate) + return playerVehiclesFuelType[plate] or "default" +end + +function setVehicleFuelType(plate, fuelType) + if not isFuelTypeValid(fuelType) then + print("Invalid fuel type ("..fuelType..") set to vehicle ("..plate..")") + fuelType = "regular" + end + + playerVehiclesFuelType[plate] = fuelType + -- Only store in database if the vehicle is in player vehicles table + if Config.SaveAllVehicleFuelTypes == true or (Utils.Framework.getVehicleOwner(Utils.Math.trim(plate)) ~= false or Utils.Framework.getVehicleOwner(plate) ~= false) then + local sql = [[ + INSERT INTO `player_vehicles_fuel_type` (plate, fuelType) + VALUES (@plate, @fuelType) + ON DUPLICATE KEY UPDATE fuelType = @fuelType + ]]; + Utils.Database.execute(sql, {['@plate'] = plate, ['@fuelType'] = fuelType}) + end +end + +function cacheplayerVehiclesFuelTypeType() + local sql = "SELECT * FROM player_vehicles_fuel_type"; + local queryData = Utils.Database.fetchAll(sql, {}); + for _, value in pairs(queryData) do + playerVehiclesFuelType[value.plate] = value.fuelType + end + print("^2[lc_fuel] #"..#queryData.." Fuel types successfully fetched from database^7") +end + +function Wrapper(source,cb) + if utils_outdated then + TriggerClientEvent("lc_fuel:Notify",source,"error","The script requires 'lc_utils' in version "..utils_required_version..", but you currently have version "..Utils.Version..". Please update your 'lc_utils' script to the latest version: https://github.com/LeonardoSoares98/lc_utils/releases/latest/download/lc_utils.zip") + return + end + + if cooldown[source] == nil then + cooldown[source] = true + local user_id = Utils.Framework.getPlayerId(source) + if user_id then + cb(user_id) + else + print("User not found: "..source) + end + SetTimeout(100,function() + cooldown[source] = nil + end) + end +end + + +Citizen.CreateThread(function() + Wait(1000) + + -- Load version number from file + local versionFile = LoadResourceFile("lc_fuel", "version") + if versionFile then + version = Utils.Math.trim(versionFile) + print("^2[lc_fuel] Loaded! Support discord: https://discord.gg/U5YDgbh ^3[v"..version..subversion.."]^7") + else + error("^1[lc_fuel] Warning: Could not load the version file.^7") + end + + checkIfFrameworkWasLoaded() + checkScriptName() + + -- Startup queries + runCreateTableQueries() + cacheplayerVehiclesFuelTypeType() + + -- Config checker + assert(Config.FuelConsumptionPerClass, "^3You have errors in your config file, consider fixing it or redownload the original config.^7") + + -- Check lc_utils dependency + assert(GetResourceState('lc_utils') == 'started', "^3The '^1lc_utils^3' file is missing. Please refer to the documentation for installation instructions: ^7https://docs.lixeirocharmoso.com/fuel/installation^7") + + if Utils.Math.checkIfCurrentVersionisOutdated(utils_required_version, Utils.Version) then + utils_outdated = true + error("^3The script requires 'lc_utils' in version ^1"..utils_required_version.."^3, but you currently have version ^1"..Utils.Version.."^3. Please update your 'lc_utils' script to the latest version: https://github.com/LeonardoSoares98/lc_utils/releases/latest/download/lc_utils.zip^7") + end + + -- Load langs + Utils.loadLanguageFile(Lang) + + -- Config validator + local configs_to_validate = { + { config_path = {"Blips", "onlyShowNearestBlip"}, default_value = false }, + { config_path = {"ReturnNozzleRefund"}, default_value = true }, + } + Config = Utils.validateConfig(Config, configs_to_validate) + + Wait(1000) + + checkVersion() +end) + +function checkIfFrameworkWasLoaded() + assert(Utils.Framework.getPlayerId, "^3The framework wasn't loaded in the '^1lc_utils^3' resource. Please check if the '^1Config.framework^3' is correctly set to your framework, and make sure there are no errors in your file. For more information, refer to the documentation at '^7https://docs.lixeirocharmoso.com/^3'.^7") +end + +function checkScriptName() + assert(GetCurrentResourceName() == "lc_fuel", "^3The script name does not match the expected resource name. Please ensure that the current resource name is set to '^1lc_fuel^7'.") +end + +function runCreateTableQueries() + Utils.Database.execute([[ + CREATE TABLE IF NOT EXISTS `player_vehicles_fuel_type` ( + `plate` VARCHAR(20) NOT NULL COLLATE 'utf8_general_ci', + `fuelType` VARCHAR(20) NOT NULL COLLATE 'utf8_general_ci', + PRIMARY KEY (`plate`) USING BTREE + ) + COLLATE='utf8_general_ci' + ENGINE=InnoDB + ; + ]]) +end diff --git a/resources/[carscripts]/lc_fuel/version b/resources/[carscripts]/lc_fuel/version index d2d61a7e8..e2cac26c1 100644 --- a/resources/[carscripts]/lc_fuel/version +++ b/resources/[carscripts]/lc_fuel/version @@ -1 +1 @@ -1.2.2 \ No newline at end of file +1.2.3 \ No newline at end of file