├── stream ├── id6.ytd ├── id7.ytd └── default.ytd ├── html ├── sounds │ └── initiald.ogg └── speedometer.html ├── config.lua ├── fxmanifest.lua ├── README.md ├── skins ├── default.lua ├── default_middle.lua ├── id7.lua └── id6.lua ├── LICENSE └── client.lua /stream/id6.ytd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esx-community/fivem-speedometer/HEAD/stream/id6.ytd -------------------------------------------------------------------------------- /stream/id7.ytd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esx-community/fivem-speedometer/HEAD/stream/id7.ytd -------------------------------------------------------------------------------- /stream/default.ytd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esx-community/fivem-speedometer/HEAD/stream/default.ytd -------------------------------------------------------------------------------- /html/sounds/initiald.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esx-community/fivem-speedometer/HEAD/html/sounds/initiald.ogg -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | Config = {} 2 | 3 | Config.DefaultSkin = 'default' 4 | 5 | -- Special cars are vehicles that will play a speed chime when going above 105 km/h. 6 | Config.SpecialCars = { 'FUTO', 'BLISTA2' } 7 | Config.PlaySpeedChime = true -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'adamant' 2 | 3 | game 'gta5' 4 | 5 | description 'FiveM FXServer speedometer' 6 | 7 | version '1.1.0' 8 | 9 | client_scripts { 10 | 'config.lua', 11 | 'client.lua', 12 | 'skins/default.lua', 13 | 'skins/default_middle.lua', 14 | 'skins/id6.lua', 15 | 'skins/id7.lua' 16 | } 17 | 18 | exports { 19 | 'getAvailableSkins', 20 | 'changeSkin', 21 | 'addSkin', 22 | 'toggleSpeedo', 23 | 'getCurrentSkin', 24 | 'addSkin', 25 | 'toggleFuelGauge', 26 | 'DoesSkinExist' 27 | } 28 | 29 | ui_page 'html/speedometer.html' 30 | 31 | files { 32 | 'html/speedometer.html', 33 | 'html/sounds/initiald.ogg' 34 | } 35 | -------------------------------------------------------------------------------- /html/speedometer.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fivem-speedometer (SexySpeedometer) 2 | 3 | Good looking speedometer for FiveM, a fork of the original speedometer by [Bluethefurry](https://github.com/Bluethefurry) with awesome improvements. It comes pre-installed with [initialdspeedo](https://github.com/Bluethefurry/initialdspeedo-fivem) (extra skins). The speed unit used is automatically selected depending on your settings. 4 | 5 | ### Links 6 | 7 | - [FiveM thread](https://forum.fivem.net/t/release-sexyspeedometer-tacho-dashboard-elements-skins-fuel/39772) 8 | - [Tutorial: make your own skin](https://forum.fivem.net/t/release-sexyspeedometer-tacho-dashboard-elements-skins-fuel/39772/108) 9 | 10 | ### Preview images 11 | 12 |  default 13 | 14 |  id6 skin 15 | 16 |  id7 skin (+ high rpm) 17 | 18 |  id7 with drift 19 | 20 | ### Commands 21 | 22 | ```bash 23 | - /speedoskin [skinName] 24 | - /speedoskins 25 | - /speedotoggle 26 | ``` 27 | 28 | ### License 29 | 30 | See the [license](LICENSE) for legal information, this script has been licensed under **BSD 2-Clause**. 31 | -------------------------------------------------------------------------------- /skins/default.lua: -------------------------------------------------------------------------------- 1 | local skinData = { 2 | -- names 3 | skinName = 'default', 4 | ytdName = 'default', 5 | -- texture dictionary informations: 6 | -- night textures are supposed to look like this: 7 | -- 'needle', 'tachometer', 'speedometer', 'fuelgauge' 8 | -- daytime textures this: 9 | -- 'needle_day', 'tachometer_day', 'speedometer_day', 'fuelgauge_day' 10 | -- these names are hardcoded 11 | 12 | -- where the speedo gets centered, values below are OFFSETS from this. 13 | centerCoords = {0.8,0.8}, 14 | 15 | -- icon locations 16 | -- these are xy,width,height 17 | lightsLoc = {0.010,0.092,0.018,0.02}, 18 | blinkerLoc = {0.105,0.034,0.022,0.03}, 19 | fuelLoc = {0.105,0.090,0.012,0.025}, 20 | oilLoc = {0.100,0.062,0.020,0.025}, 21 | engineLoc = {0.130,0.092,0.020,0.025}, 22 | 23 | -- gauge locations 24 | SpeedoBGLoc = {0.000,0.060,0.12,0.185}, 25 | SpeedoNeedleLoc = {0.000,0.062,0.076,0.15}, 26 | 27 | TachoBGloc = {0.120,0.060,0.12,0.185}, 28 | TachoNeedleLoc = {0.120,0.062,0.076,0.15}, 29 | 30 | FuelBGLoc = {0.060, -0.020,0.04, 0.04}, 31 | FuelGaugeLoc = {0.060,0.000,0.040,0.08}, 32 | 33 | RotMult = 2.036936, 34 | RotStep = 2.32833, 35 | 36 | -- rpm scale, defines how 'far' the rpm gauge goes before hitting redline 37 | rpmScale = 270, 38 | rpmScaleDecrease = 30, -- how much we want to decrease the rpm end result, this gives lower idle 39 | 40 | } 41 | 42 | addSkin(skinData) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017-2018, Bluethefurry 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /skins/default_middle.lua: -------------------------------------------------------------------------------- 1 | local skinData = { 2 | -- names 3 | skinName = 'default_middle', 4 | ytdName = 'default', 5 | -- texture dictionary informations: 6 | -- night textures are supposed to look like this: 7 | -- 'needle', 'tachometer', 'speedometer', 'fuelgauge' 8 | -- daytime textures this: 9 | -- 'needle_day', 'tachometer_day', 'speedometer_day', 'fuelgauge_day' 10 | -- these names are hardcoded 11 | 12 | -- where the speedo gets centered, values below are OFFSETS from this. 13 | centerCoords = {0.45,0.85}, 14 | 15 | -- icon locations 16 | -- these are xy,width,height 17 | lightsLoc = {0.010,0.092,0.018,0.02}, 18 | blinkerLoc = {0.105,0.034,0.022,0.03}, 19 | fuelLoc = {0.105,0.090,0.012,0.025}, 20 | oilLoc = {0.100,0.062,0.020,0.025}, 21 | engineLoc = {0.130,0.092,0.020,0.025}, 22 | 23 | -- gauge locations 24 | SpeedoBGLoc = {0.000,0.060,0.12,0.185}, 25 | SpeedoNeedleLoc = {0.000,0.062,0.076,0.15}, 26 | 27 | TachoBGloc = {0.120,0.060,0.12,0.185}, 28 | TachoNeedleLoc = {0.120,0.062,0.076,0.15}, 29 | 30 | FuelBGLoc = {0.060, -0.020,0.04, 0.04}, 31 | FuelGaugeLoc = {0.060,0.000,0.040,0.08}, 32 | 33 | RotMult = 2.036936, 34 | RotStep = 2.32833, 35 | 36 | -- rpm scale, defines how 'far' the rpm gauge goes before hitting redline 37 | rpmScale = 250, 38 | rpmScaleDecrease = 30, -- how much we want to decrease the rpm end result, this gives lower idle 39 | 40 | } 41 | 42 | addSkin(skinData) -------------------------------------------------------------------------------- /skins/id7.lua: -------------------------------------------------------------------------------- 1 | local skinData = { 2 | skinName = 'id7', 3 | ytdName = 'id7', 4 | 5 | centerCoords = {0.8,0.8}, 6 | 7 | lightsLoc = {0.015,0.12,0.018,0.02}, 8 | blinkerLoc = {0.04,0.12,0.022,0.03}, 9 | fuelLoc = {-0.005,0.12,0.012,0.025}, 10 | oilLoc = {0.100,0.12,0.020,0.025}, 11 | engineLoc = {0.130,0.12,0.020,0.025}, 12 | 13 | SpeedoBGLoc = {0.115, 0.012, 0.17,0.28}, 14 | SpeedoNeedleLoc = {0.000,5,0.076,0.15}, 15 | 16 | TachoBGloc = {0.108,0.009,0.135,0.235}, 17 | TachoNeedleLoc = {0.108,0.009,0.135,0.215}, 18 | 19 | FuelBGLoc = {0.085, 0.020,0.030, 0.020}, 20 | FuelGaugeLoc = {0.060,0.000,0.030,0.080}, 21 | 22 | GearLoc = {0.115,0.043,0.025,0.055}, -- gear location 23 | Speed1Loc = {0.090,-0.020,0.022,0.05}, -- 3rd digit 24 | Speed2Loc = {0.106,-0.020,0.022,0.05}, -- 2nd digit 25 | Speed3Loc = {0.126,-0.020,0.022,0.05}, -- 1st digit 26 | UnitLoc = {0.145,-0.000,0.020,0.020}, 27 | RevLight = {0.1054,-0.005,0.138,0.230}, 28 | 29 | RotMult = 2.036936, 30 | RotStep = 2.32833, 31 | 32 | rpmScale = 250, 33 | rpmScaleDecrease = 60 34 | } 35 | 36 | addSkin(skinData) 37 | 38 | -- addon code 39 | local labelType = '8k' 40 | local curDriftAlpha = 0 41 | 42 | function angle(vehicle) 43 | if not vehicle then return false end 44 | local vx,vy,vz = table.unpack(GetEntityVelocity(vehicle)) 45 | local modV = math.sqrt(vx*vx + vy*vy) 46 | 47 | local rx,ry,rz = table.unpack(GetEntityRotation(vehicle,0)) 48 | local sn,cs = -math.sin(math.rad(rz)), math.cos(math.rad(rz)) 49 | 50 | if GetEntitySpeed(vehicle)* 3.6 < 40 or GetVehicleCurrentGear(vehicle) == 0 then 51 | return 0, modV -- --speed over 25 km/h 52 | end 53 | 54 | local cosX = (sn*vx + cs*vy)/modV 55 | 56 | return math.deg(math.acos(cosX))*0.5, modV 57 | end 58 | 59 | local function BlinkDriftText(hide) 60 | if overwriteAlpha then curDriftAlpha = 0 return end 61 | 62 | if hide or goDown then 63 | curDriftAlpha = curDriftAlpha - 15 64 | elseif not hide or not goDown then 65 | curDriftAlpha = curDriftAlpha + 15 66 | end 67 | 68 | if curDriftAlpha <= 0 then 69 | curDriftAlpha = 0 70 | goDown = false 71 | elseif curDriftAlpha >= 255 then 72 | curDriftAlpha = 255 73 | if driftSprite ~= 'drift_yellow' then 74 | goDown = true 75 | end 76 | end 77 | end 78 | 79 | SpeedChimeActive = false 80 | 81 | Citizen.CreateThread(function() 82 | while true do 83 | 84 | Citizen.Wait(0) 85 | 86 | if getCurrentSkin() == skinData.skinName then 87 | speedTable = {} 88 | toggleFuelGauge(false) 89 | 90 | local playerPed = PlayerPedId() 91 | local vehicle = GetVehiclePedIsUsing(playerPed) 92 | local vehicleClass = GetVehicleClass(vehicle) 93 | 94 | if DoesEntityExist(vehicle) and not IsEntityDead(vehicle) then 95 | if vehicleClass >= 0 and vehicleClass <= 5 then 96 | labelType = '8k' 97 | cst.rpmScale = 235 98 | elseif vehicleClass == 6 then 99 | labelType = '9k' 100 | cst.rpmScale = 235 101 | elseif vehicleClass == 7 then 102 | labelType = '10k' 103 | cst.rpmScale = 235 104 | elseif vehicleClass == 8 then 105 | labelType = '13k' 106 | cst.rpmScale = 235 107 | end 108 | 109 | for i,name in ipairs(Config.SpecialCars) do 110 | if GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)) == name then 111 | labelType = '86' 112 | cst.rpmScale = 242 113 | 114 | if not SpeedChimeActive and GetEntitySpeed(vehicle) * 3.6 > 105.0 then 115 | SpeedChimeActive = true 116 | TriggerEvent('fivem-speedometer:playSound', 'initiald', 0.7, true) 117 | elseif SpeedChimeActive and GetEntitySpeed(vehicle) * 3.6 < 105.0 then 118 | SpeedChimeActive = false 119 | TriggerEvent('fivem-speedometer:stopSound') 120 | end 121 | 122 | break 123 | end 124 | end 125 | 126 | local _,lightsOn,highbeamsOn = GetVehicleLightsState(vehicle) 127 | 128 | if lightsOn == 1 or highbeamsOn == 1 then 129 | curTachometer = 'night_labels_'..labelType 130 | else 131 | curTachometer = 'labels_'..labelType 132 | end 133 | 134 | local curSpeedometer = 'nodrift_background' 135 | local gear = GetVehicleCurrentGear(vehicle) + 1 136 | local speed = GetEntitySpeed(vehicle) 137 | 138 | if not gear then gear = 1 end 139 | if gear == 1 then gear = 0 end 140 | 141 | if RPM > 0.90 then 142 | DrawSprite(cst.ytdName, 'rev_light', cst.centerCoords[1]+cst.RevLight[1],cst.centerCoords[2]+cst.RevLight[2],cst.RevLight[3],cst.RevLight[4], 0.0, 255, 255, 255, curAlpha) 143 | end 144 | 145 | DrawSprite(cst.ytdName, curSpeedometer, cst.centerCoords[1]+cst.SpeedoBGLoc[1],cst.centerCoords[2]+cst.SpeedoBGLoc[2],cst.SpeedoBGLoc[3],cst.SpeedoBGLoc[4], 0.0, 255, 255, 255, curAlpha) 146 | DrawSprite(cst.ytdName, curTachometer, cst.centerCoords[1]+cst.TachoBGloc[1],cst.centerCoords[2]+cst.TachoBGloc[2],cst.TachoBGloc[3],cst.TachoBGloc[4], 0.0, 255, 255, 255, curAlpha) 147 | DrawSprite(cst.ytdName, 'gear_'..gear, cst.centerCoords[1]+cst.GearLoc[1],cst.centerCoords[2]+cst.GearLoc[2],cst.GearLoc[3],cst.GearLoc[4], 0.0, 255, 255, 255, curAlpha) 148 | 149 | if useKMH then 150 | speed = speed * 3.6 151 | DrawSprite(cst.ytdName, 'kmh', cst.centerCoords[1]+cst.UnitLoc[1],cst.centerCoords[2]+cst.UnitLoc[2],cst.UnitLoc[3],cst.UnitLoc[4], 0.0, 255, 255, 255, curAlpha) 152 | else 153 | speed = speed * 2.236936 154 | DrawSprite(cst.ytdName, 'mph', cst.centerCoords[1]+cst.UnitLoc[1],cst.centerCoords[2]+cst.UnitLoc[2],cst.UnitLoc[3],cst.UnitLoc[4], 0.0, 255, 255, 255, curAlpha) 155 | end 156 | 157 | if not speed then speed = '0.0' end 158 | speed = tonumber(string.format('%.' .. (0) .. 'f', speed)) 159 | speed = tostring(speed) 160 | 161 | for i = 1, string.len(speed) do 162 | speedTable[i] = speed:sub(i, i) 163 | end 164 | 165 | if string.len(speed) == 1 then 166 | DrawSprite(cst.ytdName, 'speed_digits_'..speedTable[1], cst.centerCoords[1]+cst.Speed3Loc[1],cst.centerCoords[2]+cst.Speed3Loc[2],cst.Speed3Loc[3],cst.Speed3Loc[4], 0.0, 255, 255, 255, curAlpha) 167 | elseif string.len(speed) == 2 then 168 | DrawSprite(cst.ytdName, 'speed_digits_'..speedTable[1], cst.centerCoords[1]+cst.Speed2Loc[1],cst.centerCoords[2]+cst.Speed2Loc[2],cst.Speed2Loc[3],cst.Speed2Loc[4], 0.0, 255, 255, 255, curAlpha) 169 | DrawSprite(cst.ytdName, 'speed_digits_'..speedTable[2], cst.centerCoords[1]+cst.Speed3Loc[1],cst.centerCoords[2]+cst.Speed3Loc[2],cst.Speed3Loc[3],cst.Speed3Loc[4], 0.0, 255, 255, 255, curAlpha) 170 | elseif string.len(speed) == 3 then 171 | DrawSprite(cst.ytdName, 'speed_digits_'..speedTable[1], cst.centerCoords[1]+cst.Speed1Loc[1],cst.centerCoords[2]+cst.Speed1Loc[2],cst.Speed1Loc[3],cst.Speed1Loc[4], 0.0, 255, 255, 255, curAlpha) 172 | DrawSprite(cst.ytdName, 'speed_digits_'..speedTable[2], cst.centerCoords[1]+cst.Speed2Loc[1],cst.centerCoords[2]+cst.Speed2Loc[2],cst.Speed2Loc[3],cst.Speed2Loc[4], 0.0, 255, 255, 255, curAlpha) 173 | DrawSprite(cst.ytdName, 'speed_digits_'..speedTable[3], cst.centerCoords[1]+cst.Speed3Loc[1],cst.centerCoords[2]+cst.Speed3Loc[2],cst.Speed3Loc[3],cst.Speed3Loc[4], 0.0, 255, 255, 255, curAlpha) 174 | elseif string.len(speed) >= 4 then 175 | DrawSprite(cst.ytdName, 'speed_digits_9', cst.centerCoords[1]+cst.Speed3Loc[1],cst.centerCoords[2]+cst.Speed3Loc[2],cst.Speed3Loc[3],cst.Speed3Loc[4], 0.0, 255, 255, 255, curAlpha) 176 | DrawSprite(cst.ytdName, 'speed_digits_9', cst.centerCoords[1]+cst.Speed2Loc[1],cst.centerCoords[2]+cst.Speed2Loc[2],cst.Speed2Loc[3],cst.Speed2Loc[4], 0.0, 255, 255, 255, curAlpha) 177 | DrawSprite(cst.ytdName, 'speed_digits_9', cst.centerCoords[1]+cst.Speed1Loc[1],cst.centerCoords[2]+cst.Speed1Loc[2],cst.Speed1Loc[3],cst.Speed1Loc[4], 0.0, 255, 255, 255, curAlpha) 178 | end 179 | 180 | if GetPedInVehicleSeat(vehicle, -1) == playerPed and vehicleClass >= 0 and vehicleClass < 13 or vehicleClass >= 17 then 181 | if angle(vehicle) >= 10 and angle(vehicle) <= 18 then 182 | driftSprite = 'drift_blue' 183 | DrawSprite(cst.ytdName, driftSprite, cst.centerCoords[1]+cst.FuelBGLoc[1],cst.centerCoords[2]+cst.FuelBGLoc[2],cst.FuelBGLoc[3],cst.FuelBGLoc[4], 0.0, 255, 255, 255, curDriftAlpha) 184 | BlinkDriftText(false) 185 | elseif angle(vehicle) > 18 then 186 | driftSprite = 'drift_yellow' 187 | DrawSprite(cst.ytdName, driftSprite, cst.centerCoords[1]+cst.FuelBGLoc[1],cst.centerCoords[2]+cst.FuelBGLoc[2],cst.FuelBGLoc[3],cst.FuelBGLoc[4], 0.0, 255, 255, 255, curDriftAlpha) 188 | BlinkDriftText(false) 189 | elseif angle(vehicle) < 10 then 190 | driftSprite = 'drift_blue' 191 | DrawSprite(cst.ytdName, driftSprite, cst.centerCoords[1]+cst.FuelBGLoc[1],cst.centerCoords[2]+cst.FuelBGLoc[2],cst.FuelBGLoc[3],cst.FuelBGLoc[4], 0.0, 255, 255, 255, curDriftAlpha) 192 | BlinkDriftText(true) 193 | end 194 | else 195 | curDriftAlpha = 0 196 | end 197 | end 198 | else 199 | Citizen.Wait(500) 200 | end 201 | end 202 | end) -------------------------------------------------------------------------------- /skins/id6.lua: -------------------------------------------------------------------------------- 1 | local skinData = { 2 | skinName = 'id6', 3 | ytdName = 'id6', 4 | 5 | centerCoords = {0.8,0.8}, 6 | 7 | lightsLoc = {0.015,0.12,0.018,0.02}, 8 | blinkerLoc = {0.04,0.12,0.022,0.03}, 9 | fuelLoc = {-0.005,0.12,0.012,0.025}, 10 | oilLoc = {0.100,0.12,0.020,0.025}, 11 | engineLoc = {0.130,0.12,0.020,0.025}, 12 | 13 | SpeedoBGLoc = {0.053, 0.020, 0.25,0.23}, 14 | SpeedoNeedleLoc = {0.000,5,0.076,0.15}, 15 | 16 | TachoBGloc = {0.110,0.004,0.125,0.17}, 17 | TachoNeedleLoc = {0.110,0.030,0.09,0.17}, 18 | 19 | FuelBGLoc = {-0.035, -0.030,0.050, 0.040}, 20 | FuelGaugeLoc = {0.060,0.000,0.030,0.080}, 21 | 22 | GearLoc = {0.010,-0.033,0.025,0.055}, -- gear location 23 | Speed1Loc = {-0.024,0.042,0.025,0.06}, -- 3rd digit 24 | Speed2Loc = {-0.004,0.042,0.025,0.06}, -- 2nd digit 25 | Speed3Loc = {0.020,0.042,0.025,0.06}, -- 1st digit 26 | UnitLoc = {0.029,0.088,0.025,0.025}, 27 | TurboBGLoc = {0.053, -0.130, 0.075,0.090}, 28 | TurboGaugeLoc = {0.0533, -0.125, 0.045,0.060}, 29 | 30 | RotMult = 2.036936, 31 | RotStep = 2.32833, 32 | 33 | rpmScale = 250 34 | } 35 | 36 | addSkin(skinData) 37 | 38 | -- addon code 39 | local labelType = '8k' 40 | local curDriftAlpha = 0 41 | 42 | function angle(vehicle) 43 | if not vehicle then return false end 44 | local vx,vy,vz = table.unpack(GetEntityVelocity(vehicle)) 45 | local modV = math.sqrt(vx*vx + vy*vy) 46 | 47 | local rx,ry,rz = table.unpack(GetEntityRotation(vehicle,0)) 48 | local sn,cs = -math.sin(math.rad(rz)), math.cos(math.rad(rz)) 49 | 50 | if GetEntitySpeed(vehicle)* 3.6 < 40 or GetVehicleCurrentGear(vehicle) == 0 then 51 | return 0, modV --speed over 25 km/h 52 | end 53 | 54 | local cosX = (sn*vx + cs*vy)/modV 55 | 56 | return math.deg(math.acos(cosX))*0.5, modV 57 | end 58 | 59 | local function BlinkDriftText(hide) 60 | if overwriteAlpha then curDriftAlpha = 0 return end 61 | 62 | if hide or goDown then 63 | curDriftAlpha = curDriftAlpha - 15 64 | elseif not hide or not goDown then 65 | curDriftAlpha = curDriftAlpha + 15 66 | end 67 | 68 | if curDriftAlpha <= 0 then 69 | curDriftAlpha = 0 70 | goDown = false 71 | elseif curDriftAlpha >= 255 then 72 | curDriftAlpha = 255 73 | if driftSprite ~= 'drift_yellow' then 74 | goDown = true 75 | end 76 | end 77 | end 78 | 79 | SpeedChimeActive = false 80 | Citizen.CreateThread(function() 81 | while true do 82 | 83 | Citizen.Wait(0) 84 | 85 | if getCurrentSkin() == skinData.skinName then 86 | speedTable = {} 87 | toggleFuelGauge(false) 88 | 89 | local playerPed = PlayerPedId() 90 | local vehicle = GetVehiclePedIsUsing(playerPed) 91 | local vehicleClass = GetVehicleClass(vehicle) 92 | 93 | if DoesEntityExist(vehicle) and not IsEntityDead(vehicle) then 94 | if vehicleClass >= 0 and vehicleClass <= 5 then 95 | labelType = '8k' 96 | skinData.rpmScale = 200 97 | elseif vehicleClass == 6 then 98 | labelType = '9k' 99 | skinData.rpmScale = 222 100 | elseif vehicleClass == 7 then 101 | labelType = '10k' 102 | skinData.rpmScale = 222 103 | elseif vehicleClass == 8 then 104 | labelType = '13k' 105 | skinData.rpmScale = 220 106 | end 107 | 108 | for i,name in ipairs(Config.SpecialCars) do 109 | if GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)) == name then 110 | labelType = '86' 111 | cst.rpmScale = 242 112 | if not SpeedChimeActive and GetEntitySpeed(vehicle) * 3.6 > 105.0 then 113 | SpeedChimeActive = true 114 | TriggerEvent('fivem-speedometer:playSound', 'initiald', 0.7, true) 115 | elseif SpeedChimeActive and GetEntitySpeed(vehicle) * 3.6 < 105.0 then 116 | SpeedChimeActive = false 117 | TriggerEvent('fivem-speedometer:stopSound') 118 | end 119 | 120 | break 121 | end 122 | end 123 | 124 | local _,lightsOn,highbeamsOn = GetVehicleLightsState(vehicle) 125 | 126 | if lightsOn == 1 or highbeamsOn == 1 then 127 | curTachometer = 'night_labels_'..labelType 128 | if useKMH then 129 | curTurbo = 'turbo' 130 | else 131 | curTurbo = 'turbo_psi' 132 | end 133 | curTurboNeedle = 'needle' 134 | else 135 | curTachometer = 'labels_' .. labelType 136 | if useKMH then 137 | curTurbo = 'turbo_day' 138 | else 139 | curTurbo = 'turbo_day_psi' 140 | end 141 | curTurboNeedle = 'needle_day' 142 | end 143 | 144 | local curSpeedometer = 'nodrift_background' 145 | local gear = GetVehicleCurrentGear(vehicle) + 1 146 | local speed = GetEntitySpeed(vehicle) 147 | 148 | if not gear then gear = 1 end 149 | if gear == 1 then gear = 0 end 150 | 151 | 152 | DrawSprite(skinData.ytdName, curSpeedometer, skinData.centerCoords[1]+skinData.SpeedoBGLoc[1],skinData.centerCoords[2]+skinData.SpeedoBGLoc[2],skinData.SpeedoBGLoc[3],skinData.SpeedoBGLoc[4], 0.0, 255, 255, 255, curAlpha) 153 | DrawSprite(skinData.ytdName, curTachometer, skinData.centerCoords[1]+skinData.TachoBGloc[1],skinData.centerCoords[2]+skinData.TachoBGloc[2],skinData.TachoBGloc[3],skinData.TachoBGloc[4], 0.0, 255, 255, 255, curAlpha) 154 | DrawSprite(skinData.ytdName, 'gear_'..gear, skinData.centerCoords[1]+skinData.GearLoc[1],skinData.centerCoords[2]+skinData.GearLoc[2],skinData.GearLoc[3],skinData.GearLoc[4], 0.0, 255, 255, 255, curAlpha) 155 | 156 | if useKMH then 157 | speed = speed * 3.6 158 | DrawSprite(skinData.ytdName, 'kmh', skinData.centerCoords[1]+skinData.UnitLoc[1],skinData.centerCoords[2]+skinData.UnitLoc[2],skinData.UnitLoc[3],skinData.UnitLoc[4], 0.0, 255, 255, 255, curAlpha) 159 | else 160 | speed = speed * 2.236936 161 | DrawSprite(skinData.ytdName, 'mph', skinData.centerCoords[1]+skinData.UnitLoc[1],skinData.centerCoords[2]+skinData.UnitLoc[2],skinData.UnitLoc[3],skinData.UnitLoc[4], 0.0, 255, 255, 255, curAlpha) 162 | end 163 | 164 | if not speed then speed = '0.0' end 165 | speed = tonumber(string.format('%.' .. (0) .. 'f', speed)) 166 | speed = tostring(speed) 167 | for i = 1, string.len(speed) do 168 | speedTable[i] = speed:sub(i, i) 169 | end 170 | 171 | if string.len(speed) == 1 then 172 | DrawSprite(skinData.ytdName, 'speed_digits_'..speedTable[1], skinData.centerCoords[1]+skinData.Speed3Loc[1],skinData.centerCoords[2]+skinData.Speed3Loc[2],skinData.Speed3Loc[3],skinData.Speed3Loc[4], 0.0, 255, 255, 255, curAlpha) 173 | elseif string.len(speed) == 2 then 174 | DrawSprite(skinData.ytdName, 'speed_digits_'..speedTable[1], skinData.centerCoords[1]+skinData.Speed2Loc[1],skinData.centerCoords[2]+skinData.Speed2Loc[2],skinData.Speed2Loc[3],skinData.Speed2Loc[4], 0.0, 255, 255, 255, curAlpha) 175 | DrawSprite(skinData.ytdName, 'speed_digits_'..speedTable[2], skinData.centerCoords[1]+skinData.Speed3Loc[1],skinData.centerCoords[2]+skinData.Speed3Loc[2],skinData.Speed3Loc[3],skinData.Speed3Loc[4], 0.0, 255, 255, 255, curAlpha) 176 | elseif string.len(speed) == 3 then 177 | DrawSprite(skinData.ytdName, 'speed_digits_'..speedTable[1], skinData.centerCoords[1]+skinData.Speed1Loc[1],skinData.centerCoords[2]+skinData.Speed1Loc[2],skinData.Speed1Loc[3],skinData.Speed1Loc[4], 0.0, 255, 255, 255, curAlpha) 178 | DrawSprite(skinData.ytdName, 'speed_digits_'..speedTable[2], skinData.centerCoords[1]+skinData.Speed2Loc[1],skinData.centerCoords[2]+skinData.Speed2Loc[2],skinData.Speed2Loc[3],skinData.Speed2Loc[4], 0.0, 255, 255, 255, curAlpha) 179 | DrawSprite(skinData.ytdName, 'speed_digits_'..speedTable[3], skinData.centerCoords[1]+skinData.Speed3Loc[1],skinData.centerCoords[2]+skinData.Speed3Loc[2],skinData.Speed3Loc[3],skinData.Speed3Loc[4], 0.0, 255, 255, 255, curAlpha) 180 | elseif string.len(speed) >= 4 then 181 | DrawSprite(skinData.ytdName, 'speed_digits_9', skinData.centerCoords[1]+skinData.Speed3Loc[1],skinData.centerCoords[2]+skinData.Speed3Loc[2],skinData.Speed3Loc[3],skinData.Speed3Loc[4], 0.0, 255, 255, 255, curAlpha) 182 | DrawSprite(skinData.ytdName, 'speed_digits_9', skinData.centerCoords[1]+skinData.Speed2Loc[1],skinData.centerCoords[2]+skinData.Speed2Loc[2],skinData.Speed2Loc[3],skinData.Speed2Loc[4], 0.0, 255, 255, 255, curAlpha) 183 | DrawSprite(skinData.ytdName, 'speed_digits_9', skinData.centerCoords[1]+skinData.Speed1Loc[1],skinData.centerCoords[2]+skinData.Speed1Loc[2],skinData.Speed1Loc[3],skinData.Speed1Loc[4], 0.0, 255, 255, 255, curAlpha) 184 | end 185 | 186 | if IsToggleModOn(vehicle, 18) then -- turbo 187 | DrawSprite(skinData.ytdName, curTurbo, skinData.centerCoords[1]+skinData.TurboBGLoc[1],skinData.centerCoords[2]+skinData.TurboBGLoc[2],skinData.TurboBGLoc[3],skinData.TurboBGLoc[4], 0.0, 255, 255, 255, curAlpha) 188 | DrawSprite(skinData.ytdName, curTurboNeedle, skinData.centerCoords[1]+skinData.TurboGaugeLoc[1],skinData.centerCoords[2]+skinData.TurboGaugeLoc[2],skinData.TurboGaugeLoc[3],skinData.TurboGaugeLoc[4], (GetVehicleTurboPressure(vehicle)*100)-625, 255, 255, 255, curAlpha) 189 | end 190 | 191 | if GetPedInVehicleSeat(vehicle, -1) == playerPed and vehicleClass >= 0 and vehicleClass < 13 or vehicleClass >= 17 then 192 | if angle(vehicle) >= 10 and angle(vehicle) <= 18 then 193 | driftSprite = 'drift_blue' 194 | DrawSprite(skinData.ytdName, driftSprite, skinData.centerCoords[1]+skinData.FuelBGLoc[1],skinData.centerCoords[2]+skinData.FuelBGLoc[2],skinData.FuelBGLoc[3],skinData.FuelBGLoc[4], 0.0, 255, 255, 255, curDriftAlpha) 195 | BlinkDriftText(false) 196 | elseif angle(vehicle) > 18 then 197 | driftSprite = 'drift_yellow' 198 | DrawSprite(skinData.ytdName, driftSprite, skinData.centerCoords[1]+skinData.FuelBGLoc[1],skinData.centerCoords[2]+skinData.FuelBGLoc[2],skinData.FuelBGLoc[3],skinData.FuelBGLoc[4], 0.0, 255, 255, 255, curDriftAlpha) 199 | BlinkDriftText(false) 200 | elseif angle(vehicle) < 10 then 201 | driftSprite = 'drift_blue' 202 | DrawSprite(skinData.ytdName, driftSprite, skinData.centerCoords[1]+skinData.FuelBGLoc[1],skinData.centerCoords[2]+skinData.FuelBGLoc[2],skinData.FuelBGLoc[3],skinData.FuelBGLoc[4], 0.0, 255, 255, 255, curDriftAlpha) 203 | BlinkDriftText(true) 204 | end 205 | else 206 | curDriftAlpha = 0 207 | end 208 | 209 | end 210 | else 211 | Citizen.Wait(500) 212 | end 213 | end 214 | end) -------------------------------------------------------------------------------- /client.lua: -------------------------------------------------------------------------------- 1 | showFuelGauge = true -- use fuel gauge? 2 | overwriteChecks = false -- debug value to display all icons 3 | useKMH = (GetProfileSetting(227) == 1) 4 | skins = {} 5 | 6 | function addSkin(skin) 7 | table.insert(skins, skin) 8 | end 9 | 10 | function getAvailableSkins() 11 | local skinList = {} 12 | 13 | for i,theSkin in pairs(skins) do 14 | table.insert(skinList, theSkin.skinName) 15 | end 16 | 17 | return skinList 18 | end 19 | 20 | function toggleFuelGauge(toggle) 21 | showFuelGauge = toggle 22 | end 23 | 24 | overwriteAlpha = false 25 | function changeSkin(skin) 26 | for i,theSkin in pairs(skins) do 27 | if theSkin.skinName == skin then 28 | cst = theSkin 29 | currentSkin = theSkin.skinName 30 | SetResourceKvp('fivem-speedometer:skin', skin) 31 | showFuelGauge = true 32 | overwriteAlpha = false 33 | 34 | return true 35 | end 36 | end 37 | 38 | return false 39 | end 40 | 41 | function DoesSkinExist(skinName) 42 | for i,theSkin in pairs(getAvailableSkins()) do 43 | if theSkin == skinName then 44 | return true 45 | end 46 | end 47 | 48 | return false 49 | end 50 | 51 | function getCurrentSkin() 52 | return currentSkin 53 | end 54 | 55 | function toggleSpeedo(state) 56 | if state == true then 57 | overwriteAlpha = false 58 | elseif state == false then 59 | overwriteAlpha = true 60 | else 61 | overwriteAlpha = not overwriteAlpha 62 | end 63 | end 64 | 65 | Citizen.CreateThread(function() 66 | currentSkin = GetResourceKvpString('fivem-speedometer:skin') 67 | if not currentSkin then 68 | currentSkin = Config.DefaultSkin 69 | SetResourceKvp('fivem-speedometer:skin', Config.DefaultSkin) 70 | end 71 | 72 | if currentSkin == 'default' then 73 | if DoesSkinExist('default') then 74 | currentSkin = 'default' 75 | changeSkin('default') 76 | else 77 | currentSkin = skins[1].skinName 78 | changeSkin(skins[1].skinName) 79 | end 80 | else 81 | for i,theSkin in pairs(skins) do 82 | if theSkin.skinName == currentSkin then 83 | cst = theSkin 84 | break 85 | end 86 | end 87 | if not cst then changeSkin(skins[1].skinName) end 88 | end 89 | end) 90 | 91 | curNeedle, curTachometer, curSpeedometer, curFuelGauge, curAlpha = 'needle_day', 'tachometer_day', 'speedometer_day', 'fuelgauge_day', 0 92 | RPM, degree, blinkertick, showBlinker = 0, 0, 0, false 93 | 94 | Citizen.CreateThread(function() 95 | while true do 96 | Citizen.Wait(0) 97 | local playerPed = PlayerPedId() 98 | local vehicle = GetVehiclePedIsUsing(playerPed) 99 | if overwriteAlpha then curAlpha = 0 end 100 | 101 | -- fade away 102 | if not overwriteAlpha then 103 | if IsPedInAnyVehicle(playerPed, true) and GetSeatPedIsTryingToEnter(playerPed) == -1 or GetPedInVehicleSeat(vehicle, -1) == playerPed then 104 | if curAlpha >= 255 then 105 | curAlpha = 255 106 | else 107 | curAlpha = curAlpha + 5 108 | end 109 | elseif not IsPedInAnyVehicle(playerPed, false) then 110 | if curAlpha <= 0 then 111 | curAlpha = 0 112 | Citizen.Wait(500) 113 | else 114 | curAlpha = curAlpha - 5 115 | end 116 | end 117 | end 118 | 119 | if not HasStreamedTextureDictLoaded(cst.ytdName) then 120 | RequestStreamedTextureDict(cst.ytdName, true) 121 | while not HasStreamedTextureDictLoaded(cst.ytdName) do 122 | Citizen.Wait(1) 123 | end 124 | else 125 | if DoesEntityExist(vehicle) and not IsEntityDead(vehicle) then 126 | degree, step = 0, cst.RotStep 127 | RPM = GetVehicleCurrentRpm(vehicle) 128 | if not GetIsVehicleEngineRunning(vehicle) then RPM = 0 end 129 | if RPM > 0.99 then 130 | RPM = RPM * 100 131 | RPM = RPM + math.random(-2, 2) 132 | RPM = RPM / 100 133 | end 134 | 135 | blinkerState = GetVehicleIndicatorLights(vehicle) 136 | if blinkerState == 0 then 137 | blinkerLeft,blinkerRight = false,false 138 | elseif blinkerState == 1 then 139 | blinkerLeft,blinkerRight = true,false 140 | elseif blinkerState == 2 then 141 | blinkerLeft,blinkerRight = false,true 142 | elseif blinkerState == 3 then 143 | blinkerLeft,blinkerRight = true,true 144 | end 145 | 146 | engineHealth = GetVehicleEngineHealth(vehicle) 147 | if engineHealth <= 350 and engineHealth > 100 then 148 | showDamageYellow,showDamageRed = true,false 149 | elseif engineHealth <= 100 then 150 | showDamageYellow,showDamageRed = false, true 151 | else 152 | showDamageYellow,showDamageRed = false, false 153 | end 154 | 155 | OilLevel = GetVehicleOilLevel(vehicle) 156 | FuelLevel = GetVehicleFuelLevel(vehicle) 157 | MaxFuelLevel = Citizen.InvokeNative(0x642FC12F, vehicle, 'CHandlingData', 'fPetrolTankVolume', Citizen.ReturnResultAnyway(), Citizen.ResultAsFloat()) 158 | if FuelLevel <= MaxFuelLevel*0.25 and FuelLevel > MaxFuelLevel*0.13 then 159 | showLowFuelYellow,showLowFuelRed = true,false 160 | elseif FuelLevel <= MaxFuelLevel*0.2 then 161 | showLowFuelYellow,showLowFuelRed = false,true 162 | else 163 | showLowFuelYellow,showLowFuelRed = false,false 164 | end 165 | if OilLevel <= 0.5 then 166 | showLowOil = true 167 | else 168 | showLowOil = false 169 | end 170 | 171 | local _,lightsOn,highbeamsOn = GetVehicleLightsState(vehicle) 172 | if lightsOn == 1 or highbeamsOn == 1 then 173 | curNeedle, curTachometer, curSpeedometer, curFuelGauge = 'needle', 'tachometer', 'speedometer', 'fuelgauge' 174 | if highbeamsOn == 1 then 175 | showHighBeams,showLowBeams = true,false 176 | elseif lightsOn == 1 and highbeamsOn == 0 then 177 | showHighBeams,showLowBeams = false,true 178 | end 179 | else 180 | curNeedle, curTachometer, curSpeedometer, curFuelGauge, showHighBeams, showLowBeams = 'needle_day', 'tachometer_day', 'speedometer_day', 'fuelgauge_day', false, false 181 | end 182 | 183 | if GetEntitySpeed(vehicle) > 0 then degree=(GetEntitySpeed(vehicle)*2.036936)*step end 184 | if degree > 290 then degree=290 end 185 | if GetVehicleClass(vehicle) >= 0 and GetVehicleClass(vehicle) < 13 or GetVehicleClass(vehicle) >= 17 then 186 | else 187 | curAlpha = 0 188 | end 189 | else 190 | RPM, degree = 0, 0 191 | end 192 | 193 | if RPM < 0.12 or not RPM then 194 | RPM = 0.12 195 | end 196 | 197 | if overwriteChecks then 198 | showHighBeams, showLowBeams, showBlinker, blinkerLeft, blinkerRight, showDamageRed, showLowFuelRed, showLowOil = true, true, true, true, true, true, true, true 199 | end 200 | if showHighBeams then 201 | DrawSprite(cst.ytdName, cst.BeamLight or 'lights', cst.centerCoords[1]+cst.lightsLoc[1],cst.centerCoords[2]+cst.lightsLoc[2],cst.lightsLoc[3],cst.lightsLoc[4],0, 0, 50, 240, curAlpha) 202 | elseif showLowBeams then 203 | DrawSprite(cst.ytdName, cst.BeamLight or 'lights', cst.centerCoords[1]+cst.lightsLoc[1],cst.centerCoords[2]+cst.lightsLoc[2],cst.lightsLoc[3],cst.lightsLoc[4],0, 0, 255, 0, curAlpha) 204 | end 205 | if blinkerLeft and showBlinker then 206 | DrawSprite(cst.ytdName, cst.BlinkerLight or 'blinker', cst.centerCoords[1]+cst.blinkerLoc[1],cst.centerCoords[2]+cst.blinkerLoc[2],cst.blinkerLoc[3],cst.blinkerLoc[4],180.0, 124,252,0, curAlpha) 207 | end 208 | if blinkerRight and showBlinker then 209 | DrawSprite(cst.ytdName, cst.BlinkerLight or 'blinker', cst.centerCoords[1]+cst.blinkerLoc[1]+0.03,cst.centerCoords[2]+cst.blinkerLoc[2]-0.001,cst.blinkerLoc[3],cst.blinkerLoc[4],0.0, 124,252,0, curAlpha) 210 | end 211 | if MaxFuelLevel ~= 0 then 212 | if showLowFuelYellow then 213 | DrawSprite(cst.ytdName, cst.FuelLight or 'fuel', cst.centerCoords[1]+cst.fuelLoc[1],cst.centerCoords[2]+cst.fuelLoc[2],cst.fuelLoc[3],cst.fuelLoc[4],0, 255, 191, 0, curAlpha) 214 | elseif showLowFuelRed then 215 | DrawSprite(cst.ytdName, cst.FuelLight or 'fuel', cst.centerCoords[1]+cst.fuelLoc[1],cst.centerCoords[2]+cst.fuelLoc[2],cst.fuelLoc[3],cst.fuelLoc[4],0, 255, 0, 0, curAlpha) 216 | end 217 | if showLowOil then 218 | DrawSprite(cst.ytdName, cst.OilLight or 'oil', cst.centerCoords[1]+cst.oilLoc[1],cst.centerCoords[2]+cst.oilLoc[2],cst.oilLoc[3],cst.oilLoc[4],0, 255, 0, 0, curAlpha) 219 | end -- MAKE SURE TO DRAW THIS BEFORE THE TACHO NEEDLE, OTHERWISE OVERLAPPING WILL HAPPEN! 220 | end 221 | if showDamageYellow then 222 | DrawSprite(cst.ytdName, cst.EngineLight or 'engine', cst.centerCoords[1]+cst.engineLoc[1],cst.centerCoords[2]+cst.engineLoc[2],cst.engineLoc[3],cst.engineLoc[4],0, 255, 191, 0, curAlpha) 223 | elseif showDamageRed then 224 | DrawSprite(cst.ytdName, cst.EngineLight or 'engine', cst.centerCoords[1]+cst.engineLoc[1],cst.centerCoords[2]+cst.engineLoc[2],cst.engineLoc[3],cst.engineLoc[4],0, 255, 0, 0, curAlpha) 225 | end 226 | DrawSprite(cst.ytdName, cst.SpeedometerBG or curSpeedometer, cst.centerCoords[1]+cst.SpeedoBGLoc[1],cst.centerCoords[2]+cst.SpeedoBGLoc[2],cst.SpeedoBGLoc[3],cst.SpeedoBGLoc[4], 0.0, 255, 255, 255, curAlpha) 227 | if MaxFuelLevel ~= 0 then 228 | DrawSprite(cst.ytdName, cst.TachometerBG or curTachometer, cst.centerCoords[1]+cst.TachoBGloc[1],cst.centerCoords[2]+cst.TachoBGloc[2],cst.TachoBGloc[3],cst.TachoBGloc[4], 0.0, 255, 255, 255, curAlpha) 229 | DrawSprite(cst.ytdName, cst.Needle or curNeedle, cst.centerCoords[1]+cst.TachoNeedleLoc[1],cst.centerCoords[2]+cst.TachoNeedleLoc[2],cst.TachoNeedleLoc[3],cst.TachoNeedleLoc[4],RPM*(cst.rpmScale)-(cst.rpmScaleDecrease or 0), 255, 255, 255, curAlpha) 230 | end 231 | DrawSprite(cst.ytdName, curNeedle, cst.centerCoords[1]+cst.SpeedoNeedleLoc[1],cst.centerCoords[2]+cst.SpeedoNeedleLoc[2],cst.SpeedoNeedleLoc[3],cst.SpeedoNeedleLoc[4],-5.00001+degree, 255, 255, 255, curAlpha) 232 | if showFuelGauge and FuelLevel and MaxFuelLevel ~= 0 then 233 | DrawSprite(cst.ytdName, cst.FuelGauge or curFuelGauge, cst.centerCoords[1]+cst.FuelBGLoc[1],cst.centerCoords[2]+cst.FuelBGLoc[2],cst.FuelBGLoc[3],cst.FuelBGLoc[4], 0.0, 255,255,255, curAlpha) 234 | DrawSprite(cst.ytdName, cst.FuelNeedle or curNeedle, cst.centerCoords[1]+cst.FuelGaugeLoc[1],cst.centerCoords[2]+cst.FuelGaugeLoc[2],cst.FuelGaugeLoc[3],cst.FuelGaugeLoc[4],80.0+FuelLevel/MaxFuelLevel*110, 255, 255, 255, curAlpha) 235 | end 236 | end 237 | end 238 | 239 | end) 240 | 241 | Citizen.CreateThread(function() 242 | while true do 243 | Citizen.Wait(0) 244 | 245 | if blinkerLeft or blinkerRight then 246 | showBlinker = true 247 | Citizen.Wait(500) 248 | showBlinker = false 249 | Citizen.Wait(500) 250 | else 251 | Citizen.Wait(1000) 252 | end 253 | end 254 | end) 255 | 256 | -- Keep updating the selected measurement unit 257 | Citizen.CreateThread(function() 258 | local fakeTimer = 0 259 | 260 | while true do 261 | Citizen.Wait(1000) 262 | fakeTimer = fakeTimer + 1 263 | 264 | if fakeTimer == 3 then 265 | useKMH = (GetProfileSetting(227) == 1) 266 | fakeTimer = 0 267 | end 268 | end 269 | end) 270 | 271 | RegisterNetEvent('fivem-speedometer:playSound') 272 | AddEventHandler('fivem-speedometer:playSound', function(soundFile, soundVolume, loop) 273 | if not Config.PlaySpeedChime then return end 274 | 275 | SendNUIMessage({ 276 | action = 'playSound', 277 | file = soundFile, 278 | volume = soundVolume, 279 | loop = loop 280 | }) 281 | end) 282 | 283 | RegisterNetEvent('fivem-speedometer:stopSound') 284 | AddEventHandler('fivem-speedometer:stopSound', function() 285 | if not Config.PlaySpeedChime then return end 286 | 287 | SendNUIMessage({ 288 | action = 'stopSound' 289 | }) 290 | end) 291 | 292 | Citizen.CreateThread(function() 293 | TriggerEvent('chat:addSuggestion', '/speedoskin', 'change the speedometer skin', { {name='skin', help='the skin name'} } ) 294 | TriggerEvent('chat:addSuggestion', '/speedoskins', 'show all available speedometer skins') 295 | TriggerEvent('chat:addSuggestion', '/speedotoggle', 'toggle the speedometer') 296 | end) 297 | 298 | AddEventHandler('onResourceStop', function(resource) 299 | if resource == GetCurrentResourceName() then 300 | TriggerEvent('chat:removeSuggestion', '/speedoskin') 301 | TriggerEvent('chat:removeSuggestion', '/speedoskins') 302 | TriggerEvent('chat:removeSuggestion', '/speedotoggle') 303 | end 304 | end) 305 | 306 | RegisterCommand('speedoskin', function(source, args, rawCommand) 307 | if args[1] and DoesSkinExist(args[1]) then 308 | changeSkin(args[1]) 309 | else 310 | TriggerEvent('chat:addMessage', { args = { '^3ERROR', 'unknown skin' } }) 311 | end 312 | end, false) 313 | 314 | RegisterCommand('speedoskins', function(source, args, rawCommand) 315 | local skins = getAvailableSkins() 316 | local first, message = true, '' 317 | 318 | for i,skin in pairs(skins) do 319 | if first then 320 | message = skin 321 | first = false 322 | else 323 | message = message .. ', ' .. skin 324 | end 325 | end 326 | 327 | TriggerEvent('chat:addMessage', { args = { '^2Available skins', message } }) 328 | end, false) 329 | 330 | RegisterCommand('speedotoggle', function(source, args, rawCommand) 331 | toggleSpeedo() 332 | end, false) --------------------------------------------------------------------------------