Skip to content

Dynamic speed limits

By default the limiter caps the vehicle at whatever speed it was doing when the driver engaged it. With dynamic limits enabled, it instead tracks the posted road speed limit and follows it as the driver moves between zones.

The resource does not decide what the road speed limit is — you tell it, through one function.

Enabling

lua
Config.EnableDynamicLimits = true

Then wire your speed limit source into GetSpeedLimit in client/public.lua.

The bridge

client/public.lua ships ready for the speedlimit resource:

lua
function GetSpeedLimit()
  if GetResourceState('speedlimit') == 'started' then
    -- Returns in MPH
    return exports['speedlimit']:GetCurrentSpeedlimit(), 'mph'
  else
    -- Implement custom logic to get the current speedlimit
    return false, false
  end
end

Contract

Return two values: limit, unit.

ReturnTypeMeaning
limitnumber | falseThe road speed limit at the player's position.
unit'mph' | 'kph' | falseThe unit limit is expressed in.

Conversion to Config.Unit is handled for you — return whatever unit your source natively uses and say which it is.

Return false, false when no limit is known. The limiter then falls back to holding the speed the vehicle was doing when it engaged.

The function is called on the client, on the player's own machine, every Config.CheckInterval milliseconds while the limiter is active.

TIP

Keep this function cheap. It runs on an interval — no server callbacks, no PerformHttpRequest, no scanning every zone in a table. Cache in your own resource and return the cached value.

Examples

From another resource's export

lua
function GetSpeedLimit()
  if GetResourceState('my_zones') ~= 'started' then return false, false end

  return exports.my_zones:getLimitHere(), 'kph'
end

From a statebag your resource maintains

lua
function GetSpeedLimit()
  local limit = LocalPlayer.state.roadSpeedLimit

  if not limit then return false, false end

  return limit, 'mph'
end

Fixed limits by area

lua
local zones = {
  { coords = vec3(-1037.0, -2737.0, 20.0), radius = 400.0, limit = 30 },
  { coords = vec3(1200.0, -1400.0, 35.0), radius = 250.0, limit = 20 }
}

function GetSpeedLimit()
  local coords = GetEntityCoords(cache.ped)

  for i = 1, #zones do
    local zone = zones[i]

    if #(coords - zone.coords) < zone.radius then
      return zone.limit, 'mph'
    end
  end

  return 60, 'mph' -- default road limit
end

Manual adjustment while tracking

While dynamic limits are on, using the increase or decrease key suspends tracking for that vehicle. The driver's chosen number sticks until the limiter is toggled off and on again, at which point tracking resumes.

This is deliberate: a driver who deliberately sets 25 in a 60 zone should not have the resource yank them back up to 60 two seconds later.

Behaviour when a limit is unknown

If GetSpeedLimit returns false, false, or dynamic limits are disabled entirely, the limiter uses the vehicle's speed at the moment of engaging. Config.SpeedTolerance still applies to the resulting target.

Built by duckie210