Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/lang/ja.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,41 @@
"言語を選択し、アプリを再起動して適用してください。",
"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。",
"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。",
"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.15.":
"トリガーするtickあたりの最小ブースト変化。デフォルト0.15。",
"Cooldown (ms)": "クールダウン (ms)",
"Min gap between rumbles. Suppresses steady-state jitter. Default 300.":
"振動間の最小間隔。定常状態のジッターを抑制します。デフォルト300。",
}
37 changes: 37 additions & 0 deletions src/lang/ru.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,41 @@
"Выберите язык, затем перезапустите приложение, чтобы применить его.",
"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.",
"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.",
"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.15.":
"Мин. дельта буста за тик для срабатывания. По умолчанию 0.15.",
"Cooldown (ms)": "Перезарядка (мс)",
"Min gap between rumbles. Suppresses steady-state jitter. Default 300.":
"Мин. пауза между вибрациями. Подавляет дрожание. По умолчанию 300.",
}
37 changes: 37 additions & 0 deletions src/lang/tr.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,41 @@
"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.",
"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.",
"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.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.",
}
37 changes: 37 additions & 0 deletions src/lang/zh.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,41 @@
"选择一种语言,然后重启应用以应用更改。",
"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。",
"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。",
"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.15.":
"每次触发最小增压变化量。默认0.15。",
"Cooldown (ms)": "冷却时间 (ms)",
"Min gap between rumbles. Suppresses steady-state jitter. Default 300.":
"振动间最短间隔。抑制稳态抖动。默认300。",
}
24 changes: 24 additions & 0 deletions src/modules/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -53,6 +61,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
Expand All @@ -78,6 +93,15 @@ 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.15 # 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.
enable_gear_shift: bool = True # buzz on R2
Expand Down
55 changes: 49 additions & 6 deletions src/modules/forzahorizon/effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ def __init__(self):
self._prev_gear = None
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"]
Expand Down Expand Up @@ -139,6 +141,21 @@ 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, now):
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
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)

def abs_pulse(self, t, s):
if not s.enable_abs:
return None
Expand All @@ -155,15 +172,35 @@ 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))
if handbrake:
force += s.handbrake_bonus
return rigid(force)

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 -----------------------------------------------------------
Expand All @@ -185,8 +222,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):
Expand Down Expand Up @@ -254,11 +292,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, now)
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)
30 changes: 30 additions & 0 deletions src/modules/gui/settings_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,29 @@
("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, ""),
("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, ""),
Expand All @@ -56,6 +74,18 @@
("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.15."),
("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, ""),
("gear_shift_amp", "Thump strength", 0, 255, ""),
Expand Down
Loading