From a00df6a1d9d7785b53f909af73c0b7ac9cf5161c Mon Sep 17 00:00:00 2001 From: O0rphan <104819654+O0rphan@users.noreply.github.com> Date: Sun, 24 May 2026 22:05:28 +0500 Subject: [PATCH 1/5] Add speed-based throttle resistance At low speed (parking, hairpins) the throttle trigger feels heavier for more precise inputs; at high speed (highway cruising) it goes lighter so sustained WOT is comfortable without finger fatigue. New settings (Settings > Right trigger section): - enable_speed_throttle: toggle (default on) - speed_throttle_boost: extra force at 0 km/h (0 = off, default 0) - speed_throttle_fade_km: speed where boost fully fades (default 80) When boost > 0, the effective max_force scales from (throttle_max_force + boost) at 0 km/h down to throttle_max_force at fade_km. The existing throttle ramp curve is preserved throughout. Translations added: ru, tr, zh, ja. --- src/lang/ja.py | 9 +++++++++ src/lang/ru.py | 9 +++++++++ src/lang/tr.py | 9 +++++++++ src/lang/zh.py | 9 +++++++++ src/modules/config/settings.py | 7 +++++++ src/modules/forzahorizon/effects.py | 11 +++++++++-- src/modules/gui/settings_tab.py | 8 ++++++++ src/modules/tui/settings_tab.py | 8 ++++++++ 8 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/lang/ja.py b/src/lang/ja.py index 258e2ee..4736631 100644 --- a/src/lang/ja.py +++ b/src/lang/ja.py @@ -125,4 +125,13 @@ "言語を選択し、アプリを再起動して適用してください。", "Restart the app to apply the new language.": "新しい言語を適用するにはアプリを再起動してください。", + "Speed-based throttle": "速度ベースのスロットル", + "Extra force at standstill": "停止時の追加力", + "Fade-out speed (km/h)": "減衰速度 (km/h)", + "Extra resistance at low speed for precise control, lighter at high speed.": + "低速では精密操作のため抵抗を増し、高速では軽くします。", + "0 = off. Flat resistance added at 0 km/h, fading to normal.": + "0 = オフ。0 km/hで平坦な抵抗が追加され、通常まで減衰します。", + "Speed where boost fully fades. Default 80.": + "ブーストが完全に減衰する速度。デフォルト80。", } diff --git a/src/lang/ru.py b/src/lang/ru.py index 1d7e36e..74ff1fb 100644 --- a/src/lang/ru.py +++ b/src/lang/ru.py @@ -136,4 +136,13 @@ "Выберите язык, затем перезапустите приложение, чтобы применить его.", "Restart the app to apply the new language.": "Перезапустите приложение, чтобы применить новый язык.", + "Speed-based throttle": "Зависимость газа от скорости", + "Extra force at standstill": "Доп. усилие на месте", + "Fade-out speed (km/h)": "Скорость затухания (км/ч)", + "Extra resistance at low speed for precise control, lighter at high speed.": + "Доп. сопротивление на низкой скорости для точного контроля, легче на высокой.", + "0 = off. Flat resistance added at 0 km/h, fading to normal.": + "0 = выкл. Плоское сопротивление при 0 км/ч, затухая до нормы.", + "Speed where boost fully fades. Default 80.": + "Скорость, где усиление полностью затухает. По умолчанию 80.", } diff --git a/src/lang/tr.py b/src/lang/tr.py index beec0bb..a17b10e 100644 --- a/src/lang/tr.py +++ b/src/lang/tr.py @@ -125,4 +125,13 @@ "Bir dil seçin, ardından uygulamak için uygulamayı yeniden başlatın.", "Restart the app to apply the new language.": "Yeni dili uygulamak için uygulamayı yeniden başlatın.", + "Speed-based throttle": "Hıza dayalı gaz", + "Extra force at standstill": "Dururken ekstra kuvvet", + "Fade-out speed (km/h)": "Sönme hızı (km/s)", + "Extra resistance at low speed for precise control, lighter at high speed.": + "Düşük hızda hassas kontrol için ekstra direnç, yüksek hızda daha hafif.", + "0 = off. Flat resistance added at 0 km/h, fading to normal.": + "0 = kapalı. 0 km/s'de düz direnç eklenir, normale kadar sönür.", + "Speed where boost fully fades. Default 80.": + "Takviyenin tamamen söndüğü hız. Varsayılan 80.", } diff --git a/src/lang/zh.py b/src/lang/zh.py index b9db58d..1176eca 100644 --- a/src/lang/zh.py +++ b/src/lang/zh.py @@ -125,4 +125,13 @@ "选择一种语言,然后重启应用以应用更改。", "Restart the app to apply the new language.": "重启应用以应用新语言。", + "Speed-based throttle": "基于速度的油门", + "Extra force at standstill": "静止时额外力度", + "Fade-out speed (km/h)": "衰减速度 (km/h)", + "Extra resistance at low speed for precise control, lighter at high speed.": + "低速时增加阻力以实现精确控制,高速时更轻。", + "0 = off. Flat resistance added at 0 km/h, fading to normal.": + "0 = 关闭。在0 km/h时增加平坦阻力,逐渐衰减至正常。", + "Speed where boost fully fades. Default 80.": + "增益完全衰减的速度。默认80。", } diff --git a/src/modules/config/settings.py b/src/modules/config/settings.py index 07908b0..d3f5fbe 100644 --- a/src/modules/config/settings.py +++ b/src/modules/config/settings.py @@ -53,6 +53,13 @@ class Settings: throttle_wall_engage_at: int = 250 # byte that triggers firmware wall. DO NOT CHANGE throttle_wall_release_at: int = 200 # hysteresis exit byte. DO NOT CHANGE + # MARK: R2 speed-based throttle + # Extra throttle resistance at low speed; fades as speed rises. + # boost=0 means off (no extra force). Flat additive at 0 km/h, fading to normal by fade_km. + enable_speed_throttle: bool = True + speed_throttle_boost: int = 30 # extra force added at standstill (0 = off) + speed_throttle_fade_km: float = 80.0 # speed where boost fully fades + # MARK: R2 rev limiter # Vibrates when rpm/max_rpm exceeds the ratio; brief hold smooths rpm bounce. enable_rev_limiter: bool = True diff --git a/src/modules/forzahorizon/effects.py b/src/modules/forzahorizon/effects.py index b8b373f..6ff0be2 100644 --- a/src/modules/forzahorizon/effects.py +++ b/src/modules/forzahorizon/effects.py @@ -162,8 +162,15 @@ def brake_resistance(self, t, s): def throttle_ramp(self, t, s): if not s.enable_throttle_resistance: return off() - return rigid(_ramp(t["accel"], s.accel_deadzone, s.throttle_baseline_force, - s.throttle_max_force, s.throttle_curve, s.throttle_wall_engage_at)) + force = _ramp(t["accel"], s.accel_deadzone, s.throttle_baseline_force, + s.throttle_max_force, s.throttle_curve, s.throttle_wall_engage_at) + if s.enable_speed_throttle and s.speed_throttle_boost > 0 and t["accel"] >= s.accel_deadzone: + fade = max(1.0, s.speed_throttle_fade_km) + speed = t["speed"] + if speed < fade: + ratio = speed / fade + force = min(255, force + int(s.speed_throttle_boost * (1.0 - ratio))) + return rigid(force) # --- Controller ----------------------------------------------------------- diff --git a/src/modules/gui/settings_tab.py b/src/modules/gui/settings_tab.py index af599f1..ec9f283 100644 --- a/src/modules/gui/settings_tab.py +++ b/src/modules/gui/settings_tab.py @@ -36,6 +36,14 @@ ("throttle_max_force", "Hard-press stiffness", 0, 255, ""), ("throttle_curve", "Stiffness curve shape", 0.1, 20.0, ""), ]), + ("Speed-based throttle", [ + ("enable_speed_throttle", "Speed-based throttle", None, None, + "Extra resistance at low speed for precise control, lighter at high speed."), + ("speed_throttle_boost", "Extra force at standstill", 0, 255, + "0 = off. Flat resistance added at 0 km/h, fading to normal."), + ("speed_throttle_fade_km", "Fade-out speed (km/h)", 0.0, 500.0, + "Speed where boost fully fades. Default 80."), + ]), ("ABS (anti-lock brake) rumble", [ ("abs_brake_threshold", "Only when braking harder than", 0, 255, ""), ("abs_min_speed_kmh", "Only when faster than (km/h)", 0.0, 500.0, ""), diff --git a/src/modules/tui/settings_tab.py b/src/modules/tui/settings_tab.py index 682aa9e..874d91a 100644 --- a/src/modules/tui/settings_tab.py +++ b/src/modules/tui/settings_tab.py @@ -33,6 +33,14 @@ ("throttle_max_force", "Hard-press stiffness", 0, 255, ""), ("throttle_curve", "Stiffness curve shape", 0.1, 20.0, ""), ]), + ("Speed-based throttle", [ + ("enable_speed_throttle", "Speed-based throttle", None, None, + "Extra resistance at low speed for precise control, lighter at high speed."), + ("speed_throttle_boost", "Extra force at standstill", 0, 255, + "0 = off. Flat resistance added at 0 km/h, fading to normal."), + ("speed_throttle_fade_km", "Fade-out speed (km/h)", 0.0, 500.0, + "Speed where boost fully fades. Default 80."), + ]), ("ABS (anti-lock brake) rumble", [ ("abs_brake_threshold", "Only when braking harder than", 0, 255, ""), ("abs_min_speed_kmh", "Only when faster than (km/h)", 0.0, 500.0, ""), From 07fda9e3865366706f49715bb0e910319febb6b2 Mon Sep 17 00:00:00 2001 From: O0rphan <104819654+O0rphan@users.noreply.github.com> Date: Sun, 24 May 2026 22:07:35 +0500 Subject: [PATCH 2/5] Add surface-type brake resistance Modulates brake trigger stiffness based on the driving surface. On tarmac the brake feels firm and progressive; on dirt it goes softer (spongy feel); on gravel/water even softer - matching how real brakes feel less effective on loose surfaces. New settings (Settings > Left trigger section): - enable_surface_brake: toggle (default off) - surface_brake_tarmac: multiplier on tarmac (1.0 = unchanged) - surface_brake_dirt: multiplier on dirt (default 0.7) - surface_brake_gravel: multiplier on gravel/water (default 0.5) Reuses the same surface classification from wheelspin_buzz (surface_rumble + wheel_in_puddle). Both baseline and max force are scaled by the multiplier before the ramp is computed. Only active while braking. Translations added: ru, tr, zh, ja. --- src/lang/ja.py | 12 ++++++++++++ src/lang/ru.py | 12 ++++++++++++ src/lang/tr.py | 12 ++++++++++++ src/lang/zh.py | 12 ++++++++++++ src/modules/config/settings.py | 8 ++++++++ src/modules/forzahorizon/effects.py | 14 ++++++++++++++ src/modules/gui/settings_tab.py | 10 ++++++++++ src/modules/tui/settings_tab.py | 10 ++++++++++ 8 files changed, 90 insertions(+) diff --git a/src/lang/ja.py b/src/lang/ja.py index 4736631..4335fd2 100644 --- a/src/lang/ja.py +++ b/src/lang/ja.py @@ -134,4 +134,16 @@ "0 = オフ。0 km/hで平坦な抵抗が追加され、通常まで減衰します。", "Speed where boost fully fades. Default 80.": "ブーストが完全に減衰する速度。デフォルト80。", + "Surface brake resistance": "路面ブレーキ抵抗", + "Tarmac multiplier": "ターマック乗数", + "Dirt multiplier": "ダート乗数", + "Gravel multiplier": "砂利乗数", + "Softer brake on loose surfaces, firmer on tarmac.": + "緩い路面ではブレーキが柔らかく、ターマックでは硬くなります。", + "1.0 = unchanged. Scales final resistance force.": + "1.0 = 変更なし。最終抵抗力をスケールします。", + "Lower = softer brake on dirt. Default 0.5.": + "低い = ダートでより柔らかいブレーキ。デフォルト0.5。", + "Lower = softer brake on gravel/water. Default 0.25.": + "低い = 砂利/水でより柔らかいブレーキ。デフォルト0.25。", } diff --git a/src/lang/ru.py b/src/lang/ru.py index 74ff1fb..91a85ae 100644 --- a/src/lang/ru.py +++ b/src/lang/ru.py @@ -145,4 +145,16 @@ "0 = выкл. Плоское сопротивление при 0 км/ч, затухая до нормы.", "Speed where boost fully fades. Default 80.": "Скорость, где усиление полностью затухает. По умолчанию 80.", + "Surface brake resistance": "Поверхностное торможение", + "Tarmac multiplier": "Множитель асфальта", + "Dirt multiplier": "Множитель грязи", + "Gravel multiplier": "Множитель гравия", + "Softer brake on loose surfaces, firmer on tarmac.": + "Мягкий тормоз на рыхлых поверхностях, жёсткий на асфальте.", + "1.0 = unchanged. Scales final resistance force.": + "1.0 = без изменений. Масштабирует итоговое сопротивление.", + "Lower = softer brake on dirt. Default 0.5.": + "Ниже = мягче тормоз на грязи. По умолчанию 0.5.", + "Lower = softer brake on gravel/water. Default 0.25.": + "Ниже = мягче тормоз на гравии/воде. По умолчанию 0.25.", } diff --git a/src/lang/tr.py b/src/lang/tr.py index a17b10e..04402e8 100644 --- a/src/lang/tr.py +++ b/src/lang/tr.py @@ -134,4 +134,16 @@ "0 = kapalı. 0 km/s'de düz direnç eklenir, normale kadar sönür.", "Speed where boost fully fades. Default 80.": "Takviyenin tamamen söndüğü hız. Varsayılan 80.", + "Surface brake resistance": "Yüzey freni direnci", + "Tarmac multiplier": "Asfalt çarpanı", + "Dirt multiplier": "Toprak çarpanı", + "Gravel multiplier": "Çakıl çarpanı", + "Softer brake on loose surfaces, firmer on tarmac.": + "Gevşek yüzeylerde daha yumuşak fren, asfaltta daha sert.", + "1.0 = unchanged. Scales final resistance force.": + "1.0 = değişmez. Son direnç kuvvetini ölçekler.", + "Lower = softer brake on dirt. Default 0.5.": + "Düşük = toprakta daha yumuşak fren. Varsayılan 0.5.", + "Lower = softer brake on gravel/water. Default 0.25.": + "Düşük = çakıl/suda daha yumuşak fren. Varsayılan 0.25.", } diff --git a/src/lang/zh.py b/src/lang/zh.py index 1176eca..37b2540 100644 --- a/src/lang/zh.py +++ b/src/lang/zh.py @@ -134,4 +134,16 @@ "0 = 关闭。在0 km/h时增加平坦阻力,逐渐衰减至正常。", "Speed where boost fully fades. Default 80.": "增益完全衰减的速度。默认80。", + "Surface brake resistance": "路面刹车阻力", + "Tarmac multiplier": "柏油路倍率", + "Dirt multiplier": "泥土路倍率", + "Gravel multiplier": "碎石路倍率", + "Softer brake on loose surfaces, firmer on tarmac.": + "松散路面刹车更软,柏油路更硬。", + "1.0 = unchanged. Scales final resistance force.": + "1.0 = 不变。缩放最终阻力力度。", + "Lower = softer brake on dirt. Default 0.5.": + "越低 = 泥土路刹车越软。默认0.5。", + "Lower = softer brake on gravel/water. Default 0.25.": + "越低 = 碎石/水面刹车越软。默认0.25。", } diff --git a/src/modules/config/settings.py b/src/modules/config/settings.py index d3f5fbe..976aacb 100644 --- a/src/modules/config/settings.py +++ b/src/modules/config/settings.py @@ -33,6 +33,14 @@ class Settings: enable_handbrake_bonus: bool = True handbrake_bonus: int = 60 # flat extra force while handbrake is engaged + # MARK: L2 surface-type brake + # Modulates brake resistance based on surface type. + # Tarmac = firm, dirt = softer, gravel = softest. Off by default. + enable_surface_brake: bool = False + surface_brake_tarmac: float = 1.0 # multiplier on tarmac (1.0 = unchanged) + surface_brake_dirt: float = 0.5 # multiplier on dirt + surface_brake_gravel: float = 0.25 # multiplier on gravel/water + # MARK: L2 ABS pulse # Vibrates when tire slip crosses thresholds under hard braking. enable_abs: bool = True diff --git a/src/modules/forzahorizon/effects.py b/src/modules/forzahorizon/effects.py index 6ff0be2..57fb09d 100644 --- a/src/modules/forzahorizon/effects.py +++ b/src/modules/forzahorizon/effects.py @@ -155,6 +155,20 @@ def brake_resistance(self, t, s): return rigid(s.handbrake_bonus) if handbrake else off() force = _ramp(t["brake"], s.brake_deadzone, s.brake_baseline_force, s.brake_max_force, s.brake_curve, s.brake_wall_engage_at) + if s.enable_surface_brake and t["brake"] > 0: + wheels = DRIVEN_WHEELS.get(t["drive_train"], ("fl", "fr", "rl", "rr")) + if any(t[f"wheel_in_puddle_{w}"] > 0 for w in wheels): + mult = s.surface_brake_gravel + else: + rumble = max(abs(t[f"surface_rumble_{w}"]) for w in wheels) + if rumble > 0.30: + mult = s.surface_brake_gravel + elif rumble > 0.10: + mult = s.surface_brake_dirt + else: + mult = s.surface_brake_tarmac + force = max(0, int(force * mult)) + force = _ramp(t["brake"], s.brake_deadzone, baseline, if handbrake: force += s.handbrake_bonus return rigid(force) diff --git a/src/modules/gui/settings_tab.py b/src/modules/gui/settings_tab.py index ec9f283..d003543 100644 --- a/src/modules/gui/settings_tab.py +++ b/src/modules/gui/settings_tab.py @@ -31,6 +31,16 @@ ("brake_static_wall_at", "Wall position on the trigger", 0, 255, ""), ("brake_static_wall_force", "Wall hardness", 0, 255, ""), ]), + ("Surface brake resistance", [ + ("enable_surface_brake", "Surface brake resistance", None, None, + "Softer brake on loose surfaces, firmer on tarmac."), + ("surface_brake_tarmac", "Tarmac multiplier", 0.0, 2.0, + "1.0 = unchanged. Scales final resistance force."), + ("surface_brake_dirt", "Dirt multiplier", 0.0, 2.0, + "Lower = softer brake on dirt. Default 0.5."), + ("surface_brake_gravel", "Gravel multiplier", 0.0, 2.0, + "Lower = softer brake on gravel/water. Default 0.25."), + ]), ("Right trigger - Gas force", [ ("throttle_baseline_force", "Resting stiffness", 0, 255, ""), ("throttle_max_force", "Hard-press stiffness", 0, 255, ""), diff --git a/src/modules/tui/settings_tab.py b/src/modules/tui/settings_tab.py index 874d91a..2ed8947 100644 --- a/src/modules/tui/settings_tab.py +++ b/src/modules/tui/settings_tab.py @@ -28,6 +28,16 @@ ("brake_static_wall_at", "Wall position on the trigger", 0, 255, ""), ("brake_static_wall_force", "Wall hardness", 0, 255, ""), ]), + ("Surface brake resistance", [ + ("enable_surface_brake", "Surface brake resistance", None, None, + "Softer brake on loose surfaces, firmer on tarmac."), + ("surface_brake_tarmac", "Tarmac multiplier", 0.0, 2.0, + "1.0 = unchanged. Scales final resistance force."), + ("surface_brake_dirt", "Dirt multiplier", 0.0, 2.0, + "Lower = softer brake on dirt. Default 0.5."), + ("surface_brake_gravel", "Gravel multiplier", 0.0, 2.0, + "Lower = softer brake on gravel/water. Default 0.25."), + ]), ("Right trigger - Gas force", [ ("throttle_baseline_force", "Resting stiffness", 0, 255, ""), ("throttle_max_force", "Hard-press stiffness", 0, 255, ""), From 546008d640b71ad2d0c4812a43db095d146b92b8 Mon Sep 17 00:00:00 2001 From: O0rphan <104819654+O0rphan@users.noreply.github.com> Date: Sun, 24 May 2026 22:09:38 +0500 Subject: [PATCH 3/5] Add turbo lag effect Brief deep vibration on R2 when boost pressure is actively climbing, simulating the moment of lag before full turbo power kicks in. Silent at steady-state boost or zero boost. Uses the existing boost field (0.0-1.0) from Forza telemetry. Delta detection: only fires when (current_boost - previous_boost) exceeds a configurable threshold. Amplitude scales with the rate of boost increase. New settings (Settings > Turbo lag section): - enable_turbo_lag: toggle (default on) - turbo_lag_freq: vibration frequency in Hz (default 8, deep rumble) - turbo_lag_amp: max amplitude (default 40) - turbo_lag_threshold: min boost delta per tick (default 0.05) R2 priority chain updated: gear shift > idle > rev limiter > wheelspin > turbo lag > end wall > throttle ramp. Translations added: ru, tr, zh, ja. --- src/lang/ja.py | 13 +++++++++++++ src/lang/ru.py | 13 +++++++++++++ src/lang/tr.py | 13 +++++++++++++ src/lang/zh.py | 13 +++++++++++++ src/modules/config/settings.py | 8 ++++++++ src/modules/forzahorizon/effects.py | 28 +++++++++++++++++++++++----- src/modules/gui/settings_tab.py | 10 ++++++++++ src/modules/tui/settings_tab.py | 10 ++++++++++ 8 files changed, 103 insertions(+), 5 deletions(-) diff --git a/src/lang/ja.py b/src/lang/ja.py index 4335fd2..8be56d2 100644 --- a/src/lang/ja.py +++ b/src/lang/ja.py @@ -146,4 +146,17 @@ "低い = ダートでより柔らかいブレーキ。デフォルト0.5。", "Lower = softer brake on gravel/water. Default 0.25.": "低い = 砂利/水でより柔らかいブレーキ。デフォルト0.25。", + "Turbo lag": "ターボラグ", + "Turbo lag vibration": "ターボラグ振動", + "Rumble speed (Hz)": "振動速度 (Hz)", + "Rumble strength": "振動強度", + "Boost change sensitivity": "ブースト変化感度", + "Brief R2 rumble when boost pressure climbs.": + "ブースト圧力上昇時の短いR2振動。", + "Deep rumble frequency. Default 8.": + "低い振動周波数。デフォルト8。", + "Max vibration amplitude. Default 40.": + "最大振動振幅。デフォルト40。", + "Min boost delta per tick to trigger. Default 0.05.": + "トリガーするtickあたりの最小ブースト変化。デフォルト0.05。", } diff --git a/src/lang/ru.py b/src/lang/ru.py index 91a85ae..6e90149 100644 --- a/src/lang/ru.py +++ b/src/lang/ru.py @@ -157,4 +157,17 @@ "Ниже = мягче тормоз на грязи. По умолчанию 0.5.", "Lower = softer brake on gravel/water. Default 0.25.": "Ниже = мягче тормоз на гравии/воде. По умолчанию 0.25.", + "Turbo lag": "Турбояма", + "Turbo lag vibration": "Вибрация турбоямы", + "Rumble speed (Hz)": "Скорость гула (Гц)", + "Rumble strength": "Сила гула", + "Boost change sensitivity": "Чувствительность изменения буста", + "Brief R2 rumble when boost pressure climbs.": + "Краткая вибрация R2 при росте давления наддува.", + "Deep rumble frequency. Default 8.": + "Глубокая частота гула. По умолчанию 8.", + "Max vibration amplitude. Default 40.": + "Макс. амплитуда вибрации. По умолчанию 40.", + "Min boost delta per tick to trigger. Default 0.05.": + "Мин. дельта буста за тик для срабатывания. По умолчанию 0.05.", } diff --git a/src/lang/tr.py b/src/lang/tr.py index 04402e8..56eff47 100644 --- a/src/lang/tr.py +++ b/src/lang/tr.py @@ -146,4 +146,17 @@ "Düşük = toprakta daha yumuşak fren. Varsayılan 0.5.", "Lower = softer brake on gravel/water. Default 0.25.": "Düşük = çakıl/suda daha yumuşak fren. Varsayılan 0.25.", + "Turbo lag": "Turbo gecikmesi", + "Turbo lag vibration": "Turbo gecikme titreşimi", + "Rumble speed (Hz)": "Gürültü hızı (Hz)", + "Rumble strength": "Gürültü gücü", + "Boost change sensitivity": "Boost değişim hassasiyeti", + "Brief R2 rumble when boost pressure climbs.": + "Boost basıncı yükselirken kısa R2 gürültüsü.", + "Deep rumble frequency. Default 8.": + "Derin gürültü frekansı. Varsayılan 8.", + "Max vibration amplitude. Default 40.": + "Maks titreşim genliği. Varsayılan 40.", + "Min boost delta per tick to trigger. Default 0.05.": + "Tetiklemek için tick başına min boost deltası. Varsayılan 0.05.", } diff --git a/src/lang/zh.py b/src/lang/zh.py index 37b2540..5f04ce3 100644 --- a/src/lang/zh.py +++ b/src/lang/zh.py @@ -146,4 +146,17 @@ "越低 = 泥土路刹车越软。默认0.5。", "Lower = softer brake on gravel/water. Default 0.25.": "越低 = 碎石/水面刹车越软。默认0.25。", + "Turbo lag": "涡轮迟滞", + "Turbo lag vibration": "涡轮迟滞振动", + "Rumble speed (Hz)": "轰鸣速度 (Hz)", + "Rumble strength": "轰鸣强度", + "Boost change sensitivity": "增压变化灵敏度", + "Brief R2 rumble when boost pressure climbs.": + "增压压力上升时R2短暂轰鸣。", + "Deep rumble frequency. Default 8.": + "低沉轰鸣频率。默认8。", + "Max vibration amplitude. Default 40.": + "最大振动幅度。默认40。", + "Min boost delta per tick to trigger. Default 0.05.": + "每次触发最小增压变化量。默认0.05。", } diff --git a/src/modules/config/settings.py b/src/modules/config/settings.py index 976aacb..f5221ed 100644 --- a/src/modules/config/settings.py +++ b/src/modules/config/settings.py @@ -93,6 +93,14 @@ class Settings: idle_amp_high: int = 30 # loud half of the cycle idle_period_s: float = 0.5 # full cycle length (sec) + # MARK: R2 turbo lag + # Brief deep vibration on R2 when boost pressure is climbing, simulating + # turbo lag before full power kicks in. Silent at steady-state or zero boost. + enable_turbo_lag: bool = True + turbo_lag_freq: int = 8 # deep rumble frequency (Hz) + turbo_lag_amp: int = 40 # max vibration amplitude + turbo_lag_threshold: float = 0.05 # minimum boost change per tick to trigger + # MARK: Gear shift # One short burst on up/downshift while moving. enable_gear_shift: bool = True # buzz on R2 diff --git a/src/modules/forzahorizon/effects.py b/src/modules/forzahorizon/effects.py index 57fb09d..ebd4e9b 100644 --- a/src/modules/forzahorizon/effects.py +++ b/src/modules/forzahorizon/effects.py @@ -68,6 +68,7 @@ def __init__(self): self._prev_gear = None self._shift_until = 0.0 self._rev_until = 0.0 + self._prev_boost = 0.0 def arm_shift(self, t, s, now): gear = t["gear"] @@ -139,6 +140,18 @@ def wheelspin_buzz(self, t, s, now): return vibrate(45, min(255, int(amp * 2))) return vibrate(130, amp) # tarmac: sharp squeal + def turbo_lag(self, t, s): + if not s.enable_turbo_lag: + self._prev_boost = t["boost"] + return None + boost = t["boost"] + delta = boost - self._prev_boost + self._prev_boost = boost + if delta < s.turbo_lag_threshold or boost < 0.1: + return None + amp = min(255, int(s.turbo_lag_amp * min(delta / 0.2, 1.0))) + return vibrate(s.turbo_lag_freq, amp) + def abs_pulse(self, t, s): if not s.enable_abs: return None @@ -168,7 +181,6 @@ def brake_resistance(self, t, s): else: mult = s.surface_brake_tarmac force = max(0, int(force * mult)) - force = _ramp(t["brake"], s.brake_deadzone, baseline, if handbrake: force += s.handbrake_bonus return rigid(force) @@ -206,8 +218,9 @@ class Controller: 1. Gear shift thump - one-shot burst on every shift, brief 2. Rev limiter buzz - rpm/max_rpm >= rev_limit_ratio 3. Wheelspin buzz - driven wheels slipping (surface-aware) - 4. Firmware end wall - hard wall near 100% travel (hysteresis) - 5. Throttle resistance - default rigid ramp 0..max_force + 4. Turbo lag - boost pressure rising, deep rumble + 5. Firmware end wall - hard wall near 100% travel (hysteresis) + 6. Throttle resistance - default rigid ramp 0..max_force """ def __init__(self, settings): @@ -275,11 +288,16 @@ def R2(self, t, s, now): if spin is not None: return spin - # 5. Firmware end wall - hard wall near 100% travel (latched via hysteresis) + # 5. Turbo lag - boost climbing, brief deep rumble + turbo = self.anim.turbo_lag(t, s) + if turbo is not None: + return turbo + + # 6. Firmware end wall - hard wall near 100% travel (latched via hysteresis) self._r2_in_wall = _wall_state(accel, self._r2_in_wall, s.throttle_wall_engage_at, s.throttle_wall_release_at) if self._r2_in_wall: return self.wall - # 6. Throttle resistance - default rigid ramp + # 7. Throttle resistance - default rigid ramp return self.anim.throttle_ramp(t, s) diff --git a/src/modules/gui/settings_tab.py b/src/modules/gui/settings_tab.py index d003543..a5cade7 100644 --- a/src/modules/gui/settings_tab.py +++ b/src/modules/gui/settings_tab.py @@ -74,6 +74,16 @@ ("Idle buzz", [ ("idle_amp_high", "Idle strength", 0, 255, ""), ]), + ("Turbo lag", [ + ("enable_turbo_lag", "Turbo lag vibration", None, None, + "Brief R2 rumble when boost pressure climbs."), + ("turbo_lag_freq", "Rumble speed (Hz)", 0, 255, + "Deep rumble frequency. Default 8."), + ("turbo_lag_amp", "Rumble strength", 0, 255, + "Max vibration amplitude. Default 40."), + ("turbo_lag_threshold", "Boost change sensitivity", 0.0, 1.0, + "Min boost delta per tick to trigger. Default 0.05."), + ]), ("Gear shift thump", [ ("gear_shift_freq", "Thump speed (Hz)", 0, 255, ""), ("gear_shift_amp", "Thump strength", 0, 255, ""), diff --git a/src/modules/tui/settings_tab.py b/src/modules/tui/settings_tab.py index 2ed8947..1b678c3 100644 --- a/src/modules/tui/settings_tab.py +++ b/src/modules/tui/settings_tab.py @@ -71,6 +71,16 @@ ("Idle buzz", [ ("idle_amp_high", "Idle strength", 0, 255, ""), ]), + ("Turbo lag", [ + ("enable_turbo_lag", "Turbo lag vibration", None, None, + "Brief R2 rumble when boost pressure climbs."), + ("turbo_lag_freq", "Rumble speed (Hz)", 0, 255, + "Deep rumble frequency. Default 8."), + ("turbo_lag_amp", "Rumble strength", 0, 255, + "Max vibration amplitude. Default 40."), + ("turbo_lag_threshold", "Boost change sensitivity", 0.0, 1.0, + "Min boost delta per tick to trigger. Default 0.05."), + ]), ("Gear shift thump", [ ("gear_shift_freq", "Thump speed (Hz)", 0, 255, ""), ("gear_shift_amp", "Thump strength", 0, 255, ""), From 446e88b566655b278ddef3fdd86b976aa8e8d2dc Mon Sep 17 00:00:00 2001 From: O0rphan <104819654+O0rphan@users.noreply.github.com> Date: Mon, 25 May 2026 00:12:26 +0500 Subject: [PATCH 4/5] Fix turbo lag: add cooldown to suppress steady-state jitter --- src/lang/ja.py | 3 +++ src/lang/ru.py | 3 +++ src/lang/tr.py | 3 +++ src/lang/zh.py | 3 +++ src/modules/config/settings.py | 1 + src/modules/forzahorizon/effects.py | 8 ++++++-- src/modules/gui/settings_tab.py | 2 ++ src/modules/tui/settings_tab.py | 2 ++ 8 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/lang/ja.py b/src/lang/ja.py index 8be56d2..40d7b66 100644 --- a/src/lang/ja.py +++ b/src/lang/ja.py @@ -159,4 +159,7 @@ "最大振動振幅。デフォルト40。", "Min boost delta per tick to trigger. Default 0.05.": "トリガーするtickあたりの最小ブースト変化。デフォルト0.05。", + "Cooldown (ms)": "クールダウン (ms)", + "Min gap between rumbles. Suppresses steady-state jitter. Default 300.": + "振動間の最小間隔。定常状態のジッターを抑制します。デフォルト300。", } diff --git a/src/lang/ru.py b/src/lang/ru.py index 6e90149..bc6266c 100644 --- a/src/lang/ru.py +++ b/src/lang/ru.py @@ -170,4 +170,7 @@ "Макс. амплитуда вибрации. По умолчанию 40.", "Min boost delta per tick to trigger. Default 0.05.": "Мин. дельта буста за тик для срабатывания. По умолчанию 0.05.", + "Cooldown (ms)": "Перезарядка (мс)", + "Min gap between rumbles. Suppresses steady-state jitter. Default 300.": + "Мин. пауза между вибрациями. Подавляет дрожание. По умолчанию 300.", } diff --git a/src/lang/tr.py b/src/lang/tr.py index 56eff47..6093f09 100644 --- a/src/lang/tr.py +++ b/src/lang/tr.py @@ -159,4 +159,7 @@ "Maks titreşim genliği. Varsayılan 40.", "Min boost delta per tick to trigger. Default 0.05.": "Tetiklemek için tick başına min boost deltası. Varsayılan 0.05.", + "Cooldown (ms)": "Bekleme (ms)", + "Min gap between rumbles. Suppresses steady-state jitter. Default 300.": + "Gürültüler arası min boşluk. Kararlı durum titremesini bastırır. Varsayılan 300.", } diff --git a/src/lang/zh.py b/src/lang/zh.py index 5f04ce3..1af0341 100644 --- a/src/lang/zh.py +++ b/src/lang/zh.py @@ -159,4 +159,7 @@ "最大振动幅度。默认40。", "Min boost delta per tick to trigger. Default 0.05.": "每次触发最小增压变化量。默认0.05。", + "Cooldown (ms)": "冷却时间 (ms)", + "Min gap between rumbles. Suppresses steady-state jitter. Default 300.": + "振动间最短间隔。抑制稳态抖动。默认300。", } diff --git a/src/modules/config/settings.py b/src/modules/config/settings.py index f5221ed..a3cb7d7 100644 --- a/src/modules/config/settings.py +++ b/src/modules/config/settings.py @@ -100,6 +100,7 @@ class Settings: turbo_lag_freq: int = 8 # deep rumble frequency (Hz) turbo_lag_amp: int = 40 # max vibration amplitude turbo_lag_threshold: float = 0.05 # minimum boost change per tick to trigger + turbo_lag_cooldown_ms: float = 300.0 # min time between rumbles (suppresses jitter) # MARK: Gear shift # One short burst on up/downshift while moving. diff --git a/src/modules/forzahorizon/effects.py b/src/modules/forzahorizon/effects.py index ebd4e9b..a7bf280 100644 --- a/src/modules/forzahorizon/effects.py +++ b/src/modules/forzahorizon/effects.py @@ -69,6 +69,7 @@ def __init__(self): self._shift_until = 0.0 self._rev_until = 0.0 self._prev_boost = 0.0 + self._turbo_last_fire = 0.0 def arm_shift(self, t, s, now): gear = t["gear"] @@ -140,7 +141,7 @@ def wheelspin_buzz(self, t, s, now): return vibrate(45, min(255, int(amp * 2))) return vibrate(130, amp) # tarmac: sharp squeal - def turbo_lag(self, t, s): + def turbo_lag(self, t, s, now): if not s.enable_turbo_lag: self._prev_boost = t["boost"] return None @@ -149,6 +150,9 @@ def turbo_lag(self, t, s): self._prev_boost = boost if delta < s.turbo_lag_threshold or boost < 0.1: return None + if now - self._turbo_last_fire < s.turbo_lag_cooldown_ms / 1000.0: + return None + self._turbo_last_fire = now amp = min(255, int(s.turbo_lag_amp * min(delta / 0.2, 1.0))) return vibrate(s.turbo_lag_freq, amp) @@ -289,7 +293,7 @@ def R2(self, t, s, now): return spin # 5. Turbo lag - boost climbing, brief deep rumble - turbo = self.anim.turbo_lag(t, s) + turbo = self.anim.turbo_lag(t, s, now) if turbo is not None: return turbo diff --git a/src/modules/gui/settings_tab.py b/src/modules/gui/settings_tab.py index a5cade7..ce16af4 100644 --- a/src/modules/gui/settings_tab.py +++ b/src/modules/gui/settings_tab.py @@ -83,6 +83,8 @@ "Max vibration amplitude. Default 40."), ("turbo_lag_threshold", "Boost change sensitivity", 0.0, 1.0, "Min boost delta per tick to trigger. Default 0.05."), + ("turbo_lag_cooldown_ms", "Cooldown (ms)", 0.0, 2000.0, + "Min gap between rumbles. Suppresses steady-state jitter. Default 300."), ]), ("Gear shift thump", [ ("gear_shift_freq", "Thump speed (Hz)", 0, 255, ""), diff --git a/src/modules/tui/settings_tab.py b/src/modules/tui/settings_tab.py index 1b678c3..6fb1833 100644 --- a/src/modules/tui/settings_tab.py +++ b/src/modules/tui/settings_tab.py @@ -80,6 +80,8 @@ "Max vibration amplitude. Default 40."), ("turbo_lag_threshold", "Boost change sensitivity", 0.0, 1.0, "Min boost delta per tick to trigger. Default 0.05."), + ("turbo_lag_cooldown_ms", "Cooldown (ms)", 0.0, 2000.0, + "Min gap between rumbles. Suppresses steady-state jitter. Default 300."), ]), ("Gear shift thump", [ ("gear_shift_freq", "Thump speed (Hz)", 0, 255, ""), From 880a6002e5eebc343a027346c3f4f0993eb82802 Mon Sep 17 00:00:00 2001 From: O0rphan <104819654+O0rphan@users.noreply.github.com> Date: Mon, 25 May 2026 00:47:34 +0500 Subject: [PATCH 5/5] Raise turbo lag threshold default to 0.15 to suppress jitter --- src/lang/ja.py | 4 ++-- src/lang/ru.py | 4 ++-- src/lang/tr.py | 4 ++-- src/lang/zh.py | 4 ++-- src/modules/config/settings.py | 2 +- src/modules/gui/settings_tab.py | 2 +- src/modules/tui/settings_tab.py | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lang/ja.py b/src/lang/ja.py index 40d7b66..5e9c625 100644 --- a/src/lang/ja.py +++ b/src/lang/ja.py @@ -157,8 +157,8 @@ "低い振動周波数。デフォルト8。", "Max vibration amplitude. Default 40.": "最大振動振幅。デフォルト40。", - "Min boost delta per tick to trigger. Default 0.05.": - "トリガーするtickあたりの最小ブースト変化。デフォルト0.05。", + "Min boost delta per tick to trigger. Default 0.15.": + "トリガーするtickあたりの最小ブースト変化。デフォルト0.15。", "Cooldown (ms)": "クールダウン (ms)", "Min gap between rumbles. Suppresses steady-state jitter. Default 300.": "振動間の最小間隔。定常状態のジッターを抑制します。デフォルト300。", diff --git a/src/lang/ru.py b/src/lang/ru.py index bc6266c..7a166e4 100644 --- a/src/lang/ru.py +++ b/src/lang/ru.py @@ -168,8 +168,8 @@ "Глубокая частота гула. По умолчанию 8.", "Max vibration amplitude. Default 40.": "Макс. амплитуда вибрации. По умолчанию 40.", - "Min boost delta per tick to trigger. Default 0.05.": - "Мин. дельта буста за тик для срабатывания. По умолчанию 0.05.", + "Min boost delta per tick to trigger. Default 0.15.": + "Мин. дельта буста за тик для срабатывания. По умолчанию 0.15.", "Cooldown (ms)": "Перезарядка (мс)", "Min gap between rumbles. Suppresses steady-state jitter. Default 300.": "Мин. пауза между вибрациями. Подавляет дрожание. По умолчанию 300.", diff --git a/src/lang/tr.py b/src/lang/tr.py index 6093f09..c02d23f 100644 --- a/src/lang/tr.py +++ b/src/lang/tr.py @@ -157,8 +157,8 @@ "Derin gürültü frekansı. Varsayılan 8.", "Max vibration amplitude. Default 40.": "Maks titreşim genliği. Varsayılan 40.", - "Min boost delta per tick to trigger. Default 0.05.": - "Tetiklemek için tick başına min boost deltası. Varsayılan 0.05.", + "Min boost delta per tick to trigger. Default 0.15.": + "Tetiklemek için tick başına min boost deltası. Varsayılan 0.15.", "Cooldown (ms)": "Bekleme (ms)", "Min gap between rumbles. Suppresses steady-state jitter. Default 300.": "Gürültüler arası min boşluk. Kararlı durum titremesini bastırır. Varsayılan 300.", diff --git a/src/lang/zh.py b/src/lang/zh.py index 1af0341..702dd9a 100644 --- a/src/lang/zh.py +++ b/src/lang/zh.py @@ -157,8 +157,8 @@ "低沉轰鸣频率。默认8。", "Max vibration amplitude. Default 40.": "最大振动幅度。默认40。", - "Min boost delta per tick to trigger. Default 0.05.": - "每次触发最小增压变化量。默认0.05。", + "Min boost delta per tick to trigger. Default 0.15.": + "每次触发最小增压变化量。默认0.15。", "Cooldown (ms)": "冷却时间 (ms)", "Min gap between rumbles. Suppresses steady-state jitter. Default 300.": "振动间最短间隔。抑制稳态抖动。默认300。", diff --git a/src/modules/config/settings.py b/src/modules/config/settings.py index a3cb7d7..20d8beb 100644 --- a/src/modules/config/settings.py +++ b/src/modules/config/settings.py @@ -99,7 +99,7 @@ class Settings: enable_turbo_lag: bool = True turbo_lag_freq: int = 8 # deep rumble frequency (Hz) turbo_lag_amp: int = 40 # max vibration amplitude - turbo_lag_threshold: float = 0.05 # minimum boost change per tick to trigger + turbo_lag_threshold: float = 0.15 # minimum boost change per tick to trigger turbo_lag_cooldown_ms: float = 300.0 # min time between rumbles (suppresses jitter) # MARK: Gear shift diff --git a/src/modules/gui/settings_tab.py b/src/modules/gui/settings_tab.py index ce16af4..2303d59 100644 --- a/src/modules/gui/settings_tab.py +++ b/src/modules/gui/settings_tab.py @@ -82,7 +82,7 @@ ("turbo_lag_amp", "Rumble strength", 0, 255, "Max vibration amplitude. Default 40."), ("turbo_lag_threshold", "Boost change sensitivity", 0.0, 1.0, - "Min boost delta per tick to trigger. Default 0.05."), + "Min boost delta per tick to trigger. Default 0.15."), ("turbo_lag_cooldown_ms", "Cooldown (ms)", 0.0, 2000.0, "Min gap between rumbles. Suppresses steady-state jitter. Default 300."), ]), diff --git a/src/modules/tui/settings_tab.py b/src/modules/tui/settings_tab.py index 6fb1833..1a2b198 100644 --- a/src/modules/tui/settings_tab.py +++ b/src/modules/tui/settings_tab.py @@ -79,7 +79,7 @@ ("turbo_lag_amp", "Rumble strength", 0, 255, "Max vibration amplitude. Default 40."), ("turbo_lag_threshold", "Boost change sensitivity", 0.0, 1.0, - "Min boost delta per tick to trigger. Default 0.05."), + "Min boost delta per tick to trigger. Default 0.15."), ("turbo_lag_cooldown_ms", "Cooldown (ms)", 0.0, 2000.0, "Min gap between rumbles. Suppresses steady-state jitter. Default 300."), ]),