├── .github └── FUNDING.yml ├── Screenshots ├── Speed.png ├── Gearbox.png ├── Wheels.png ├── PadGamepad.png └── PadKeyboard.png ├── .gitignore ├── info.toml ├── Source ├── ForeverLastInputHook.as ├── Thing.as ├── Main.as ├── SettingsGradient.as ├── Dashboard.as ├── Things │ ├── Clock.as │ ├── PadHost.as │ ├── Speed.as │ ├── Acceleration.as │ ├── Gearbox.as │ └── Wheels.as ├── Pads │ ├── Keyboard.as │ └── Gamepad.as ├── SettingsWidgets.as └── Settings.as ├── Readme.md └── License.txt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: [openplanet] 2 | -------------------------------------------------------------------------------- /Screenshots/Speed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codecat/tm-dashboard/HEAD/Screenshots/Speed.png -------------------------------------------------------------------------------- /Screenshots/Gearbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codecat/tm-dashboard/HEAD/Screenshots/Gearbox.png -------------------------------------------------------------------------------- /Screenshots/Wheels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codecat/tm-dashboard/HEAD/Screenshots/Wheels.png -------------------------------------------------------------------------------- /Screenshots/PadGamepad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codecat/tm-dashboard/HEAD/Screenshots/PadGamepad.png -------------------------------------------------------------------------------- /Screenshots/PadKeyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codecat/tm-dashboard/HEAD/Screenshots/PadKeyboard.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Signature files 2 | *.sig 3 | .manifest 4 | 5 | # Releases 6 | *.zip 7 | *.op 8 | 9 | # Sign scripts 10 | Sign.bat 11 | SignComp.bat 12 | RemoveSign.bat 13 | -------------------------------------------------------------------------------- /info.toml: -------------------------------------------------------------------------------- 1 | [meta] 2 | name = "Dashboard" 3 | author = "Miss" 4 | category = "Overlay" 5 | version = "1.9.7" 6 | 7 | blocks = [ "Plugin_Dashboard" ] 8 | 9 | [script] 10 | dependencies = [ "VehicleState" ] 11 | timeout = 0 12 | -------------------------------------------------------------------------------- /Source/ForeverLastInputHook.as: -------------------------------------------------------------------------------- 1 | #if FOREVER 2 | // I couldn't find an easily accessible field in TmForever to find out the last used input device. 3 | // We should get rid of this: it adds a lot of extra overhead, especially with high refresh rate controllers. 4 | // But this will have to do for now until I come up with something better.. :( 5 | namespace ForeverLastInputHook 6 | { 7 | Dev::HookInfo@ g_hook; 8 | CInputDevice@ g_device; 9 | 10 | void Start() 11 | { 12 | IntPtr pMessage = Dev::FindPattern("83 85 ?? ?? ?? ?? ?? 83 7D"); 13 | if (pMessage == 0) { 14 | error("Unable to find last input device hook pattern!"); 15 | return; 16 | } 17 | @g_hook = Dev::Hook(pMessage, 2, "ForeverLastInputHook::InputDeviceMessage"); 18 | } 19 | 20 | void Stop() 21 | { 22 | if (g_hook !is null) { 23 | Dev::Unhook(g_hook); 24 | } 25 | } 26 | 27 | void InputDeviceMessage(CInputDevice@ esi) 28 | { 29 | @ForeverLastInputHook::g_device = esi; 30 | } 31 | } 32 | #endif 33 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Dashboard 2 | Plugin for Openplanet that displays a bunch of Trackmania vehicle information on screen. Nice for streaming overlays! 3 | 4 | ## Features 5 | * Controller visualization 6 | * Gamepad 7 | * Keyboard 8 | * Gearbox visualization 9 | * Wheel info visualization 10 | * Speed visualization 11 | * Acceleration visualization 12 | * Clock widget 13 | * Makes use of `VehicleState` (works in solo, online, spectating online, watching replays) 14 | * Fully customizable with many settings 15 | * Toggle each widget 16 | * Positions & size 17 | * Colors & styles 18 | 19 | ## Installation 20 | You can install the plugin in Openplanet's plugin manager. Additionally, the plugin is available [on Openplanet.dev](https://openplanet.dev/plugin/dashboard) as well. 21 | 22 | ## Screenshots 23 | ![](Screenshots/PadGamepad.png) 24 | 25 | ![](Screenshots/PadKeyboard.png) 26 | 27 | ![](Screenshots/Gearbox.png) 28 | 29 | ![](Screenshots/Speed.png) 30 | 31 | ![](Screenshots/Wheels.png) 32 | -------------------------------------------------------------------------------- /Source/Thing.as: -------------------------------------------------------------------------------- 1 | class DashboardThing 2 | { 3 | string m_name; 4 | 5 | vec2 m_pos; 6 | vec2 m_size; 7 | 8 | DashboardThing(const string &in name) 9 | { 10 | m_name = name; 11 | UpdateProportions(); 12 | } 13 | 14 | void InternalRender(CSceneVehicleVisState@ vis) 15 | { 16 | vec2 pos = m_pos * (Display::GetSize() - m_size); 17 | nvg::Translate(pos.x, pos.y); 18 | Render(vis); 19 | nvg::ResetTransform(); 20 | } 21 | 22 | void OnSettingsChanged() {} 23 | void UpdateAsync() {} 24 | 25 | bool IsVisible(bool whenHidden) { throw("IsVisible is not implemented!"); return false; } 26 | void SetVisible(bool visible, bool visibleWhenHidden) { throw("SetVisible is not implemented!"); } 27 | 28 | void UpdateProportions() { throw("UpdateProportions is not implemented!"); } 29 | void SetProportions(const vec2 &in pos, const vec2 &in size) { throw("SetProportions is not implemented!"); } 30 | void ResetProportions() { throw("ResetProportions is not implemented!"); } 31 | 32 | void Render(CSceneVehicleVisState@ vis) { throw("Render is not implemented!"); } 33 | } 34 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 - 2024 Melissa Geels 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Source/Main.as: -------------------------------------------------------------------------------- 1 | bool g_visible = true; 2 | float g_dt = 0; 3 | Dashboard@ g_dashboard; 4 | 5 | nvg::Font g_font; 6 | nvg::Font g_fontBold; 7 | 8 | void RenderMenu() 9 | { 10 | if (UI::MenuItem("\\$9f3" + Icons::BarChart + "\\$z Dashboard", "", g_visible)) { 11 | g_visible = !g_visible; 12 | } 13 | } 14 | 15 | void Render() 16 | { 17 | if (!g_visible) { 18 | return; 19 | } 20 | 21 | // Render dashboard 22 | if (g_dashboard !is null) { 23 | g_dashboard.Render(); 24 | } 25 | } 26 | 27 | void Update(float dt) 28 | { 29 | // We need to save this for the Accelerometer: 30 | // Specifically to get an accurate acceleration value in meters per second per second. 31 | // Without this dt we can only get a fake 'acceleration' that doesn't incorporate time properly. 32 | g_dt = dt; 33 | } 34 | 35 | void OnSettingsChanged() 36 | { 37 | g_dashboard.OnSettingsChanged(); 38 | } 39 | 40 | void Main() 41 | { 42 | g_font = nvg::LoadFont("DroidSans.ttf"); 43 | g_fontBold = nvg::LoadFont("DroidSans-Bold.ttf"); 44 | 45 | #if FOREVER 46 | ForeverLastInputHook::Start(); 47 | #endif 48 | 49 | @g_dashboard = Dashboard(); 50 | g_dashboard.Main(); 51 | } 52 | 53 | #if FOREVER 54 | void OnDestroyed() { ForeverLastInputHook::Stop(); } 55 | #endif 56 | -------------------------------------------------------------------------------- /Source/SettingsGradient.as: -------------------------------------------------------------------------------- 1 | class SettingsGradient 2 | { 3 | [Setting name="Color 1" color] 4 | vec4 Color1 = vec4(0, 0, 0, 1); 5 | 6 | [Setting name="Color 2" color] 7 | vec4 Color2 = vec4(1, 1, 1, 1); 8 | 9 | [Setting name="Orientation" min=0 max=360] 10 | float Orientation; 11 | 12 | SettingsGradient() {} //TODO: https://www.gamedev.net/forums/topic/717099-script-builder-addon-fails-on-variable-declaration/ 13 | SettingsGradient(const vec4 &in color1, const vec4 &in color2, float orientation = 60) 14 | { 15 | Color1 = color1; 16 | Color2 = color2; 17 | Orientation = orientation; 18 | } 19 | 20 | nvg::Paint GetPaint(const vec2 &in pos, const vec2 &in size, float alpha = 1.0f) 21 | { 22 | float rot = Math::ToRad(Orientation); 23 | vec2 mid = pos + size / 2; 24 | vec2 dir(Math::Cos(rot), Math::Sin(rot)); 25 | float radius = Math::Max(size.x, size.y) / 2; 26 | vec2 start = mid + dir * radius; 27 | vec2 end = mid + (dir * -1) * radius; 28 | 29 | if (alpha < 1.0f) { 30 | vec4 c1 = Color1; 31 | c1.w *= alpha; 32 | 33 | vec4 c2 = Color2; 34 | c2.w *= alpha; 35 | 36 | return nvg::LinearGradient(start, end, c1, c2); 37 | } 38 | 39 | return nvg::LinearGradient(start, end, Color1, Color2); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Source/Dashboard.as: -------------------------------------------------------------------------------- 1 | class Dashboard 2 | { 3 | array m_things; 4 | 5 | Dashboard() 6 | { 7 | m_things.InsertLast(DashboardPadHost()); 8 | m_things.InsertLast(DashboardGearbox()); 9 | m_things.InsertLast(DashboardWheels()); 10 | m_things.InsertLast(DashboardAcceleration()); 11 | m_things.InsertLast(DashboardSpeed()); 12 | m_things.InsertLast(DashboardClock()); 13 | } 14 | 15 | void Main() 16 | { 17 | while (true) { 18 | for (uint i = 0; i < m_things.Length; i++) { 19 | m_things[i].UpdateAsync(); 20 | } 21 | yield(); 22 | } 23 | } 24 | 25 | void OnSettingsChanged() 26 | { 27 | for (uint i = 0; i < m_things.Length; i++) { 28 | m_things[i].OnSettingsChanged(); 29 | } 30 | } 31 | 32 | void Render() 33 | { 34 | auto app = GetApp(); 35 | 36 | if (Setting_General_HideWhenNotPlaying) { 37 | #if FOREVER 38 | //todo 39 | #else 40 | if (app.CurrentPlayground !is null && (app.CurrentPlayground.UIConfigs.Length > 0)) { 41 | if (app.CurrentPlayground.UIConfigs[0].UISequence == CGamePlaygroundUIConfig::EUISequence::Intro) { 42 | return; 43 | } 44 | } 45 | #endif 46 | } 47 | 48 | auto visState = VehicleState::ViewingPlayerState(); 49 | if (visState is null) { 50 | return; 51 | } 52 | 53 | bool gameUIVisible = UI::IsGameUIVisible(); 54 | 55 | for (uint i = 0; i < m_things.Length; i++) { 56 | auto thing = m_things[i]; 57 | if (thing.IsVisible(!gameUIVisible)) { 58 | thing.UpdateProportions(); 59 | thing.InternalRender(visState); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Source/Things/Clock.as: -------------------------------------------------------------------------------- 1 | class DashboardClock : DashboardThing 2 | { 3 | nvg::Font m_font; 4 | string m_fontPath; 5 | 6 | DashboardClock() 7 | { 8 | super("Clock"); 9 | LoadFont(); 10 | } 11 | 12 | bool IsVisible(bool whenHidden) override { return whenHidden ? Setting_General_ShowClockHidden : Setting_General_ShowClock; } 13 | void SetVisible(bool visible, bool visibleWhenHidden) override 14 | { 15 | Setting_General_ShowClock = visible; 16 | Setting_General_ShowClockHidden = visibleWhenHidden; 17 | } 18 | 19 | void UpdateProportions() override 20 | { 21 | m_pos = Setting_General_ClockPos; 22 | m_size = Setting_General_ClockSize; 23 | } 24 | 25 | void SetProportions(const vec2 &in pos, const vec2 &in size) override 26 | { 27 | Setting_General_ClockPos = pos; 28 | Setting_General_ClockSize = size; 29 | } 30 | 31 | void ResetProportions() override 32 | { 33 | auto plugin = Meta::ExecutingPlugin(); 34 | plugin.GetSetting("Setting_General_ClockPos").Reset(); 35 | plugin.GetSetting("Setting_General_ClockSize").Reset(); 36 | } 37 | 38 | void LoadFont() 39 | { 40 | if (Setting_Clock_Font == m_fontPath) { 41 | return; 42 | } 43 | 44 | auto font = nvg::LoadFont(Setting_Clock_Font); 45 | if (font > 0) { 46 | m_fontPath = Setting_Clock_Font; 47 | m_font = font; 48 | } 49 | } 50 | 51 | void OnSettingsChanged() override 52 | { 53 | LoadFont(); 54 | } 55 | 56 | void Render(CSceneVehicleVisState@ vis) override 57 | { 58 | string clockTime; 59 | 60 | switch (Setting_Clock_Mode){ 61 | case ClockMode::LocalTime: clockTime = Time::FormatString(Setting_Clock_Format); break; 62 | case ClockMode::UTCTime: clockTime = Time::FormatStringUTC(Setting_Clock_Format); break; 63 | } 64 | 65 | switch (Setting_Clock_Icon) { 66 | case ClockIcon::Left: clockTime = Icons::ClockO + " " + clockTime; break; 67 | case ClockIcon::Right: clockTime = clockTime + " " + Icons::ClockO; break; 68 | } 69 | 70 | nvg::BeginPath(); 71 | nvg::RoundedRect(0, 0, m_size.x, m_size.y, Setting_Clock_BorderRadius); 72 | 73 | nvg::FillColor(Setting_Clock_BackdropColor); 74 | nvg::Fill(); 75 | 76 | nvg::StrokeColor(Setting_Clock_BorderColor); 77 | nvg::StrokeWidth(Setting_Clock_BorderWidth); 78 | nvg::Stroke(); 79 | 80 | nvg::BeginPath(); 81 | nvg::FontFace(m_font); 82 | nvg::FontSize(Setting_Clock_FontSize); 83 | nvg::FillColor(Setting_Clock_TextColor); 84 | 85 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Center); 86 | nvg::TextBox(0, m_size.y / 2, m_size.x, clockTime); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Source/Things/PadHost.as: -------------------------------------------------------------------------------- 1 | interface IDashboardPad 2 | { 3 | void Render(const vec2 &in size, CSceneVehicleVisState@ vis); 4 | } 5 | 6 | class DashboardPadHost : DashboardThing 7 | { 8 | PadType m_currentPadType = PadType::None; 9 | 10 | IDashboardPad@ m_pad = null; 11 | 12 | DashboardPadHost() 13 | { 14 | super("Controller/pad"); 15 | } 16 | 17 | bool IsVisible(bool whenHidden) override { return whenHidden ? Setting_General_ShowPadHidden : Setting_General_ShowPad; } 18 | void SetVisible(bool visible, bool visibleWhenHidden) override 19 | { 20 | Setting_General_ShowPad = visible; 21 | Setting_General_ShowPadHidden = visibleWhenHidden; 22 | } 23 | 24 | void UpdateProportions() override 25 | { 26 | m_pos = Setting_General_PadPos; 27 | m_size = Setting_General_PadSize; 28 | } 29 | 30 | void SetProportions(const vec2 &in pos, const vec2 &in size) override 31 | { 32 | Setting_General_PadPos = pos; 33 | Setting_General_PadSize = size; 34 | } 35 | 36 | void ResetProportions() override 37 | { 38 | auto plugin = Meta::ExecutingPlugin(); 39 | plugin.GetSetting("Setting_General_PadPos").Reset(); 40 | plugin.GetSetting("Setting_General_PadSize").Reset(); 41 | } 42 | 43 | void Render(CSceneVehicleVisState@ vis) override 44 | { 45 | if (m_pad !is null) { 46 | m_pad.Render(m_size, vis); 47 | } 48 | } 49 | 50 | void UpdateAsync() override 51 | { 52 | // Force a pad type if the settings demand it 53 | if (Setting_General_ForcePadType != PadType::None) { 54 | SetPad(Setting_General_ForcePadType); 55 | return; 56 | } 57 | 58 | #if FOREVER 59 | // Use the most recently used pad from the hook 60 | auto lastDevice = ForeverLastInputHook::g_device; 61 | if (cast(lastDevice) !is null) { 62 | SetPad(PadType::Gamepad); 63 | } else { 64 | SetPad(PadType::Keyboard); 65 | } 66 | #else 67 | // Find the most recently used pad 68 | CInputScriptPad@ mostRecentPad; 69 | 70 | auto inputPort = GetApp().InputPort; 71 | 72 | for (uint i = 0; i < inputPort.Script_Pads.Length; i++) { 73 | auto pad = inputPort.Script_Pads[i]; 74 | if (mostRecentPad is null || pad.IdleDuration < mostRecentPad.IdleDuration) { 75 | @mostRecentPad = pad; 76 | if (pad.IdleDuration == 0) { 77 | break; 78 | } 79 | } 80 | } 81 | 82 | // Clear pad if there is none found 83 | if (mostRecentPad is null) { 84 | ClearPad(); 85 | return; 86 | } 87 | 88 | switch (mostRecentPad.Type) { 89 | case CInputScriptPad::EPadType::Keyboard: 90 | SetPad(PadType::Keyboard); 91 | break; 92 | 93 | case CInputScriptPad::EPadType::XBox: 94 | case CInputScriptPad::EPadType::PlayStation: 95 | SetPad(PadType::Gamepad); 96 | break; 97 | } 98 | #endif 99 | } 100 | 101 | void ClearPad() 102 | { 103 | m_currentPadType = PadType::None; 104 | @m_pad = null; 105 | } 106 | 107 | void SetPad(PadType type) 108 | { 109 | if (m_currentPadType == type) { 110 | return; 111 | } 112 | m_currentPadType = type; 113 | 114 | switch (type) { 115 | case PadType::Keyboard: @m_pad = DashboardPadKeyboard(); break; 116 | case PadType::Gamepad: @m_pad = DashboardPadGamepad(); break; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Source/Pads/Keyboard.as: -------------------------------------------------------------------------------- 1 | class DashboardPadKeyboard : IDashboardPad 2 | { 3 | vec2 m_size; 4 | 5 | void Render(const vec2 &in size, CSceneVehicleVisState@ vis) override 6 | { 7 | m_size = size; 8 | 9 | float steerLeft = vis.InputSteer < 0 ? Math::Abs(vis.InputSteer) : 0.0f; 10 | float steerRight = vis.InputSteer > 0 ? vis.InputSteer : 0.0f; 11 | 12 | vec2 keySize = vec2((size.x - Setting_Keyboard_Spacing * 2) / 3, (size.y - Setting_Keyboard_Spacing) / 2); 13 | vec2 sideKeySize = keySize; 14 | 15 | vec2 upPos = vec2(keySize.x + Setting_Keyboard_Spacing, 0); 16 | vec2 downPos = vec2(keySize.x + Setting_Keyboard_Spacing, keySize.y + Setting_Keyboard_Spacing); 17 | vec2 leftPos = vec2(0, keySize.y + Setting_Keyboard_Spacing); 18 | vec2 rightPos = vec2(keySize.x * 2 + Setting_Keyboard_Spacing * 2, keySize.y + Setting_Keyboard_Spacing); 19 | 20 | if (Setting_Keyboard_Shape == KeyboardShape::Compact) { 21 | sideKeySize.y = size.y; 22 | leftPos.y = 0; 23 | rightPos.y = 0; 24 | } 25 | 26 | RenderKey(upPos, keySize, Icons::AngleUp, vis.InputGasPedal); 27 | RenderKey(downPos, keySize, Icons::AngleDown, vis.InputIsBraking ? 1.0f : vis.InputBrakePedal); 28 | 29 | RenderKey(leftPos, sideKeySize, Icons::AngleLeft, steerLeft, -1); 30 | RenderKey(rightPos, sideKeySize, Icons::AngleRight, steerRight, 1); 31 | } 32 | 33 | void RenderKey(const vec2 &in pos, const vec2 &in size, const string &in text, float value, int fillDir = 0) 34 | { 35 | nvg::BeginPath(); 36 | nvg::StrokeWidth(Setting_Keyboard_BorderWidth); 37 | 38 | switch (Setting_Keyboard_Shape) { 39 | case KeyboardShape::Rectangle: 40 | case KeyboardShape::Compact: 41 | nvg::RoundedRect(pos.x, pos.y, size.x, size.y, Setting_Keyboard_BorderRadius); 42 | break; 43 | case KeyboardShape::Ellipse: 44 | nvg::Ellipse(pos + size / 2, size.x / 2, size.y / 2); 45 | break; 46 | } 47 | 48 | nvg::FillColor(Setting_Keyboard_EmptyFillColor); 49 | nvg::Fill(); 50 | 51 | if (fillDir == 0) { 52 | if (Math::Abs(value) > 0.1f) { 53 | if (Setting_Keyboard_UseFillGradient) { 54 | nvg::FillPaint(Setting_Keyboard_FillGradient.GetPaint(vec2(), m_size)); 55 | } else { 56 | nvg::FillColor(Setting_Keyboard_FillColor); 57 | } 58 | nvg::Fill(); 59 | } 60 | } else if (value > 0) { 61 | if (fillDir == -1) { 62 | float valueWidth = value * size.x; 63 | nvg::Scissor(size.x - valueWidth, pos.y, valueWidth, size.y); 64 | } else if (fillDir == 1) { 65 | float valueWidth = value * size.x; 66 | nvg::Scissor(pos.x, pos.y, valueWidth, size.y); 67 | } 68 | if (Setting_Keyboard_UseFillGradient) { 69 | nvg::FillPaint(Setting_Keyboard_FillGradient.GetPaint(vec2(), m_size)); 70 | } else { 71 | nvg::FillColor(Setting_Keyboard_FillColor); 72 | } 73 | nvg::Fill(); 74 | nvg::ResetScissor(); 75 | } 76 | 77 | float fillAlpha; 78 | if (fillDir == 0) { 79 | fillAlpha = Math::Abs(value) > 0.1f ? 1.0f : Setting_Keyboard_InactiveAlpha; 80 | } else { 81 | fillAlpha = Math::Lerp(Setting_Keyboard_InactiveAlpha, 1.0f, value); 82 | } 83 | 84 | vec4 borderColor = Setting_Keyboard_BorderColor; 85 | borderColor.w *= fillAlpha; 86 | 87 | if (Setting_Keyboard_UseBorderGradient) { 88 | nvg::StrokePaint(Setting_Keyboard_BorderGradient.GetPaint(vec2(), m_size, fillAlpha)); 89 | } else { 90 | nvg::StrokeColor(borderColor); 91 | } 92 | nvg::Stroke(); 93 | 94 | nvg::BeginPath(); 95 | nvg::FontFace(g_font); 96 | nvg::FontSize(size.x / 2); 97 | if (Setting_Keyboard_UseBorderGradient) { 98 | nvg::FillPaint(Setting_Keyboard_BorderGradient.GetPaint(vec2(), m_size, fillAlpha)); 99 | } else { 100 | nvg::FillColor(borderColor); 101 | } 102 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Center); 103 | nvg::TextBox(pos.x, pos.y + size.y / 2, size.x, text); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Source/SettingsWidgets.as: -------------------------------------------------------------------------------- 1 | bool g_settingsMove = false; 2 | UI::Cond g_settingsMoveCond = UI::Cond::Appearing; 3 | 4 | [Setting hidden] 5 | bool Setting_AdvancedWidgetSettings = false; 6 | 7 | [SettingsTab name="Widgets" icon="ArrowsAlt"] 8 | void RenderSettingsWidgets() 9 | { 10 | g_settingsMove = true; 11 | 12 | UI::TextWrapped("While this tab is open, you can drag the widgets around, resize them, and toggle them on and off."); 13 | 14 | Setting_AdvancedWidgetSettings = UI::Checkbox("Advanced visibility settings", Setting_AdvancedWidgetSettings); 15 | 16 | UI::Separator(); 17 | 18 | if (Setting_AdvancedWidgetSettings) { 19 | if (UI::BeginTable("Widgets", 5)) { 20 | UI::TableSetupColumn("Widget", UI::TableColumnFlags::WidthStretch, 0.9f); 21 | UI::TableSetupColumn("Position", UI::TableColumnFlags::WidthFixed, 130); 22 | UI::TableSetupColumn("Size", UI::TableColumnFlags::WidthFixed, 130); 23 | UI::TableSetupColumn("Visible UI", UI::TableColumnFlags::WidthFixed, 80); 24 | UI::TableSetupColumn("Hidden UI", UI::TableColumnFlags::WidthFixed, 80); 25 | UI::TableHeadersRow(); 26 | 27 | for (uint i = 0; i < g_dashboard.m_things.Length; i++) { 28 | auto thing = g_dashboard.m_things[i]; 29 | 30 | UI::PushID(thing); 31 | 32 | UI::TableNextColumn(); 33 | UI::Text(thing.m_name); 34 | 35 | UI::TableNextColumn(); 36 | UI::PushItemWidth(-1); 37 | thing.m_pos = UI::InputFloat2("##Position", thing.m_pos); 38 | UI::PopItemWidth(); 39 | 40 | UI::TableNextColumn(); 41 | UI::PushItemWidth(-1); 42 | thing.m_size = UI::InputFloat2("##Size", thing.m_size); 43 | UI::PopItemWidth(); 44 | 45 | UI::TableNextColumn(); 46 | bool whenVisible = UI::Checkbox("##WhenVisible", thing.IsVisible(false)); 47 | 48 | UI::TableNextColumn(); 49 | bool whenHidden = UI::Checkbox("##WhenHidden", thing.IsVisible(true)); 50 | 51 | thing.SetVisible(whenVisible, whenHidden); 52 | thing.SetProportions(thing.m_pos, thing.m_size); 53 | 54 | UI::PopID(); 55 | } 56 | 57 | UI::EndTable(); 58 | } 59 | 60 | UI::Separator(); 61 | UI::TextWrapped("In the above table, select which widgets you want to display when the game UI is visible, and when the game UI is hidden. Note that this requires the option \"Hide overlay on hidden game UI\" under \"General\" to be disabled!"); 62 | 63 | } else { 64 | for (uint i = 0; i < g_dashboard.m_things.Length; i++) { 65 | auto thing = g_dashboard.m_things[i]; 66 | bool visible = UI::Checkbox(thing.m_name, thing.IsVisible(false)); 67 | thing.SetVisible(visible, visible); 68 | } 69 | } 70 | 71 | UI::Separator(); 72 | if (UI::Button("Reset proportions to defaults")) { 73 | for (uint i = 0; i < g_dashboard.m_things.Length; i++) { 74 | auto thing = g_dashboard.m_things[i]; 75 | thing.ResetProportions(); 76 | thing.UpdateProportions(); 77 | } 78 | 79 | // This is required to force the locator windows to reset on the next frame, rather than use the same positions from the last frame 80 | g_settingsMoveCond = UI::Cond::Always; 81 | } 82 | } 83 | 84 | void RenderInterface() 85 | { 86 | if (!g_settingsMove) { 87 | return; 88 | } 89 | 90 | float scale = UI::GetScale(); 91 | 92 | for (uint i = 0; i < g_dashboard.m_things.Length; i++) { 93 | auto thing = g_dashboard.m_things[i]; 94 | if (!thing.IsVisible(true) && !thing.IsVisible(false)) { 95 | continue; 96 | } 97 | 98 | thing.UpdateProportions(); 99 | 100 | vec2 screenSize = Display::GetSize(); 101 | vec2 pos = thing.m_pos * (screenSize - thing.m_size); 102 | 103 | UI::SetNextWindowSize(int(thing.m_size.x / scale), int(thing.m_size.y / scale), g_settingsMoveCond); 104 | UI::SetNextWindowPos(int(pos.x / scale), int(pos.y / scale), g_settingsMoveCond); 105 | 106 | UI::Begin(Icons::ArrowsAlt + " " + thing.m_name, UI::WindowFlags::NoCollapse | UI::WindowFlags::NoSavedSettings); 107 | thing.m_size = UI::GetWindowSize(); 108 | thing.m_pos = UI::GetWindowPos() / (screenSize - thing.m_size); 109 | UI::End(); 110 | 111 | thing.SetProportions(thing.m_pos, thing.m_size); 112 | } 113 | 114 | g_settingsMove = false; 115 | g_settingsMoveCond = UI::Cond::Appearing; 116 | } 117 | -------------------------------------------------------------------------------- /Source/Things/Speed.as: -------------------------------------------------------------------------------- 1 | class DashboardSpeed : DashboardThing 2 | { 3 | nvg::Font m_font; 4 | string m_fontPath; 5 | 6 | DashboardSpeed() 7 | { 8 | super("Speed"); 9 | LoadFont(); 10 | } 11 | 12 | bool IsVisible(bool whenHidden) override { return whenHidden ? Setting_General_ShowSpeedHidden : Setting_General_ShowSpeed; } 13 | void SetVisible(bool visible, bool visibleWhenHidden) override 14 | { 15 | Setting_General_ShowSpeed = visible; 16 | Setting_General_ShowSpeedHidden = visibleWhenHidden; 17 | } 18 | 19 | void UpdateProportions() override 20 | { 21 | m_pos = Setting_General_SpeedPos; 22 | m_size = Setting_General_SpeedSize; 23 | } 24 | 25 | void SetProportions(const vec2 &in pos, const vec2 &in size) override 26 | { 27 | Setting_General_SpeedPos = pos; 28 | Setting_General_SpeedSize = size; 29 | } 30 | 31 | void ResetProportions() override 32 | { 33 | auto plugin = Meta::ExecutingPlugin(); 34 | plugin.GetSetting("Setting_General_SpeedPos").Reset(); 35 | plugin.GetSetting("Setting_General_SpeedSize").Reset(); 36 | } 37 | 38 | void LoadFont() 39 | { 40 | if (Setting_Speed_Font == m_fontPath) { 41 | return; 42 | } 43 | 44 | auto font = nvg::LoadFont(Setting_Speed_Font); 45 | if (font > 0) { 46 | m_fontPath = Setting_Speed_Font; 47 | m_font = font; 48 | } 49 | } 50 | 51 | void OnSettingsChanged() override 52 | { 53 | LoadFont(); 54 | } 55 | 56 | void Render(CSceneVehicleVisState@ vis) override 57 | { 58 | float displaySpeed; 59 | switch (Setting_Speed_Value) { 60 | case SpeedValue::FrontSpeed: displaySpeed = vis.FrontSpeed * 3.6f; break; 61 | case SpeedValue::Velocity: displaySpeed = vis.WorldVel.Length() * 3.6f; break; 62 | } 63 | 64 | // Avoid flickering negative sign 65 | if (displaySpeed < 0 && displaySpeed > -0.99f) { 66 | displaySpeed = 0; 67 | } 68 | 69 | nvg::BeginPath(); 70 | nvg::RoundedRect(0, 0, m_size.x, m_size.y, Setting_Speed_BorderRadius); 71 | 72 | nvg::FillColor(Setting_Speed_BackdropColor); 73 | nvg::Fill(); 74 | 75 | nvg::StrokeColor(Setting_Speed_BorderColor); 76 | nvg::StrokeWidth(Setting_Speed_BorderWidth); 77 | nvg::Stroke(); 78 | 79 | nvg::BeginPath(); 80 | nvg::FontFace(m_font); 81 | nvg::FontSize(Setting_Speed_FontSize); 82 | nvg::FillColor(Setting_Speed_TextColor); 83 | 84 | auto speedStyle = Setting_Speed_Style; 85 | #if !SIG_SCHOOL 86 | speedStyle = SpeedStyle::Single; 87 | #endif 88 | 89 | switch (speedStyle) { 90 | case SpeedStyle::Single: { 91 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Center); 92 | nvg::TextBox(Setting_Speed_Padding, m_size.y / 2, m_size.x - Setting_Speed_Padding * 2, Text::Format("%.0f", displaySpeed)); 93 | break; 94 | } 95 | 96 | case SpeedStyle::Double: { 97 | float textSize = (m_size.x - Setting_Speed_Padding * 2) / 2; 98 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Left); 99 | nvg::TextBox(Setting_Speed_Padding, m_size.y / 2, textSize, Icons::AngleDoubleUp + " " + Text::Format("%.0f", displaySpeed)); 100 | 101 | float sideSpeed = VehicleState::GetSideSpeed(vis) * 3.6f; 102 | // Avoid flickering negative sign 103 | if (sideSpeed < 0 && sideSpeed > -0.99f) { 104 | sideSpeed = 0; 105 | } 106 | nvg::TextBox(Setting_Speed_Padding + textSize, m_size.y / 2, textSize, Icons::AngleDoubleRight + " " + Text::Format("%.0f", sideSpeed)); 107 | break; 108 | } 109 | 110 | case SpeedStyle::Directional: { 111 | float sideSpeed = VehicleState::GetSideSpeed(vis) * 3.6f; 112 | float absSpeed = Math::Sqrt(Math::Pow(sideSpeed, 2) + Math::Pow(vis.FrontSpeed * 3.6f, 2)); 113 | 114 | float textSize = (m_size.x - Setting_Speed_Padding * 2) / 2; 115 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Left); 116 | nvg::TextBox(Setting_Speed_Padding, m_size.y / 2, textSize, Icons::ArrowUp + " " + Text::Format("%.0f", absSpeed)); 117 | float carAngle = 0; 118 | if (absSpeed != 0) { 119 | carAngle = Math::Abs((Math::Asin(vis.FrontSpeed * 3.6f / absSpeed) * 57) - 90); 120 | } 121 | // Avoid flickering negative sign 122 | if (absSpeed < 0.99f && absSpeed > -0.99f) { 123 | carAngle = 0; 124 | } 125 | nvg::TextBox(Setting_Speed_Padding + textSize, m_size.y / 2, textSize, Icons::Repeat + " " + Text::Format("%.0f", carAngle)); 126 | break; 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Source/Things/Acceleration.as: -------------------------------------------------------------------------------- 1 | class DashboardAcceleration : DashboardThing 2 | { 3 | float prev_speed = 0; 4 | int arr_size = 4; 5 | array acc = {0, 0, 0, 0}; 6 | int idx = 0; 7 | 8 | nvg::Font m_font; 9 | string m_fontPath; 10 | 11 | DashboardAcceleration() 12 | { 13 | super("Acceleration"); 14 | LoadFont(); 15 | } 16 | 17 | bool IsVisible(bool whenHidden) override { return whenHidden ? Setting_General_ShowAccelerationHidden : Setting_General_ShowAcceleration; } 18 | void SetVisible(bool visible, bool visibleWhenHidden) override 19 | { 20 | Setting_General_ShowAcceleration = visible; 21 | Setting_General_ShowAccelerationHidden = visibleWhenHidden; 22 | } 23 | 24 | void UpdateProportions() override 25 | { 26 | m_pos = Setting_General_AccelerationPos; 27 | m_size = Setting_General_AccelerationSize; 28 | } 29 | 30 | void SetProportions(const vec2 &in pos, const vec2 &in size) override 31 | { 32 | Setting_General_AccelerationPos = pos; 33 | Setting_General_AccelerationSize = size; 34 | } 35 | 36 | void ResetProportions() override 37 | { 38 | auto plugin = Meta::ExecutingPlugin(); 39 | plugin.GetSetting("Setting_General_AccelerationPos").Reset(); 40 | plugin.GetSetting("Setting_General_AccelerationSize").Reset(); 41 | } 42 | 43 | void LoadFont() 44 | { 45 | if (Setting_Acceleration_Font == m_fontPath) { 46 | return; 47 | } 48 | 49 | auto font = nvg::LoadFont(Setting_Acceleration_Font); 50 | if (font > 0) { 51 | m_fontPath = Setting_Acceleration_Font; 52 | m_font = font; 53 | } 54 | } 55 | 56 | void OnSettingsChanged() override 57 | { 58 | LoadFont(); 59 | } 60 | 61 | void RenderNegativeAccelerometer(const vec2 &in pos, const vec2 &in size, float acc) 62 | { 63 | float max_accel = (Setting_Acceleration_Unit == AccelerationUnit::KilometersPerHourPerSecond) ? Setting_Acceleration_MaximumAccelerationKMHS : Setting_Acceleration_MaximumAccelerationMSS; 64 | vec2 psize = vec2(size.x, size.y/2 - (Setting_Acceleration_BarPadding / 2)); 65 | vec2 npos = vec2(pos.x, pos.y + Setting_Acceleration_BarPadding); 66 | float accHeight = (acc / max_accel) * psize.y; 67 | 68 | nvg::Save(); 69 | nvg::BeginPath(); 70 | nvg::Translate(0, psize.y); 71 | nvg::RoundedRect(npos.x, npos.y, psize.x, psize.y, Setting_Acceleration_BorderRadius); 72 | nvg::StrokeWidth(Setting_Acceleration_BorderWidth); 73 | nvg::StrokeColor(Setting_Acceleration_BorderColor); 74 | nvg::FillColor(Setting_Acceleration_BackdropColor); 75 | nvg::Fill(); 76 | 77 | if (accHeight < 0) { 78 | nvg::Scissor(npos.x, npos.y, psize.x, accHeight * -1.0f); 79 | nvg::FillColor(Setting_Acceleration_Negative_Color); 80 | nvg::Fill(); 81 | nvg::ResetScissor(); 82 | } 83 | 84 | nvg::BeginPath(); 85 | nvg::RoundedRect(npos.x, npos.y, psize.x, psize.y, Setting_Acceleration_BorderRadius); 86 | nvg::Stroke(); 87 | nvg::Restore(); 88 | 89 | if (Setting_Acceleration_ShowTextValue) { 90 | float text_x_pos = pos.x + (psize.x / 2) - Setting_Acceleration_TextPadding*1.5; 91 | float text_y_pos_negative = npos.y + psize.y * 1.5; 92 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Center); 93 | nvg::FontFace(m_font); 94 | nvg::FontSize(Setting_Acceleration_FontSize); 95 | nvg::FillColor(Setting_Acceleration_TextColor); 96 | nvg::TextBox(text_x_pos, text_y_pos_negative, Setting_Acceleration_TextPadding * 3, Icons::AngleDoubleDown + "\n" + Text::Format("%.2f", acc < 0 ? Math::Abs(acc) : 0)); 97 | } 98 | } 99 | 100 | void RenderPositiveAccelerometer(const vec2 &in pos, const vec2 &in size, float acc) 101 | { 102 | float max_accel = (Setting_Acceleration_Unit == AccelerationUnit::KilometersPerHourPerSecond) ? Setting_Acceleration_MaximumAccelerationKMHS : Setting_Acceleration_MaximumAccelerationMSS; 103 | vec2 psize = vec2(size.x, size.y/2 - (Setting_Acceleration_BarPadding / 2)); 104 | float accHeight = (acc / max_accel) * psize.y; 105 | 106 | nvg::Save(); 107 | nvg::BeginPath(); 108 | 109 | nvg::Translate(0, psize.y); 110 | nvg::Scale(1,-1); 111 | 112 | nvg::RoundedRect(pos.x, pos.y, psize.x, psize.y, Setting_Acceleration_BorderRadius); 113 | nvg::StrokeWidth(Setting_Acceleration_BorderWidth); 114 | nvg::StrokeColor(Setting_Acceleration_BorderColor); 115 | nvg::FillColor(Setting_Acceleration_BackdropColor); 116 | nvg::Fill(); 117 | 118 | if (accHeight >= 0) { 119 | nvg::Scissor(pos.x, pos.y, psize.x, accHeight * 1.0f); 120 | nvg::FillColor(Setting_Acceleration_Positive_Color); 121 | nvg::Fill(); 122 | nvg::ResetScissor(); 123 | } 124 | 125 | nvg::BeginPath(); 126 | nvg::RoundedRect(pos.x, pos.y, psize.x, psize.y, Setting_Acceleration_BorderRadius); 127 | nvg::Stroke(); 128 | nvg::Restore(); 129 | 130 | if (Setting_Acceleration_ShowTextValue) { 131 | float text_x_pos = pos.x + (psize.x / 2) - Setting_Acceleration_TextPadding*1.5; 132 | float text_y_pos_positive = pos.y + (psize.y / 2); 133 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Center); 134 | nvg::FontFace(m_font); 135 | nvg::FontSize(Setting_Acceleration_FontSize); 136 | nvg::FillColor(Setting_Acceleration_TextColor); 137 | nvg::TextBox(text_x_pos, text_y_pos_positive, Setting_Acceleration_TextPadding * 3, Icons::AngleDoubleUp + "\n" + Text::Format("%.2f", acc > 0 ? acc : 0)); 138 | } 139 | } 140 | 141 | void Render(CSceneVehicleVisState@ vis) override 142 | { 143 | float speed = vis.FrontSpeed; 144 | float curr_acc = ((speed - prev_speed) / (g_dt/1000)); 145 | if (Setting_Acceleration_Unit == AccelerationUnit::KilometersPerHourPerSecond) { 146 | curr_acc *= 3.6; 147 | } 148 | prev_speed = speed; 149 | 150 | vec2 offset = vec2(0.0f, 0.0f); 151 | vec2 size = m_size; 152 | 153 | if (Setting_Acceleration_Smoothing) { 154 | acc[idx] = curr_acc; 155 | idx = (idx + 1) % arr_size; 156 | float sum = 0; 157 | for (int n = 0; n < arr_size; n++) { 158 | sum += acc[n]; 159 | } 160 | float avg = sum / arr_size; 161 | RenderPositiveAccelerometer(offset, size, avg); 162 | RenderNegativeAccelerometer(offset, size, avg); 163 | } else { 164 | RenderPositiveAccelerometer(offset, size, curr_acc); 165 | RenderNegativeAccelerometer(offset, size, curr_acc); 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /Source/Things/Gearbox.as: -------------------------------------------------------------------------------- 1 | class DashboardGearbox : DashboardThing 2 | { 3 | float m_minRpm = 200.0f; // Minimal RPM to avoid flickering at engine idle 4 | float m_maxRpm = 11000.0f; 5 | 6 | nvg::Font m_font; 7 | string m_fontPath; 8 | 9 | DashboardGearbox() 10 | { 11 | super("Gearbox"); 12 | LoadFont(); 13 | } 14 | 15 | bool IsVisible(bool whenHidden) override { return whenHidden ? Setting_General_ShowGearboxHidden : Setting_General_ShowGearbox; } 16 | void SetVisible(bool visible, bool visibleWhenHidden) override 17 | { 18 | Setting_General_ShowGearbox = visible; 19 | Setting_General_ShowGearboxHidden = visibleWhenHidden; 20 | } 21 | 22 | void UpdateProportions() override 23 | { 24 | m_pos = Setting_General_GearboxPos; 25 | m_size = Setting_General_GearboxSize; 26 | } 27 | 28 | void SetProportions(const vec2 &in pos, const vec2 &in size) override 29 | { 30 | Setting_General_GearboxPos = pos; 31 | Setting_General_GearboxSize = size; 32 | } 33 | 34 | void ResetProportions() override 35 | { 36 | auto plugin = Meta::ExecutingPlugin(); 37 | plugin.GetSetting("Setting_General_GearboxPos").Reset(); 38 | plugin.GetSetting("Setting_General_GearboxSize").Reset(); 39 | } 40 | 41 | void LoadFont() 42 | { 43 | if (Setting_Gearbox_Font == m_fontPath) { 44 | return; 45 | } 46 | 47 | auto font = nvg::LoadFont(Setting_Gearbox_Font); 48 | if (font > 0) { 49 | m_fontPath = Setting_Gearbox_Font; 50 | m_font = font; 51 | } 52 | } 53 | 54 | void OnSettingsChanged() override 55 | { 56 | LoadFont(); 57 | } 58 | 59 | void RenderNumbers(const vec2 &in pos, const vec2 &in size, uint gear, float rpm) 60 | { 61 | nvg::BeginPath(); 62 | nvg::RoundedRect(pos.x, pos.y, size.x, size.y, Setting_Gearbox_BorderRadius); 63 | nvg::StrokeWidth(Setting_Gearbox_BorderWidth); 64 | 65 | nvg::StrokeColor(Setting_Gearbox_BorderColor); 66 | nvg::FillColor(Setting_Gearbox_BackdropColor); 67 | 68 | nvg::Fill(); 69 | nvg::Stroke(); 70 | 71 | vec4 textColor = Setting_Gearbox_TextColor; 72 | if (Setting_Gearbox_UseGearColors) { 73 | 74 | switch (gear) { 75 | case 0: textColor = Setting_Gearbox_Gear0Color; break; 76 | case 1: textColor = Setting_Gearbox_Gear1Color; break; 77 | case 2: textColor = Setting_Gearbox_Gear2Color; break; 78 | case 3: textColor = Setting_Gearbox_Gear3Color; break; 79 | case 4: textColor = Setting_Gearbox_Gear4Color; break; 80 | case 5: textColor = Setting_Gearbox_Gear5Color; break; 81 | #if MP4 || TURBO 82 | case 6: textColor = Setting_Gearbox_Gear6Color; break; 83 | case 7: textColor = Setting_Gearbox_Gear7Color; break; 84 | #endif 85 | #if TURBO 86 | case 8: textColor = Setting_Gearbox_Gear8Color; break; 87 | case 9: textColor = Setting_Gearbox_Gear9Color; break; 88 | #endif 89 | } 90 | } 91 | nvg::FontFace(m_font); 92 | nvg::FillColor(textColor); 93 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Center); 94 | 95 | float gearY = 0.37f; 96 | float rpmY = 0.75f; 97 | if (!Setting_Gearbox_ShowRPMText) { 98 | gearY = 0.5f; 99 | } 100 | 101 | nvg::BeginPath(); 102 | nvg::FontSize(Setting_Gearbox_FontSize); 103 | nvg::TextBox(0, size.y * gearY, size.x, "" + gear); 104 | 105 | if (Setting_Gearbox_ShowRPMText) { 106 | nvg::BeginPath(); 107 | nvg::FontSize(16); 108 | nvg::TextBox(0, size.y * rpmY, size.x, Text::Format("%.01f", rpm / 1000.0f) + "k"); 109 | } 110 | } 111 | 112 | void RenderTachometer(const vec2 &in pos, const vec2 &in size, float rpm) 113 | { 114 | float rpmWidth = rpm / m_maxRpm * size.x; 115 | float downWidth = Setting_Gearbox_Downshift / m_maxRpm * size.x; 116 | float upWidth = (m_maxRpm - Setting_Gearbox_Upshift) / m_maxRpm * size.x; 117 | float midWidth = size.x - downWidth - upWidth; 118 | 119 | // Backdrop 120 | nvg::BeginPath(); 121 | nvg::RoundedRect(pos.x, pos.y, size.x, size.y, Setting_Gearbox_BorderRadius); 122 | nvg::StrokeWidth(Setting_Gearbox_BorderWidth); 123 | 124 | nvg::StrokeColor(Setting_Gearbox_BorderColor); 125 | nvg::FillColor(Setting_Gearbox_BackdropColor); 126 | 127 | nvg::Fill(); 128 | 129 | switch (Setting_Gearbox_TachometerStyle) { 130 | case GearboxTachometerStyle::Bar: { 131 | // Low RPM 132 | if (rpm > m_minRpm) { 133 | nvg::Scissor(pos.x, pos.y, Math::Min(rpmWidth, downWidth), size.y); 134 | nvg::FillColor(Setting_Gearbox_LowRPMColor); 135 | nvg::Fill(); 136 | nvg::ResetScissor(); 137 | } 138 | 139 | // Mid RPM 140 | if (rpm > Setting_Gearbox_Downshift) { 141 | nvg::Scissor(pos.x + downWidth, pos.y, Math::Min(rpmWidth - downWidth, midWidth), size.y); 142 | nvg::FillColor(Setting_Gearbox_MidRPMColor); 143 | nvg::Fill(); 144 | nvg::ResetScissor(); 145 | } 146 | 147 | // High RPM 148 | if (rpm > Setting_Gearbox_Upshift) { 149 | nvg::Scissor(pos.x + downWidth + midWidth, pos.y, Math::Min(rpmWidth - downWidth - midWidth, upWidth), size.y); 150 | nvg::FillColor(Setting_Gearbox_HighRPMColor); 151 | nvg::Fill(); 152 | nvg::ResetScissor(); 153 | } 154 | break; 155 | } 156 | 157 | case GearboxTachometerStyle::Dots: { 158 | const vec2 dotSpacing = vec2(12.0f, 20.0f); 159 | float dotSize = size.y - dotSpacing.y * 2; 160 | float dotSpace = dotSize + dotSpacing.x; 161 | int numDots = int((size.x - dotSpacing.x) / dotSpace); 162 | 163 | for (int i = 0; i < numDots; i++) { 164 | float scaledRpm = i / float(numDots) * m_maxRpm; 165 | 166 | nvg::BeginPath(); 167 | nvg::Circle(vec2(pos.x + (i + 0.5f) * dotSpace + dotSpacing.x, size.y / 2), dotSize / 2); 168 | if (rpm > m_minRpm && rpm >= scaledRpm) { 169 | if (scaledRpm <= Setting_Gearbox_Downshift) { 170 | nvg::FillColor(Setting_Gearbox_LowRPMColor); 171 | } else if (scaledRpm <= Setting_Gearbox_Upshift) { 172 | nvg::FillColor(Setting_Gearbox_MidRPMColor); 173 | } else { 174 | nvg::FillColor(Setting_Gearbox_HighRPMColor); 175 | } 176 | } else { 177 | nvg::FillColor(Setting_Gearbox_BackdropColor); 178 | } 179 | nvg::Fill(); 180 | } 181 | break; 182 | } 183 | 184 | case GearboxTachometerStyle::Blocks: { 185 | const float blockPadding = 8.0f; 186 | const float blockSpacing = 4.0f; 187 | vec2 blockSize = vec2(6, size.y - blockPadding * 2); 188 | float blockSpace = blockSize.x + blockSpacing; 189 | int numDots = int((size.x - blockPadding) / blockSpace); 190 | 191 | for (int i = 0; i < numDots; i++) { 192 | float scaledRpm = i / float(numDots) * m_maxRpm; 193 | 194 | nvg::BeginPath(); 195 | nvg::RoundedRect(pos.x + i * blockSpace + blockPadding, pos.y + blockPadding, blockSize.x, blockSize.y, 4); 196 | if (rpm > m_minRpm && rpm >= scaledRpm) { 197 | if (scaledRpm <= Setting_Gearbox_Downshift) { 198 | nvg::FillColor(Setting_Gearbox_LowRPMColor); 199 | } else if (scaledRpm <= Setting_Gearbox_Upshift) { 200 | nvg::FillColor(Setting_Gearbox_MidRPMColor); 201 | } else { 202 | nvg::FillColor(Setting_Gearbox_HighRPMColor); 203 | } 204 | } else { 205 | nvg::FillColor(Setting_Gearbox_BackdropColor); 206 | } 207 | nvg::Fill(); 208 | } 209 | break; 210 | } 211 | } 212 | 213 | // Border 214 | nvg::BeginPath(); 215 | nvg::RoundedRect(pos.x, pos.y, size.x, size.y, Setting_Gearbox_BorderRadius); 216 | nvg::Stroke(); 217 | } 218 | 219 | void Render(CSceneVehicleVisState@ vis) override 220 | { 221 | uint gear = vis.CurGear; 222 | float rpm = VehicleState::GetRPM(vis); 223 | 224 | vec2 offset; 225 | vec2 size = m_size; 226 | 227 | if (Setting_Gearbox_ShowText) { 228 | vec2 numbersSize(size.y, size.y); 229 | if (!Setting_Gearbox_ShowTachometer) { 230 | numbersSize.x = size.x; 231 | } 232 | 233 | RenderNumbers(offset, numbersSize, gear, rpm); 234 | 235 | offset.x += size.y + Setting_Gearbox_Spacing; 236 | size.x -= size.y + Setting_Gearbox_Spacing; 237 | } 238 | 239 | if (Setting_Gearbox_ShowTachometer) { 240 | RenderTachometer(offset, size, rpm); 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /Source/Things/Wheels.as: -------------------------------------------------------------------------------- 1 | enum WheelType 2 | { 3 | FL, FR, 4 | RL, RR, 5 | } 6 | 7 | class WheelState 8 | { 9 | bool m_right; 10 | float m_steerAngle; 11 | float m_rot; 12 | float m_slipCoef; 13 | float m_dirt; 14 | #if TMNEXT 15 | float m_breakCoef; 16 | float m_tireWear; 17 | float m_icing; 18 | float m_wetness; 19 | EPlugSurfaceMaterialId m_groundMaterial; 20 | #endif 21 | } 22 | 23 | class DashboardWheels : DashboardThing 24 | { 25 | nvg::Font m_font; 26 | string m_fontPath; 27 | 28 | DashboardWheels() 29 | { 30 | super("Wheels"); 31 | LoadFont(); 32 | } 33 | 34 | bool IsVisible(bool whenHidden) override { return whenHidden ? Setting_General_ShowWheelsHidden : Setting_General_ShowWheels; } 35 | void SetVisible(bool visible, bool visibleWhenHidden) override 36 | { 37 | Setting_General_ShowWheels = visible; 38 | Setting_General_ShowWheelsHidden = visibleWhenHidden; 39 | } 40 | 41 | void UpdateProportions() override 42 | { 43 | m_pos = Setting_General_WheelsPos; 44 | m_size = Setting_General_WheelsSize; 45 | } 46 | 47 | void SetProportions(const vec2 &in pos, const vec2 &in size) override 48 | { 49 | Setting_General_WheelsPos = pos; 50 | Setting_General_WheelsSize = size; 51 | } 52 | 53 | void ResetProportions() override 54 | { 55 | auto plugin = Meta::ExecutingPlugin(); 56 | plugin.GetSetting("Setting_General_WheelsPos").Reset(); 57 | plugin.GetSetting("Setting_General_WheelsSize").Reset(); 58 | } 59 | 60 | void LoadFont() 61 | { 62 | if (Setting_Wheels_DetailsFont == m_fontPath) { 63 | return; 64 | } 65 | 66 | auto font = nvg::LoadFont(Setting_Wheels_DetailsFont); 67 | if (font > 0) { 68 | m_fontPath = Setting_Wheels_DetailsFont; 69 | m_font = font; 70 | } 71 | } 72 | 73 | void OnSettingsChanged() override 74 | { 75 | LoadFont(); 76 | } 77 | 78 | WheelState GetWheelState(CSceneVehicleVisState@ vis, WheelType type) 79 | { 80 | WheelState ret; 81 | switch (type) { 82 | case WheelType::FL: 83 | ret.m_right = false; 84 | ret.m_steerAngle = vis.FLSteerAngle; 85 | ret.m_rot = vis.FLWheelRot; 86 | ret.m_slipCoef = vis.FLSlipCoef; 87 | #if TMNEXT 88 | ret.m_dirt = VehicleState::GetWheelDirt(vis, int(type)); 89 | ret.m_breakCoef = vis.FLBreakNormedCoef; 90 | ret.m_tireWear = vis.FLTireWear01; 91 | ret.m_icing = vis.FLIcing01; 92 | ret.m_groundMaterial = vis.FLGroundContactMaterial; 93 | #endif 94 | break; 95 | case WheelType::FR: 96 | ret.m_right = true; 97 | ret.m_steerAngle = vis.FRSteerAngle; 98 | ret.m_rot = vis.FRWheelRot; 99 | ret.m_slipCoef = vis.FRSlipCoef; 100 | #if TMNEXT 101 | ret.m_dirt = VehicleState::GetWheelDirt(vis, int(type)); 102 | ret.m_breakCoef = vis.FRBreakNormedCoef; 103 | ret.m_tireWear = vis.FRTireWear01; 104 | ret.m_icing = vis.FRIcing01; 105 | ret.m_groundMaterial = vis.FRGroundContactMaterial; 106 | #endif 107 | break; 108 | case WheelType::RL: 109 | ret.m_right = false; 110 | ret.m_steerAngle = vis.RLSteerAngle; 111 | ret.m_rot = vis.RLWheelRot; 112 | ret.m_slipCoef = vis.RLSlipCoef; 113 | #if TMNEXT 114 | ret.m_dirt = VehicleState::GetWheelDirt(vis, int(type)); 115 | ret.m_breakCoef = vis.RLBreakNormedCoef; 116 | ret.m_tireWear = vis.RLTireWear01; 117 | ret.m_icing = vis.RLIcing01; 118 | ret.m_groundMaterial = vis.RLGroundContactMaterial; 119 | #endif 120 | break; 121 | case WheelType::RR: 122 | ret.m_right = true; 123 | ret.m_steerAngle = vis.RRSteerAngle; 124 | ret.m_rot = vis.RRWheelRot; 125 | ret.m_slipCoef = vis.RRSlipCoef; 126 | #if TMNEXT 127 | ret.m_dirt = VehicleState::GetWheelDirt(vis, int(type)); 128 | ret.m_breakCoef = vis.RRBreakNormedCoef; 129 | ret.m_tireWear = vis.RRTireWear01; 130 | ret.m_icing = vis.RRIcing01; 131 | ret.m_groundMaterial = vis.RRGroundContactMaterial; 132 | #endif 133 | break; 134 | } 135 | 136 | #if TMNEXT 137 | // Wetness is applied on all wheels at the same time, so just duplicate it 138 | ret.m_wetness = vis.WetnessValue01; 139 | #endif 140 | 141 | return ret; 142 | } 143 | 144 | #if TMNEXT 145 | vec3 GetWheelSurfaceColor(const WheelState& state) 146 | { 147 | vec3 ret(0,0,0); 148 | 149 | switch (state.m_groundMaterial) { 150 | case EPlugSurfaceMaterialId::Ice: 151 | case EPlugSurfaceMaterialId::RoadIce: 152 | ret = Setting_Wheels_IceSurfaceColor; 153 | break; 154 | case EPlugSurfaceMaterialId::Grass: 155 | case EPlugSurfaceMaterialId::Green: 156 | ret = Setting_Wheels_GrassSurfaceColor; 157 | break; 158 | case EPlugSurfaceMaterialId::Dirt: 159 | ret = Setting_Wheels_DirtSurfaceColor; 160 | break; 161 | case EPlugSurfaceMaterialId::Wood: 162 | ret = Setting_Wheels_WoodSurfaceColor; 163 | break; 164 | case EPlugSurfaceMaterialId::Plastic: 165 | ret = Setting_Wheels_PlasticSurfaceColor; 166 | break; 167 | case EPlugSurfaceMaterialId::Snow: 168 | ret = Setting_Wheels_SnowSurfaceColor; 169 | break; 170 | case EPlugSurfaceMaterialId::Sand: 171 | ret = Setting_Wheels_SandSurfaceColor; 172 | } 173 | 174 | return ret; 175 | } 176 | #endif 177 | 178 | void RenderWheelVisual(const WheelState &in state, const vec2 &in pos, const vec2 &in size) 179 | { 180 | const float spacing = 10; 181 | 182 | auto tx = nvg::CurrentTransform(); 183 | 184 | // Handle wheel angle transformation 185 | if (Setting_Wheels_WheelAngle) { 186 | vec2 rotateOrigin = pos + size / 2; 187 | nvg::Translate(rotateOrigin.x, rotateOrigin.y); 188 | nvg::Rotate(state.m_steerAngle * -1); 189 | nvg::Translate(-rotateOrigin.x, -rotateOrigin.y); 190 | } 191 | 192 | // Wheel fill 193 | nvg::BeginPath(); 194 | nvg::RoundedRect(pos.x, pos.y, size.x, size.y, Setting_Wheels_WheelBorderRadius); 195 | 196 | vec3 fillColor = Setting_Wheels_WheelFillColor; 197 | 198 | #if TMNEXT 199 | if (state.m_icing > 0) { 200 | fillColor = Math::Lerp(fillColor, Setting_Wheels_IceColor, state.m_icing); 201 | } 202 | if (state.m_dirt > 0) { 203 | fillColor = Math::Lerp(fillColor, Setting_Wheels_DirtColor, state.m_dirt); 204 | } 205 | if (state.m_breakCoef > 0) { 206 | fillColor = Math::Lerp(fillColor, Setting_Wheels_BreakColor, state.m_breakCoef); 207 | } 208 | if (state.m_tireWear > 0) { 209 | fillColor = Math::Lerp(fillColor, Setting_Wheels_WearColor, state.m_tireWear); 210 | } 211 | if (state.m_wetness > 0) { 212 | fillColor = Math::Lerp(fillColor, Setting_Wheels_WetColor, state.m_wetness); 213 | } 214 | #endif 215 | 216 | nvg::FillColor(vec4(fillColor.x, fillColor.y, fillColor.z, Setting_Wheels_WheelFillAlpha)); 217 | nvg::Fill(); 218 | 219 | // Lines on the wheel 220 | if (Setting_Wheels_WheelMotion) { 221 | const float lineHeight = 7; 222 | const float lineSpacing = 5; 223 | int numLines = int(size.y / (lineHeight + lineSpacing)) + 1; 224 | nvg::Scissor(pos.x, pos.y, size.x, size.y); 225 | #if TMNEXT 226 | vec3 surfaceColor = Setting_Wheels_WheelSurface ? GetWheelSurfaceColor(state) : vec3(0,0,0); 227 | #else 228 | vec3 surfaceColor(0,0,0); 229 | #endif 230 | nvg::FillColor(vec4(surfaceColor.x, surfaceColor.y, surfaceColor.z, Setting_Wheels_WheelMotionAlpha)); 231 | for (int i = -1; i < numLines; i++) { 232 | float offset = i * (lineHeight + lineSpacing); 233 | offset = Math::Round(offset + ((state.m_rot * Setting_Wheels_MotionScale) % (lineHeight + lineSpacing))); 234 | nvg::BeginPath(); 235 | nvg::Rect(pos.x, pos.y + offset, size.x, lineHeight); 236 | nvg::Fill(); 237 | } 238 | nvg::ResetScissor(); 239 | } 240 | 241 | // Wheel border 242 | nvg::BeginPath(); 243 | nvg::RoundedRect(pos.x, pos.y, size.x, size.y, Setting_Wheels_WheelBorderRadius); 244 | nvg::StrokeWidth(Setting_Wheels_WheelBorderWidth); 245 | nvg::StrokeColor(Setting_Wheels_WheelBorderColor); 246 | nvg::Stroke(); 247 | 248 | nvg::SetTransform(tx); 249 | } 250 | 251 | void RenderWheelLine(const WheelState &in state, const vec3 &in color, float width, const string &in icon, const string &in text) 252 | { 253 | float iconSize = Setting_Wheels_DetailsFontSize + 4; 254 | 255 | nvg::FillColor(vec4(color.x, color.y, color.z, 1.0f)); 256 | 257 | nvg::TextAlign(nvg::Align::Center | nvg::Align::Top); 258 | 259 | if (state.m_right) { 260 | nvg::TextBox(width - iconSize, 0, iconSize, icon); 261 | nvg::TextAlign(nvg::Align::Right | nvg::Align::Top); 262 | nvg::TextBox(0, 0, width - iconSize, text); 263 | } else { 264 | nvg::TextBox(0, 0, iconSize, icon); 265 | nvg::TextAlign(nvg::Align::Left | nvg::Align::Top); 266 | nvg::TextBox(iconSize, 0, width - iconSize, text); 267 | } 268 | 269 | nvg::Translate(0, Setting_Wheels_DetailsFontSize + 4); 270 | } 271 | 272 | void RenderWheelDetails(const WheelState &in state, const vec2 &in pos, const vec2 &in size) 273 | { 274 | nvg::BeginPath(); 275 | nvg::FontFace(m_font); 276 | nvg::FontSize(Setting_Wheels_DetailsFontSize); 277 | 278 | auto tx = nvg::CurrentTransform(); 279 | nvg::Translate(pos.x, pos.y); 280 | 281 | if (state.m_slipCoef > 0) { 282 | RenderWheelLine(state, Setting_Wheels_SlipColor, size.x, Icons::Exclamation, "Slip"); 283 | } 284 | 285 | #if TMNEXT 286 | if (state.m_icing > 0) { 287 | RenderWheelLine(state, Setting_Wheels_IceColor, size.x, Icons::SnowflakeO, Text::Format("%.0f%%", state.m_icing * 100)); 288 | } 289 | 290 | if (state.m_breakCoef > 0) { 291 | RenderWheelLine(state, Setting_Wheels_BreakColor, size.x, Icons::ExclamationTriangle, Text::Format("%.0f%%", state.m_breakCoef * 100)); 292 | } 293 | 294 | if (state.m_tireWear > 0) { 295 | RenderWheelLine(state, Setting_Wheels_WearColor, size.x, Icons::ExclamationTriangle, Text::Format("%.0f%%", state.m_tireWear * 100)); 296 | } 297 | 298 | if (state.m_dirt > 0) { 299 | RenderWheelLine(state, Setting_Wheels_DirtColor, size.x, Icons::Road, Text::Format("%.0f%%", state.m_dirt * 100)); 300 | } 301 | 302 | if (state.m_wetness > 0) { 303 | RenderWheelLine(state, Setting_Wheels_WetColor, size.x, Icons::Tint, Text::Format("%.0f%%", state.m_wetness * 100)); 304 | } 305 | #endif 306 | 307 | nvg::SetTransform(tx); 308 | } 309 | 310 | void RenderWheel(const WheelState &in state, const vec2 &in pos, const vec2 &in size) 311 | { 312 | if (state.m_right) { 313 | RenderWheelVisual(state, pos + vec2(size.x - Setting_Wheels_WheelWidth, 0), vec2(Setting_Wheels_WheelWidth, size.y)); 314 | RenderWheelDetails(state, pos, vec2(size.x - Setting_Wheels_WheelWidth - Setting_Wheels_DetailsSpacing, size.y)); 315 | } else { 316 | RenderWheelVisual(state, pos, vec2(Setting_Wheels_WheelWidth, size.y)); 317 | RenderWheelDetails(state, pos + vec2(Setting_Wheels_WheelWidth + Setting_Wheels_DetailsSpacing, 0), vec2(size.x - Setting_Wheels_WheelWidth - Setting_Wheels_DetailsSpacing, size.y)); 318 | } 319 | } 320 | 321 | void RenderUnifiedWheels(const array &in wheels) 322 | { 323 | WheelState state; 324 | 325 | for (uint i = 0; i < wheels.Length; i++) { 326 | auto@ w = wheels[i]; 327 | 328 | state.m_slipCoef = Math::Max(state.m_slipCoef, w.m_slipCoef); 329 | state.m_dirt = Math::Max(state.m_dirt, w.m_dirt); 330 | #if TMNEXT 331 | state.m_breakCoef = Math::Max(state.m_breakCoef, w.m_breakCoef); 332 | state.m_tireWear = Math::Max(state.m_tireWear, w.m_tireWear); 333 | state.m_icing = Math::Max(state.m_icing, w.m_icing); 334 | state.m_wetness = Math::Max(state.m_wetness, w.m_wetness); 335 | #endif 336 | } 337 | 338 | vec2 pos( 339 | Setting_Wheels_Padding, 340 | Setting_Wheels_Padding 341 | ); 342 | 343 | vec2 size( 344 | m_size.x - Setting_Wheels_Padding * 2, 345 | (m_size.y - Setting_Wheels_Padding * 2 - Setting_Wheels_RowSpacing * 3) / 4 346 | ); 347 | 348 | RenderWheelDetails(state, pos, size); 349 | } 350 | 351 | void Render(CSceneVehicleVisState@ vis) override 352 | { 353 | // Backdrop 354 | nvg::BeginPath(); 355 | nvg::RoundedRect(0, 0, m_size.x, m_size.y, Setting_Wheels_BorderRadius); 356 | nvg::FillColor(Setting_Wheels_BackdropColor); 357 | nvg::Fill(); 358 | 359 | // Wheels 360 | array wheels; 361 | wheels.InsertLast(GetWheelState(vis, WheelType::FL)); 362 | wheels.InsertLast(GetWheelState(vis, WheelType::FR)); 363 | wheels.InsertLast(GetWheelState(vis, WheelType::RL)); 364 | wheels.InsertLast(GetWheelState(vis, WheelType::RR)); 365 | 366 | if (Setting_Wheels_Style == WheelsStyle::Unified) { 367 | RenderUnifiedWheels(wheels); 368 | } else { 369 | switch (Setting_Wheels_Style) { 370 | case WheelsStyle::Detailed: { 371 | vec2 wheelSize( 372 | m_size.x - Setting_Wheels_Padding * 2, 373 | (m_size.y - Setting_Wheels_Padding * 2 - Setting_Wheels_RowSpacing * 3) / 4 374 | ); 375 | 376 | for (uint i = 0; i < wheels.Length; i++) { 377 | RenderWheel(wheels[i], vec2( 378 | Setting_Wheels_Padding, 379 | Setting_Wheels_Padding + i * (wheelSize.y + Setting_Wheels_RowSpacing) 380 | ), wheelSize); 381 | } 382 | break; 383 | } 384 | 385 | case WheelsStyle::Simple: { 386 | vec2 wheelSize( 387 | Setting_Wheels_WheelWidth, 388 | (m_size.y - Setting_Wheels_Padding * 2 - Setting_Wheels_RowSpacing) / 2 389 | ); 390 | 391 | for (uint i = 0; i < wheels.Length; i++) { 392 | vec2 pos; 393 | if (i % 2 == 0) { 394 | pos = vec2( 395 | m_size.x - Setting_Wheels_Padding - Setting_Wheels_WheelWidth, 396 | Setting_Wheels_Padding + i / 2 * (wheelSize.y + Setting_Wheels_RowSpacing) 397 | ); 398 | } else { 399 | pos = vec2( 400 | Setting_Wheels_Padding, 401 | Setting_Wheels_Padding + i / 2 * (wheelSize.y + Setting_Wheels_RowSpacing) 402 | ); 403 | } 404 | 405 | RenderWheelVisual(wheels[i], pos, wheelSize); 406 | } 407 | break; 408 | } 409 | } 410 | } 411 | 412 | // Border around everything 413 | nvg::BeginPath(); 414 | nvg::RoundedRect(0, 0, m_size.x, m_size.y, Setting_Wheels_BorderRadius); 415 | nvg::StrokeWidth(Setting_Wheels_BorderWidth); 416 | nvg::StrokeColor(Setting_Wheels_BorderColor); 417 | nvg::Stroke(); 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /Source/Pads/Gamepad.as: -------------------------------------------------------------------------------- 1 | class DashboardPadGamepad : IDashboardPad 2 | { 3 | void RenderUniform(const vec2 &in size, CSceneVehicleVisState@ vis) 4 | { 5 | float leftSize = size.x * (0.5f - Setting_Gamepad_MiddleScale / 2) - Setting_Gamepad_Spacing; 6 | float midX = leftSize + Setting_Gamepad_Spacing; 7 | float midSize = size.x * Setting_Gamepad_MiddleScale; 8 | float rightX = midX + midSize + Setting_Gamepad_Spacing; 9 | float rightSize = size.x - rightX; 10 | float topSize = size.y / 2 - (Setting_Gamepad_Spacing / 2); 11 | float bottomY = size.y / 2 + (Setting_Gamepad_Spacing / 2); 12 | float bottomSize = size.y - bottomY; 13 | 14 | nvg::StrokeWidth(Setting_Gamepad_BorderWidth); 15 | if (Setting_Gamepad_UseBorderGradient) { 16 | nvg::StrokePaint(Setting_Gamepad_BorderGradient.GetPaint(vec2(), size)); 17 | } else { 18 | nvg::StrokeColor(Setting_Gamepad_BorderColor); 19 | } 20 | nvg::LineJoin(nvg::LineCapType::Round); 21 | 22 | // Steering scales 23 | float steerLeft = vis.InputSteer < 0 ? Math::Abs(vis.InputSteer) : 0.0f; 24 | float steerRight = vis.InputSteer > 0 ? vis.InputSteer : 0.0f; 25 | float pedalGas = vis.InputGasPedal; 26 | float pedalBrake = vis.InputBrakePedal; 27 | 28 | // It's possible that the brake pedal value is not set (eg. during replay playback), but we do 29 | // have a binary value whether we're currently braking or not, so we use that to force the 30 | // pedal value. 31 | if (vis.InputIsBraking) { 32 | pedalBrake = 1.0f; 33 | } 34 | 35 | // Left 36 | nvg::BeginPath(); 37 | nvg::MoveTo(vec2(0, size.y / 2)); 38 | nvg::LineTo(vec2(leftSize, size.y * Setting_Gamepad_ArrowPadding)); 39 | nvg::LineTo(vec2(leftSize, size.y - size.y * Setting_Gamepad_ArrowPadding)); 40 | nvg::ClosePath(); 41 | nvg::FillColor(Setting_Gamepad_EmptyFillColor); 42 | nvg::Fill(); 43 | if (steerLeft > 0) { 44 | float valueWidth = steerLeft * leftSize; 45 | nvg::Scissor(leftSize - valueWidth, 0, valueWidth, size.y); 46 | if (Setting_Gamepad_UseFillGradient) { 47 | nvg::FillPaint(Setting_Gamepad_FillGradient.GetPaint(vec2(), size)); 48 | } else { 49 | nvg::FillColor(Setting_Gamepad_FillColor); 50 | } 51 | nvg::Fill(); 52 | nvg::ResetScissor(); 53 | } 54 | nvg::Stroke(); 55 | 56 | // Right 57 | nvg::BeginPath(); 58 | nvg::MoveTo(vec2(rightX + rightSize, size.y / 2)); 59 | nvg::LineTo(vec2(rightX, size.y * Setting_Gamepad_ArrowPadding)); 60 | nvg::LineTo(vec2(rightX, size.y - size.y * Setting_Gamepad_ArrowPadding)); 61 | nvg::ClosePath(); 62 | nvg::FillColor(Setting_Gamepad_EmptyFillColor); 63 | nvg::Fill(); 64 | if (steerRight > 0) { 65 | float valueWidth = steerRight * rightSize; 66 | nvg::Scissor(rightX, 0, valueWidth, size.y); 67 | if (Setting_Gamepad_UseFillGradient) { 68 | nvg::FillPaint(Setting_Gamepad_FillGradient.GetPaint(vec2(), size)); 69 | } else { 70 | nvg::FillColor(Setting_Gamepad_FillColor); 71 | } 72 | nvg::Fill(); 73 | nvg::ResetScissor(); 74 | } 75 | nvg::Stroke(); 76 | 77 | // Up 78 | nvg::BeginPath(); 79 | nvg::RoundedRect(midX, 0, midSize, topSize, 5); 80 | nvg::FillColor(Setting_Gamepad_EmptyFillColor); 81 | nvg::Fill(); 82 | if (pedalGas > 0) { 83 | float valueHeight = pedalGas * topSize; 84 | nvg::Scissor(midX, topSize - valueHeight, midSize, valueHeight); 85 | if (Setting_Gamepad_UseFillGradient) { 86 | nvg::FillPaint(Setting_Gamepad_FillGradient.GetPaint(vec2(), size)); 87 | } else { 88 | nvg::FillColor(Setting_Gamepad_FillColor); 89 | } 90 | nvg::Fill(); 91 | nvg::ResetScissor(); 92 | } 93 | nvg::Stroke(); 94 | 95 | // Down 96 | nvg::BeginPath(); 97 | nvg::RoundedRect(midX, bottomY, midSize, bottomSize, 5); 98 | nvg::FillColor(Setting_Gamepad_EmptyFillColor); 99 | nvg::Fill(); 100 | if (pedalBrake > 0) { 101 | float valueHeight = pedalBrake * bottomSize; 102 | nvg::Scissor(midX, bottomY, midSize, valueHeight); 103 | if (Setting_Gamepad_UseFillGradient) { 104 | nvg::FillPaint(Setting_Gamepad_FillGradient.GetPaint(vec2(), size)); 105 | } else { 106 | nvg::FillColor(Setting_Gamepad_FillColor); 107 | } 108 | nvg::Fill(); 109 | nvg::ResetScissor(); 110 | } 111 | nvg::Stroke(); 112 | 113 | // Up & Down texts 114 | if (Setting_Gamepad_UpDownSymbols) { 115 | nvg::BeginPath(); 116 | nvg::FontFace(g_font); 117 | nvg::FontSize(midSize / 2); 118 | nvg::FillColor(Setting_Gamepad_TextColor); 119 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Center); 120 | nvg::TextBox(midX, topSize / 2, midSize, Icons::AngleUp); 121 | nvg::TextBox(midX, bottomY + bottomSize / 2, midSize, Icons::AngleDown); 122 | } 123 | 124 | // Steering percentage 125 | if (Setting_Gamepad_SteerPercentage) { 126 | nvg::FontFace(g_font); 127 | nvg::FontSize(Setting_Gamepad_SteerPercentageSize); 128 | nvg::FillColor(Setting_Gamepad_TextColor); 129 | 130 | // Left 131 | if (steerLeft > 0) { 132 | nvg::BeginPath(); 133 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Right); 134 | nvg::TextBox(0, size.y / 2, leftSize - Setting_Gamepad_Spacing, tostring(Math::Round(steerLeft * 100)) + "%"); 135 | } 136 | 137 | // Right 138 | if (steerRight > 0) { 139 | nvg::BeginPath(); 140 | nvg::TextAlign(nvg::Align::Middle | nvg::Align::Left); 141 | nvg::TextBox(rightX + Setting_Gamepad_Spacing, size.y / 2, rightSize, tostring(Math::Round(steerRight * 100)) + "%"); 142 | } 143 | } 144 | } 145 | 146 | void RenderClassic(const vec2 &in size, CSceneVehicleVisState@ vis) 147 | { 148 | const vec4 colorSteeringOn = Setting_Gamepad_FillColor; 149 | const vec4 colorSteeringOff = WithAlpha(Setting_Gamepad_FillColor, Setting_Gamepad_OffAlpha); 150 | 151 | auto posLeft = vec2(0, size.y / 2); 152 | auto posRight = vec2(size.x, size.y / 2); 153 | auto posTop = vec2(size.x / 2, 0); 154 | auto posBottom = vec2(size.x / 2, size.y); 155 | 156 | float leftSize = size.x * (0.5f - Setting_Gamepad_MiddleScale / 2) - Setting_Gamepad_Spacing; 157 | float midX = leftSize + Setting_Gamepad_Spacing; 158 | float midSize = size.x * Setting_Gamepad_MiddleScale; 159 | float rightX = midX + midSize + Setting_Gamepad_Spacing; 160 | float rightSize = size.x - rightX; 161 | 162 | nvg::BeginPath(); 163 | nvg::MoveTo(posLeft); 164 | nvg::LineTo(posTop); 165 | nvg::LineTo(posRight); 166 | nvg::LineTo(posBottom); 167 | nvg::ClosePath(); 168 | 169 | // Steering scales 170 | float steerLeft = vis.InputSteer < 0 ? Math::Abs(vis.InputSteer) : 0.0f; 171 | float steerRight = vis.InputSteer > 0 ? vis.InputSteer : 0.0f; 172 | float pedalGas = vis.InputGasPedal; 173 | float pedalBrake = vis.InputBrakePedal; 174 | 175 | // It's possible that the brake pedal value is not set (eg. during replay playback), but we do 176 | // have a binary value whether we're currently braking or not, so we use that to force the 177 | // pedal value. 178 | if (vis.InputIsBraking) { 179 | pedalBrake = 1.0f; 180 | } 181 | 182 | // Left 183 | nvg::Scissor(0, 0, leftSize, size.y); 184 | if (steerLeft > 0) { 185 | auto v = vec2(leftSize - steerLeft * leftSize, 0); 186 | nvg::FillPaint(nvg::LinearGradient(v - vec2(1, 0), v, colorSteeringOff, colorSteeringOn)); 187 | } else { 188 | nvg::FillColor(colorSteeringOff); 189 | } 190 | nvg::Fill(); 191 | nvg::ResetScissor(); 192 | 193 | // Right 194 | nvg::Scissor(rightX, 0, rightSize, size.y); 195 | if (steerRight > 0) { 196 | auto v = vec2(rightX + steerRight * rightSize, 0); 197 | nvg::FillPaint(nvg::LinearGradient(v, v + vec2(1, 0), colorSteeringOn, colorSteeringOff)); 198 | } else { 199 | nvg::FillColor(colorSteeringOff); 200 | } 201 | nvg::Fill(); 202 | nvg::ResetScissor(); 203 | 204 | // Up 205 | nvg::Scissor(midX, 0, midSize, size.y / 2 - Setting_Gamepad_Spacing / 2); 206 | nvg::FillColor(WithAlpha(Setting_Gamepad_ClassicUpColor, pedalGas > 0.1f ? 1.0f : Setting_Gamepad_OffAlpha)); 207 | nvg::Fill(); 208 | nvg::ResetScissor(); 209 | 210 | // Down 211 | nvg::Scissor(midX, size.y / 2 + Setting_Gamepad_Spacing / 2, midSize, size.y / 2 - Setting_Gamepad_Spacing / 2); 212 | nvg::FillColor(WithAlpha(Setting_Gamepad_ClassicDownColor, pedalBrake > 0.1f ? 1.0f : Setting_Gamepad_OffAlpha)); 213 | nvg::Fill(); 214 | nvg::ResetScissor(); 215 | } 216 | 217 | void RenderCateye(const vec2 &in size, CSceneVehicleVisState@ vis) 218 | { 219 | nvg::StrokeWidth(Setting_Gamepad_BorderWidth); 220 | nvg::StrokeColor(Setting_Gamepad_BorderColor); 221 | nvg::LineJoin(nvg::LineCapType::Round); 222 | 223 | // Steering scales 224 | float steerLeft = vis.InputSteer < 0 ? Math::Abs(vis.InputSteer) : 0.0f; 225 | float steerRight = vis.InputSteer > 0 ? vis.InputSteer : 0.0f; 226 | float pedalGas = vis.InputGasPedal; 227 | float pedalBrake = vis.InputBrakePedal; 228 | 229 | // It's possible that the brake pedal value is not set (eg. during replay playback), but we do 230 | // have a binary value whether we're currently braking or not, so we use that to force the 231 | // pedal value. 232 | if (vis.InputIsBraking) { 233 | pedalBrake = 1.0f; 234 | } 235 | 236 | auto posLeft = vec2(0, size.y / 2); 237 | auto posRight = vec2(size.x, size.y / 2); 238 | auto posTop = vec2(size.x / 2, 0); 239 | auto posBottom = vec2(size.x / 2, size.y); 240 | 241 | float leftInflectionX = (size.x / 2) - (size.x / 2 * Setting_Gamepad_MiddleScale); 242 | float leftSteerX = (1.0 - steerLeft) * leftInflectionX; 243 | float midX_Left = (size.x / 2) - (size.x / 2 * Setting_Gamepad_MiddleScale * ((100.0f - Setting_Gamepad_Spacing) / 100.0f)); 244 | float midX_Right = (size.x / 2) + (size.x / 2 * Setting_Gamepad_MiddleScale * ((100.0f - Setting_Gamepad_Spacing) / 100.0f)); 245 | float rightInflectionX = (size.x / 2) + (size.x / 2 * Setting_Gamepad_MiddleScale); 246 | float rightSteerX = rightInflectionX + (steerRight * (size.x - rightInflectionX)); 247 | float midY_Top = Setting_Gamepad_Spacing / 100.0f * size.y / 2; 248 | float midY_Bot = size.y - midY_Top; 249 | float midY_TopInflection = (size.y / 2) - ((((size.y / 2) - midY_Top) * 2) * Setting_Gamepad_ArrowPadding); 250 | float midY_BotInflection = (size.y / 2) + ((((size.y / 2) - midY_Top) * 2) * Setting_Gamepad_ArrowPadding); 251 | 252 | vec2 posLeftInflection = vec2(leftInflectionX, size.y / 2); 253 | vec2 posRightInflection = vec2(rightInflectionX, size.y / 2); 254 | vec2 posLeftSteer = vec2(leftSteerX, size.y / 2); 255 | vec2 posRightSteer = vec2(rightSteerX, size.y / 2); 256 | vec2 posMidLeft = vec2(midX_Left, size.y / 2); 257 | vec2 posMidRight = vec2(midX_Right, size.y / 2); 258 | vec2 posMidTop = vec2(size.x / 2, midY_Top); 259 | vec2 posMidBot = vec2(size.x / 2, midY_Bot); 260 | vec2 posTopInflection = vec2(size.x / 2, midY_TopInflection); 261 | vec2 posBotInflection = vec2(size.x / 2, midY_BotInflection); 262 | 263 | // Left 264 | if (Setting_Gamepad_CateyeUseSimpleSteer && steerLeft > 0) { 265 | auto v = vec2((size.x / 2) - steerLeft * (size.x / 2), 0); 266 | nvg::FillPaint(nvg::LinearGradient(v - vec2(1, 0), v, Setting_Gamepad_EmptyFillColor, Setting_Gamepad_FillColor)); 267 | } else { 268 | nvg::FillColor(Setting_Gamepad_EmptyFillColor); 269 | } 270 | FillInflectedTriangle(posLeft, posLeftInflection, posTop, posBottom); 271 | if (!Setting_Gamepad_CateyeUseSimpleSteer) { 272 | nvg::FillColor(Setting_Gamepad_FillColor); 273 | FillInflectedTriangle(posLeftSteer, posLeftInflection, posTop, posBottom); 274 | } 275 | StrokeInflectedTriangle(posLeft, posLeftInflection, posTop, posBottom); 276 | 277 | // Right 278 | if (Setting_Gamepad_CateyeUseSimpleSteer && steerRight > 0) { 279 | auto v = vec2((size.x / 2) + steerRight * (size.x / 2), 0); 280 | nvg::FillPaint(nvg::LinearGradient(v, v + vec2(1, 0), Setting_Gamepad_FillColor, Setting_Gamepad_EmptyFillColor)); 281 | } else { 282 | nvg::FillColor(Setting_Gamepad_EmptyFillColor); 283 | } 284 | FillInflectedTriangle(posRight, posRightInflection, posTop, posBottom); 285 | if (!Setting_Gamepad_CateyeUseSimpleSteer) { 286 | nvg::FillColor(Setting_Gamepad_FillColor); 287 | FillInflectedTriangle(posRightSteer, posRightInflection, posTop, posBottom); 288 | } 289 | StrokeInflectedTriangle(posRight, posRightInflection, posTop, posBottom); 290 | 291 | // Up 292 | nvg::FillColor(pedalGas > 0.1f ? Setting_Gamepad_FillColor : Setting_Gamepad_EmptyFillColor); 293 | FillInflectedTriangle(posMidTop, posTopInflection, posMidLeft, posMidRight); 294 | StrokeInflectedTriangle(posMidTop, posTopInflection, posMidLeft, posMidRight); 295 | 296 | // Down 297 | nvg::FillColor(pedalBrake > 0.1f ? Setting_Gamepad_FillColor : Setting_Gamepad_EmptyFillColor); 298 | FillInflectedTriangle(posMidBot, posBotInflection, posMidLeft, posMidRight); 299 | StrokeInflectedTriangle(posMidBot, posBotInflection, posMidLeft, posMidRight); 300 | } 301 | 302 | private void FillInflectedTriangle(const vec2 &in posApex, const vec2 &in posInflection, const vec2 &in posSide1, const vec2 &in posSide2) 303 | { 304 | if (Math::Abs(posApex.x - posInflection.x) < 0.01f && Math::Abs(posApex.y - posInflection.y) < 0.01f) { 305 | return; 306 | } 307 | 308 | nvg::BeginPath(); 309 | nvg::MoveTo(posApex); 310 | nvg::LineTo(posSide1); 311 | nvg::LineTo(posInflection); 312 | nvg::ClosePath(); 313 | nvg::Fill(); 314 | 315 | nvg::BeginPath(); 316 | nvg::MoveTo(posApex); 317 | nvg::LineTo(posSide2); 318 | nvg::LineTo(posInflection); 319 | nvg::ClosePath(); 320 | nvg::Fill(); 321 | } 322 | 323 | private void StrokeInflectedTriangle(const vec2 &in posApex, const vec2 &in posInflection, const vec2 &in posSide1, const vec2 &in posSide2) 324 | { 325 | nvg::BeginPath(); 326 | nvg::MoveTo(posApex); 327 | nvg::LineTo(posSide1); 328 | nvg::LineTo(posInflection); 329 | nvg::LineTo(posSide2); 330 | nvg::ClosePath(); 331 | nvg::Stroke(); 332 | } 333 | 334 | vec4 WithAlpha(const vec4 &in color, float alpha) 335 | { 336 | return vec4( 337 | color.x, 338 | color.y, 339 | color.z, 340 | color.w * alpha 341 | ); 342 | } 343 | 344 | void Render(const vec2 &in size, CSceneVehicleVisState@ vis) override 345 | { 346 | switch (Setting_Gamepad_Style) { 347 | case GamepadStyle::Uniform: RenderUniform(size, vis); break; 348 | case GamepadStyle::Classic: RenderClassic(size, vis); break; 349 | case GamepadStyle::Cateye: RenderCateye(size, vis); break; 350 | } 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /Source/Settings.as: -------------------------------------------------------------------------------- 1 | enum PadType 2 | { 3 | None, 4 | Gamepad, 5 | Keyboard, 6 | } 7 | 8 | [Setting category="General" name="Force pad type"] 9 | PadType Setting_General_ForcePadType = PadType::None; 10 | 11 | [Setting category="General" name="Hide during Intro sequences"] 12 | bool Setting_General_HideWhenNotPlaying = false; 13 | 14 | [Setting hidden] 15 | bool Setting_General_ShowPad = true; 16 | [Setting hidden] 17 | bool Setting_General_ShowPadHidden = true; 18 | [Setting hidden] 19 | vec2 Setting_General_PadPos = vec2(0.9f, 0.1f); 20 | [Setting hidden] 21 | vec2 Setting_General_PadSize = vec2(350, 200); 22 | 23 | [Setting hidden] 24 | bool Setting_General_ShowGearbox = true; 25 | [Setting hidden] 26 | bool Setting_General_ShowGearboxHidden = true; 27 | [Setting hidden] 28 | vec2 Setting_General_GearboxPos = vec2(0.9f, 0.32f); 29 | [Setting hidden] 30 | vec2 Setting_General_GearboxSize = vec2(350, 50); 31 | 32 | [Setting hidden] 33 | bool Setting_General_ShowWheels = false; 34 | [Setting hidden] 35 | bool Setting_General_ShowWheelsHidden = false; 36 | [Setting hidden] 37 | vec2 Setting_General_WheelsPos = vec2(0.909f, 0.9f); 38 | [Setting hidden] 39 | vec2 Setting_General_WheelsSize = vec2(230, 370); 40 | 41 | [Setting hidden] 42 | bool Setting_General_ShowAcceleration = false; 43 | [Setting hidden] 44 | bool Setting_General_ShowAccelerationHidden = false; 45 | [Setting hidden] 46 | vec2 Setting_General_AccelerationPos = vec2(0.720f, 0.120f); 47 | [Setting hidden] 48 | vec2 Setting_General_AccelerationSize = vec2(50, 350); 49 | 50 | [Setting hidden] 51 | bool Setting_General_ShowSpeed = true; 52 | [Setting hidden] 53 | bool Setting_General_ShowSpeedHidden = true; 54 | [Setting hidden] 55 | vec2 Setting_General_SpeedPos = vec2(0.909f, 0.4f); 56 | [Setting hidden] 57 | vec2 Setting_General_SpeedSize = vec2(230, 50); 58 | 59 | [Setting hidden] 60 | bool Setting_General_ShowClock = false; 61 | [Setting hidden] 62 | bool Setting_General_ShowClockHidden = false; 63 | [Setting hidden] 64 | vec2 Setting_General_ClockPos = vec2(0.906f, 0.48f); 65 | [Setting hidden] 66 | vec2 Setting_General_ClockSize = vec2(286, 50); 67 | 68 | 69 | 70 | enum GamepadStyle 71 | { 72 | Uniform, 73 | Classic, 74 | Cateye, 75 | } 76 | 77 | [Setting category="Gamepad" name="Style" description="Note: Not all styles use all customization options. Use Uniform for the best experience."] 78 | GamepadStyle Setting_Gamepad_Style = GamepadStyle::Uniform; 79 | 80 | [Setting category="Gamepad" name="Empty fill color" color if="!Setting_Gamepad_Style Classic"] 81 | vec4 Setting_Gamepad_EmptyFillColor = vec4(0, 0, 0, 0.7f); 82 | 83 | [Setting category="Gamepad" name="Fill gradient"] 84 | bool Setting_Gamepad_UseFillGradient = false; 85 | 86 | [Setting category="Gamepad" name="Fill color" color if="!Setting_Gamepad_UseFillGradient"] 87 | vec4 Setting_Gamepad_FillColor = vec4(1, 0.2f, 0.6f, 1); 88 | 89 | [Setting category="Gamepad" name="Fill gradient" if="Setting_Gamepad_UseFillGradient"] 90 | SettingsGradient Setting_Gamepad_FillGradient = SettingsGradient(vec4(1, 0.2f, 0.6f, 1), vec4(1, 0.6f, 0.9f, 1)); 91 | 92 | [Setting category="Gamepad" name="Border gradient"] 93 | bool Setting_Gamepad_UseBorderGradient = false; 94 | 95 | [Setting category="Gamepad" name="Border color" color if="!Setting_Gamepad_UseBorderGradient"] 96 | vec4 Setting_Gamepad_BorderColor = vec4(1, 1, 1, 1); 97 | 98 | [Setting category="Gamepad" name="Border gradient" if="Setting_Gamepad_UseBorderGradient"] 99 | SettingsGradient Setting_Gamepad_BorderGradient = SettingsGradient(vec4(1, 0.6f, 0.9f, 1), vec4(1, 0.9f, 0.95f, 1), 300); 100 | 101 | [Setting category="Gamepad" name="Border width" drag min=0 max=10 if="!Setting_Gamepad_Style Classic"] 102 | float Setting_Gamepad_BorderWidth = 3.0f; 103 | 104 | [Setting category="Gamepad" name="Spacing" drag min=0 max=100] 105 | float Setting_Gamepad_Spacing = 10.0f; 106 | 107 | [Setting category="Gamepad" name="Arrow padding" drag min=0 max=0.5 if="!Setting_Gamepad_Style Classic"] 108 | float Setting_Gamepad_ArrowPadding = 0.15f; 109 | 110 | [Setting category="Gamepad" name="Middle scale" drag min=0 max=1] 111 | float Setting_Gamepad_MiddleScale = 0.2f; 112 | 113 | [Setting category="Gamepad" name="Classic up color" color if="Setting_Gamepad_Style Classic"] 114 | vec4 Setting_Gamepad_ClassicUpColor = vec4(0.2f, 1, 0.6f, 1); 115 | 116 | [Setting category="Gamepad" name="Classic down color" color if="Setting_Gamepad_Style Classic"] 117 | vec4 Setting_Gamepad_ClassicDownColor = vec4(1, 0.6f, 0.2f, 1); 118 | 119 | [Setting category="Gamepad" name="Classic off alpha" drag min=0 max=1 if="Setting_Gamepad_Style Classic"] 120 | float Setting_Gamepad_OffAlpha = 0.33f; 121 | 122 | [Setting category="Gamepad" name="Display up/down arrow symbols" if="Setting_Gamepad_Style Uniform"] 123 | bool Setting_Gamepad_UpDownSymbols = true; 124 | 125 | [Setting category="Gamepad" name="Cateye use simple steer" if="Setting_Gamepad_Style Cateye"] 126 | bool Setting_Gamepad_CateyeUseSimpleSteer = false; 127 | 128 | [Setting category="Gamepad" name="Display steer percentage" if="Setting_Gamepad_Style Uniform"] 129 | bool Setting_Gamepad_SteerPercentage = false; 130 | 131 | [Setting 132 | category="Gamepad" 133 | name="Steer percentage size" 134 | drag min=2 max=40 135 | if="Setting_Gamepad_Style Uniform"] 136 | int Setting_Gamepad_SteerPercentageSize = 16; 137 | 138 | [Setting category="Gamepad" name="Text and symbol color" color if="Setting_Gamepad_Style Uniform"] 139 | vec4 Setting_Gamepad_TextColor = vec4(1, 1, 1, 1); 140 | 141 | 142 | 143 | enum KeyboardShape 144 | { 145 | Rectangle, 146 | Ellipse, 147 | Compact, 148 | } 149 | 150 | [Setting category="Keyboard" name="Shape"] 151 | KeyboardShape Setting_Keyboard_Shape = KeyboardShape::Rectangle; 152 | 153 | [Setting category="Keyboard" name="Empty fill color" color] 154 | vec4 Setting_Keyboard_EmptyFillColor = vec4(0, 0, 0, 0.7f); 155 | 156 | [Setting category="Keyboard" name="Fill gradient"] 157 | bool Setting_Keyboard_UseFillGradient = false; 158 | 159 | [Setting category="Keyboard" name="Fill color" color if="!Setting_Keyboard_UseFillGradient"] 160 | vec4 Setting_Keyboard_FillColor = vec4(1, 0.2f, 0.6f, 1); 161 | 162 | [Setting category="Keyboard" name="Fill gradient" if="Setting_Keyboard_UseFillGradient"] 163 | SettingsGradient Setting_Keyboard_FillGradient = SettingsGradient(vec4(1, 0.2f, 0.6f, 1), vec4(1, 0.6f, 0.9f, 1)); 164 | 165 | [Setting category="Keyboard" name="Border gradient"] 166 | bool Setting_Keyboard_UseBorderGradient = false; 167 | 168 | [Setting category="Keyboard" name="Border color" color if="!Setting_Keyboard_UseBorderGradient"] 169 | vec4 Setting_Keyboard_BorderColor = vec4(1, 1, 1, 1); 170 | 171 | [Setting category="Keyboard" name="Border gradient" if="Setting_Keyboard_UseBorderGradient"] 172 | SettingsGradient Setting_Keyboard_BorderGradient = SettingsGradient(vec4(1, 0.6f, 0.9f, 1), vec4(1, 0.9f, 0.95f, 1), 300); 173 | 174 | [Setting category="Keyboard" name="Border width" drag min=0 max=10] 175 | float Setting_Keyboard_BorderWidth = 3.0f; 176 | 177 | [Setting category="Keyboard" name="Border radius" drag min=0 max=50 if="!Setting_Keyboard_Shape Ellipse"] 178 | float Setting_Keyboard_BorderRadius = 5.0f; 179 | 180 | [Setting category="Keyboard" name="Spacing" drag min=0 max=100] 181 | float Setting_Keyboard_Spacing = 10.0f; 182 | 183 | [Setting category="Keyboard" name="Inactive alpha" drag min=0 max=1] 184 | float Setting_Keyboard_InactiveAlpha = 1.0f; 185 | 186 | 187 | 188 | [Setting category="Gearbox" name="Show text"] 189 | bool Setting_Gearbox_ShowText = true; 190 | 191 | [Setting category="Gearbox" name="Show RPM text"] 192 | bool Setting_Gearbox_ShowRPMText = false; 193 | 194 | [Setting category="Gearbox" name="Show tachometer"] 195 | bool Setting_Gearbox_ShowTachometer = true; 196 | 197 | enum GearboxTachometerStyle 198 | { 199 | Bar, 200 | Dots, 201 | Blocks, 202 | } 203 | 204 | [Setting category="Gearbox" name="Tachometer style" if="Setting_Gearbox_ShowTachometer"] 205 | GearboxTachometerStyle Setting_Gearbox_TachometerStyle = GearboxTachometerStyle::Blocks; 206 | 207 | [Setting category="Gearbox" name="Downshift threshold" drag min=0 max=11000] 208 | float Setting_Gearbox_Downshift = 6500; 209 | 210 | [Setting category="Gearbox" name="Upshift threshold" drag min=0 max=11000] 211 | float Setting_Gearbox_Upshift = 10000; 212 | 213 | [Setting category="Gearbox" name="Backdrop color" color] 214 | vec4 Setting_Gearbox_BackdropColor = vec4(0, 0, 0, 0.7f); 215 | 216 | [Setting category="Gearbox" name="Border color" color] 217 | vec4 Setting_Gearbox_BorderColor = vec4(1, 1, 1, 1); 218 | 219 | [Setting category="Gearbox" name="Border width" drag min=0 max=10] 220 | float Setting_Gearbox_BorderWidth = 3.0f; 221 | 222 | [Setting category="Gearbox" name="Border radius" drag min=0 max=50] 223 | float Setting_Gearbox_BorderRadius = 5.0f; 224 | 225 | [Setting category="Gearbox" name="Spacing" drag min=0 max=100 if="Setting_Gearbox_ShowText"] 226 | float Setting_Gearbox_Spacing = 10.0f; 227 | 228 | [Setting category="Gearbox" name="Low RPM color" color if="Setting_Gearbox_ShowTachometer"] 229 | vec4 Setting_Gearbox_LowRPMColor = vec4(1, 0.7f, 0, 1); 230 | 231 | [Setting category="Gearbox" name="Mid RPM color" color if="Setting_Gearbox_ShowTachometer"] 232 | vec4 Setting_Gearbox_MidRPMColor = vec4(0, 0.9f, 0, 1); 233 | 234 | [Setting category="Gearbox" name="High RPM color" color if="Setting_Gearbox_ShowTachometer"] 235 | vec4 Setting_Gearbox_HighRPMColor = vec4(0.8f, 0, 0, 1); 236 | 237 | [Setting category="Gearbox" name="Text color" color if="Setting_Gearbox_ShowText"] 238 | vec4 Setting_Gearbox_TextColor = vec4(1, 1, 1, 1); 239 | 240 | [Setting category="Gearbox" name="Font" if="Setting_Gearbox_ShowText"] 241 | string Setting_Gearbox_Font = "DroidSans.ttf"; 242 | 243 | [Setting category="Gearbox" name="Font size" drag min=0 max=100 if="Setting_Gearbox_ShowText"] 244 | float Setting_Gearbox_FontSize = 24.0f; 245 | 246 | [Setting category="Gearbox" name="Use gear colors"] 247 | bool Setting_Gearbox_UseGearColors = false; 248 | 249 | [Setting category="Gearbox" name="Gear 0 (backwards) color" color if="Setting_Gearbox_UseGearColors"] 250 | vec4 Setting_Gearbox_Gear0Color = vec4(1, 1, 1, 1); 251 | 252 | [Setting category="Gearbox" name="Gear 1 color" color if="Setting_Gearbox_UseGearColors"] 253 | vec4 Setting_Gearbox_Gear1Color = vec4(1, 1, 1, 1); 254 | 255 | [Setting category="Gearbox" name="Gear 2 color" color if="Setting_Gearbox_UseGearColors"] 256 | vec4 Setting_Gearbox_Gear2Color = vec4(1, 1, 1, 1); 257 | 258 | [Setting category="Gearbox" name="Gear 3 color" color if="Setting_Gearbox_UseGearColors"] 259 | vec4 Setting_Gearbox_Gear3Color = vec4(1, 1, 1, 1); 260 | 261 | [Setting category="Gearbox" name="Gear 4 color" color if="Setting_Gearbox_UseGearColors"] 262 | vec4 Setting_Gearbox_Gear4Color = vec4(1, 1, 1, 1); 263 | 264 | [Setting category="Gearbox" name="Gear 5 color" color if="Setting_Gearbox_UseGearColors"] 265 | vec4 Setting_Gearbox_Gear5Color = vec4(1, 1, 1, 1); 266 | 267 | #if MP4 || TURBO 268 | [Setting category="Gearbox" name="Gear 6 color" color if="Setting_Gearbox_UseGearColors"] 269 | vec4 Setting_Gearbox_Gear6Color = vec4(1, 1, 1, 1); 270 | 271 | [Setting category="Gearbox" name="Gear 7 color" color if="Setting_Gearbox_UseGearColors"] 272 | vec4 Setting_Gearbox_Gear7Color = vec4(1, 1, 1, 1); 273 | #endif 274 | 275 | #if TURBO 276 | [Setting category="Gearbox" name="Gear 8 color" color if="Setting_Gearbox_UseGearColors"] 277 | vec4 Setting_Gearbox_Gear8Color = vec4(1, 1, 1, 1); 278 | 279 | [Setting category="Gearbox" name="Gear 9 color" color if="Setting_Gearbox_UseGearColors"] 280 | vec4 Setting_Gearbox_Gear9Color = vec4(1, 1, 1, 1); 281 | #endif 282 | 283 | 284 | 285 | enum AccelerationUnit 286 | { 287 | MetersPerSecondPerSecond, 288 | KilometersPerHourPerSecond 289 | } 290 | 291 | [Setting category="Acceleration" name="Unit of measurement"] 292 | AccelerationUnit Setting_Acceleration_Unit = AccelerationUnit::MetersPerSecondPerSecond; 293 | 294 | [Setting category="Acceleration" name="Positive color" color] 295 | vec4 Setting_Acceleration_Positive_Color = vec4(0, 0.9f, 0, 1); 296 | 297 | [Setting category="Acceleration" name="Negative color" color] 298 | vec4 Setting_Acceleration_Negative_Color = vec4(0.8f, 0, 0, 1); 299 | 300 | [Setting category="Acceleration" name="Backdrop color" color] 301 | vec4 Setting_Acceleration_BackdropColor = vec4(0, 0, 0, 0.7f); 302 | 303 | [Setting category="Acceleration" name="Border color" color] 304 | vec4 Setting_Acceleration_BorderColor = vec4(1, 1, 1, 1); 305 | 306 | [Setting category="Acceleration" name="Border width" drag min=0 max=10] 307 | float Setting_Acceleration_BorderWidth = 3.0f; 308 | 309 | [Setting category="Acceleration" name="Border radius" drag min=0 max=50] 310 | float Setting_Acceleration_BorderRadius = 5.0f; 311 | 312 | [Setting category="Acceleration" name="Maximum m/s/s" drag min=0 max=250 if="Setting_Acceleration_Unit MetersPerSecondPerSecond"] 313 | float Setting_Acceleration_MaximumAccelerationMSS = 15.0f; 314 | 315 | [Setting category="Acceleration" name="Maximum km/h/s" drag min=0 max=250 if="Setting_Acceleration_Unit KilometersPerHourPerSecond"] 316 | float Setting_Acceleration_MaximumAccelerationKMHS = 54.0f; 317 | 318 | [Setting category="Acceleration" name="Bar padding" drag min=0 max=100] 319 | float Setting_Acceleration_BarPadding = 7.5f; 320 | 321 | [Setting category="Acceleration" name="Enable smoothing"] 322 | bool Setting_Acceleration_Smoothing = true; 323 | 324 | [Setting category="Acceleration" name="Show text value"] 325 | bool Setting_Acceleration_ShowTextValue = true; 326 | 327 | [Setting category="Acceleration" name="Text padding" drag min=0 max=100 if="Setting_Acceleration_ShowTextValue"] 328 | float Setting_Acceleration_TextPadding = 20.0f; 329 | 330 | [Setting category="Acceleration" name="Text color" color if="Setting_Acceleration_ShowTextValue"] 331 | vec4 Setting_Acceleration_TextColor = vec4(1, 1, 1, 1); 332 | 333 | [Setting category="Acceleration" name="Font" if="Setting_Acceleration_ShowTextValue"] 334 | string Setting_Acceleration_Font = "DroidSans.ttf"; 335 | 336 | [Setting category="Acceleration" name="Font size" drag min=0 max=100 if="Setting_Acceleration_ShowTextValue"] 337 | float Setting_Acceleration_FontSize = 15.0f; 338 | 339 | 340 | 341 | enum SpeedValue 342 | { 343 | FrontSpeed, 344 | Velocity, 345 | } 346 | 347 | [Setting category="Speed" name="Value"] 348 | SpeedValue Setting_Speed_Value = SpeedValue::FrontSpeed; 349 | 350 | enum SpeedStyle 351 | { 352 | Single, 353 | Double, 354 | Directional, 355 | } 356 | 357 | [Setting category="Speed" name="Style" description="Note: Double and directional only available in school and developer modes."] 358 | SpeedStyle Setting_Speed_Style = SpeedStyle::Double; 359 | 360 | [Setting category="Speed" name="Backdrop color" color] 361 | vec4 Setting_Speed_BackdropColor = vec4(0, 0, 0, 0.7f); 362 | 363 | [Setting category="Speed" name="Border color" color] 364 | vec4 Setting_Speed_BorderColor = vec4(1, 1, 1, 1); 365 | 366 | [Setting category="Speed" name="Text color" color] 367 | vec4 Setting_Speed_TextColor = vec4(1, 1, 1, 1); 368 | 369 | [Setting category="Speed" name="Border width" drag min=0 max=10] 370 | float Setting_Speed_BorderWidth = 3.0f; 371 | 372 | [Setting category="Speed" name="Border radius" drag min=0 max=50] 373 | float Setting_Speed_BorderRadius = 5.0f; 374 | 375 | [Setting category="Speed" name="Padding" drag min=0 max=50] 376 | float Setting_Speed_Padding = 20.0f; 377 | 378 | [Setting category="Speed" name="Font"] 379 | string Setting_Speed_Font = "DroidSans.ttf"; 380 | 381 | [Setting category="Speed" name="Font size" drag min=0 max=100] 382 | float Setting_Speed_FontSize = 24.0f; 383 | 384 | 385 | 386 | enum WheelsStyle 387 | { 388 | Detailed, 389 | Simple, 390 | Unified, 391 | } 392 | 393 | [Setting category="Wheels" name="Style"] 394 | WheelsStyle Setting_Wheels_Style = WheelsStyle::Detailed; 395 | 396 | [Setting category="Wheels" name="Show wheel motion"] 397 | bool Setting_Wheels_WheelMotion = true; 398 | 399 | [Setting category="Wheels" name="Wheel motion scale" drag min=0.1 max=10 if="Setting_Wheels_WheelMotion" if="!Setting_Wheels_Style Unified"] 400 | float Setting_Wheels_MotionScale = 5.0f; 401 | 402 | [Setting category="Wheels" name="Show wheel angle" if="!Setting_Wheels_Style Unified"] 403 | bool Setting_Wheels_WheelAngle = true; 404 | 405 | [Setting category="Wheels" name="Backdrop color" color] 406 | vec4 Setting_Wheels_BackdropColor = vec4(0, 0, 0, 0.7f); 407 | 408 | [Setting category="Wheels" name="Border color" color] 409 | vec4 Setting_Wheels_BorderColor = vec4(1, 1, 1, 1); 410 | 411 | [Setting category="Wheels" name="Border width" drag min=0 max=10] 412 | float Setting_Wheels_BorderWidth = 3.0f; 413 | 414 | [Setting category="Wheels" name="Border radius" drag min=0 max=50] 415 | float Setting_Wheels_BorderRadius = 5.0f; 416 | 417 | [Setting category="Wheels" name="Wheel fill color" color if="!Setting_Wheels_Style Unified"] 418 | vec3 Setting_Wheels_WheelFillColor = vec3(0, 0, 0); 419 | 420 | [Setting category="Wheels" name="Wheel border color" color if="!Setting_Wheels_Style Unified"] 421 | vec4 Setting_Wheels_WheelBorderColor = vec4(1, 1, 1, 1); 422 | 423 | [Setting category="Wheels" name="Wheel border width" drag min=0 max=10 if="!Setting_Wheels_Style Unified"] 424 | float Setting_Wheels_WheelBorderWidth = 3.0f; 425 | 426 | [Setting category="Wheels" name="Wheel border radius" drag min=0 max=5 if="!Setting_Wheels_Style Unified"] 427 | float Setting_Wheels_WheelBorderRadius = 5.0f; 428 | 429 | [Setting category="Wheels" name="Slip color" color] 430 | vec3 Setting_Wheels_SlipColor = vec3(1, 1, 0); 431 | 432 | [Setting category="Wheels" name="Ice color" color] 433 | vec3 Setting_Wheels_IceColor = vec3(0.6f, 1, 0.95f); 434 | 435 | [Setting category="Wheels" name="Dirt color" color] 436 | vec3 Setting_Wheels_DirtColor = vec3(0.8f, 0.5f, 0); 437 | 438 | [Setting category="Wheels" name="Damaged color" color] 439 | vec3 Setting_Wheels_BreakColor = vec3(1, 0, 0); 440 | 441 | [Setting category="Wheels" name="Wear color" color] 442 | vec3 Setting_Wheels_WearColor = vec3(1, 1, 0); 443 | 444 | [Setting category="Wheels" name="Wet color" color] 445 | vec3 Setting_Wheels_WetColor = vec3(0, 0.5f, 1); 446 | 447 | [Setting category="Wheels" name="Wheel width" drag min=0 max=100 if="!Setting_Wheels_Style Unified"] 448 | float Setting_Wheels_WheelWidth = 32.0f; 449 | 450 | [Setting category="Wheels" name="Wheel fill alpha" drag min=0 max=1 if="!Setting_Wheels_Style Unified"] 451 | float Setting_Wheels_WheelFillAlpha = 0.7f; 452 | 453 | [Setting category="Wheels" name="Wheel motion alpha" drag min=0 max=1 if="!Setting_Wheels_Style Unified"] 454 | float Setting_Wheels_WheelMotionAlpha = 0.5f; 455 | 456 | [Setting category="Wheels" name="Padding" drag min=0 max=100] 457 | float Setting_Wheels_Padding = 25.0f; 458 | 459 | [Setting category="Wheels" name="Row spacing" drag min=0 max=100] 460 | float Setting_Wheels_RowSpacing = 10.0f; 461 | 462 | [Setting category="Wheels" name="Details spacing" drag min=0 max=100] 463 | float Setting_Wheels_DetailsSpacing = 20.0f; 464 | 465 | [Setting category="Wheels" name="Details font"] 466 | string Setting_Wheels_DetailsFont = "DroidSans.ttf"; 467 | 468 | [Setting category="Wheels" name="Details font size" drag min=0 max=100] 469 | float Setting_Wheels_DetailsFontSize = 16.0f; 470 | 471 | [Setting category="Wheels" name="Show wheel surface"] 472 | bool Setting_Wheels_WheelSurface = false; 473 | 474 | [Setting category="Wheels" name="Ice Surface Color" color if="Setting_Wheels_WheelSurface"] 475 | vec3 Setting_Wheels_IceSurfaceColor = vec3(0.6f, 1, 0.95f); 476 | 477 | [Setting category="Wheels" name="Grass Surface Color" color if="Setting_Wheels_WheelSurface"] 478 | vec3 Setting_Wheels_GrassSurfaceColor = vec3(0, 1, 0); 479 | 480 | [Setting category="Wheels" name="Dirt Surface Color" color if="Setting_Wheels_WheelSurface"] 481 | vec3 Setting_Wheels_DirtSurfaceColor = vec3(0.8f, 0.5f, 0); 482 | 483 | [Setting category="Wheels" name="Wood Surface Color" color if="Setting_Wheels_WheelSurface"] 484 | vec3 Setting_Wheels_WoodSurfaceColor = vec3(0.3f, 0.1f, 0); 485 | 486 | [Setting category="Wheels" name="Plastic Surface Color" color if="Setting_Wheels_WheelSurface"] 487 | vec3 Setting_Wheels_PlasticSurfaceColor = vec3(0.5f, 0, 0); 488 | 489 | [Setting category="Wheels" name="Snow Surface Color" color if="Setting_Wheels_WheelSurface"] 490 | vec3 Setting_Wheels_SnowSurfaceColor = vec3(0.7f, 1, 1); 491 | 492 | [Setting category="Wheels" name="Sand Surface Color" color if="Setting_Wheels_WheelSurface"] 493 | vec3 Setting_Wheels_SandSurfaceColor = vec3(1, 1, 0); 494 | 495 | 496 | 497 | enum ClockMode 498 | { 499 | LocalTime, 500 | UTCTime, 501 | } 502 | 503 | enum ClockIcon 504 | { 505 | None, 506 | Left, 507 | Right, 508 | } 509 | 510 | [Setting category="Clock" name="Mode"] 511 | ClockMode Setting_Clock_Mode = ClockMode::LocalTime; 512 | 513 | [Setting category="Clock" name="Format"] 514 | string Setting_Clock_Format = "%F | %r"; 515 | 516 | [Setting category="Clock" name="Clock icon"] 517 | ClockIcon Setting_Clock_Icon = ClockIcon::Right; 518 | 519 | [Setting category="Clock" name="Backdrop color" color] 520 | vec4 Setting_Clock_BackdropColor = vec4(0, 0, 0, 0.7f); 521 | 522 | [Setting category="Clock" name="Border color" color] 523 | vec4 Setting_Clock_BorderColor = vec4(1, 1, 1, 1); 524 | 525 | [Setting category="Clock" name="Text color" color] 526 | vec4 Setting_Clock_TextColor = vec4(1, 1, 1, 1); 527 | 528 | [Setting category="Clock" name="Border width" drag min=0 max=10] 529 | float Setting_Clock_BorderWidth = 3.0f; 530 | 531 | [Setting category="Clock" name="Border radius" drag min=0 max=50] 532 | float Setting_Clock_BorderRadius = 5.0f; 533 | 534 | [Setting category="Clock" name="Font"] 535 | string Setting_Clock_Font = "DroidSans.ttf"; 536 | 537 | [Setting category="Clock" name="Font size" drag min=0 max=100] 538 | float Setting_Clock_FontSize = 20.0f; 539 | --------------------------------------------------------------------------------