Skip to content

Shift Bar / Shift Indicator upshift thresholds are unreachable in Forza Horizon 6 #317

Description

@HBbrody

Version: Race Element 2.7.1.4
Game: Forza Horizon 6
HUDs: Shift Bar, Shift Indicator

Description

In Forza, EngineMaxRpm from the Data Out packet is the top of the in-game tachometer scale, not the rev limiter. The engine hits the limiter well below it, so the Early and Redline color states never trigger and Redline Flash never fires.

GetUpShiftPercentages() in ShiftBarOverlay.cs gates the real shift point behind a game whitelist:

Game upshiftSupportedGames = Game.RaceRoom | Game.iRacing;

Outside that set the configured percentages are applied to MaxRpm directly, which in Forza means percentages of a value the engine never reaches. ShiftIndicatorOverlay.UpdateColorDictionary() has the same pattern with an even narrower check (Game.RaceRoom only, and no divide-by-zero guard).

Measurements

Car: Fiat 124 Spider Abarth. Settings: Visible Rpm Amount 4000, Early 93.000, Redline 97.300, Redline Marker On.

From one frame with the Shift RPM HUD enabled:

  • Shift RPM HUD: 7234
  • Shift Bar fill: 78.4% of bar width
  • Redline marker: 94.5% of bar width

Solving GetAdjustedPercentToHideRpm backwards for max rpm, with a 4000 rpm window:

  • from fill: 7234 - (M - 4000) = 0.784 * 4000 gives M = 8098
  • from marker: 0.973 * M - (M - 4000) = 0.945 * 4000 gives M = 8148

Both agree with the tachometer scale, which ends at 8. The bar scales correctly against EngineMaxRpm ~8100. The thresholds land at 7533 and 7881, and the engine limits below both, so the bar stays in the normal color for the whole rev range.

Suggested fix

Forza does not send a shift point. The packet only carries EngineMaxRpm, EngineIdleRpm and CurrentEngineRpm (ForzaUDP/ForzaMotorsportsData.cs), and ForzaDataProvider.cs leaves localCar.Engine.ShiftUpRpm at 0, so adding Forza to the whitelist alone would fail the upshiftRpm > 0 guard.

The limiter can be derived instead: track the peak observed CurrentEngineRpm for the current car and publish it as ShiftUpRpm. The existing branch in GetUpShiftPercentages() then works unchanged, with no new user-facing settings.

Notes on that approach:

  • Reset the peak when EngineMaxRpm changes, which already signals a car change and is what MaxRpmDetectionJob watches for.
  • The peak needs a few seconds of driving to converge. Until then, falling back to the configured percentages is reasonable.

Also

  • ShiftIndicatorOverlay.UpdateColorDictionary() needs the same treatment. The logic is duplicated in both HUDs and could be shared.
  • The Draw Upshift Data panel in ShiftBarOverlay.Render() computes _model.MaxRpm * _config.Upshift.EarlyPercentage / 100d directly instead of calling GetUpShiftPercentages(), so on RaceRoom and iRacing the panel reports different numbers than the bar actually uses.
  • CVT vehicles (for example the Polaris RZR in FH6) hold rpm roughly constant under acceleration and never approach the limiter at all. No threshold setting helps there. Mentioning it only in case it affects how a learned peak is validated.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions