--
-- vehicleControlAddon
-- This is the specialization for vehicleControlAddon
--

vehicleControlAddon = {}

function vehicleControlAddon.formatNumber( n, d, f ) -- number, decimals, (optional) factor 
	if n == nil then 
		return "nil" 
	elseif type( n ) ~= "number" then 
		return "nan"
	end 
	
	if type( d ) ~= "number" then 
		d = 5 
	end 	
	if type( f ) == "number" then 
		n = n * f 
	end 

	local z = 1 
	local q = 1
	local s = 0
	local m = math.floor( n + 0.5 )
	local zd = 10^d
	local qd = 0.4999 * 0.1^d
	
	for i=1,d do 
		if math.abs( n - m ) < qd then 
			break 
		end 
		z = z * 10 
		q = q * 0.1 
		if math.abs( n * z ) > zd then 
			break
		end 
		m = q * math.floor( z * n + 0.5 )
		s = s + 1
	end 

	return string.format( string.format( "%%%d.%df", s + 3, s ), m )
end 

function vehicleControlAddon.listToString( list )
	local result = nil 
	for _,value in pairs( list ) do 
		if result == nil then 
			result = tostring( value )
		else 
			result = result ..', '..tostring( value ) 
		end 
	end 
	
	if result == nil then 
		return "" 
	end 
	return result 
end 

function vehicleControlAddon.trim(s)
  -- from PiL2 20.4
  return (s:gsub("^%s*(.-)%s*$", "%1"))
end

function vehicleControlAddon.stringToList( text, fct )
	local list = {}
	if fct == nil then 
		fct = vehicleControlAddon.trim
	end 
	if text ~= nil then
		local start = 1;
		local splitStart, splitEnd = string.find(text, ",", start, true);
		while splitStart ~= nil do
			table.insert( list, fct( string.sub(text, start, splitStart-1 )))
			start = splitEnd + 1;
			splitStart, splitEnd = string.find(text, ",", start, true);
		end
		table.insert(list, fct( string.sub(text, start )))
	end
	
	return list;
end 		

--********************************
-- getUiScale
--********************************
function vehicleControlAddon.getUiScale()
	local uiScale = 1.0
	if g_gameSettings ~= nil and type( g_gameSettings.uiScale ) == "number" then
		uiScale = g_gameSettings.uiScale
	end
	return uiScale 
end
	
--********************************
-- getText
--********************************
function vehicleControlAddon.getText(id, default)
	if id == nil then
		return "nil";
	end;
	
	if g_i18n:hasText( id ) then
		return g_i18n:getText( id )
	end
	
	if default ~= nil then	
		return default 
	end
	
	return id
end;

--********************************
-- normalizeAngle
--********************************
function vehicleControlAddon.normalizeAngle( angle )
	local normalizedAngle = angle
	while normalizedAngle > math.pi do
		normalizedAngle = normalizedAngle - math.pi - math.pi
	end 
	while normalizedAngle <= -math.pi do
		normalizedAngle = normalizedAngle + math.pi + math.pi
	end
	return normalizedAngle
end

--********************************
-- normalizeAngleCam
--********************************
function vehicleControlAddon.normalizeAngleCam( angle )
	local normalizedAngle = angle
	while normalizedAngle > math.pi + math.pi do
		normalizedAngle = normalizedAngle - math.pi - math.pi
	end 
	while normalizedAngle <= 0 do
		normalizedAngle = normalizedAngle + math.pi + math.pi
	end
	return normalizedAngle
end

--********************************
-- mbClamp
--********************************
function vehicleControlAddon.mbClamp( v, minV, maxV )
	if v == nil then 
		return 
	end 
	if minV ~= nil and v <= minV then 
		return minV 
	end
	if maxV ~= nil and v >= maxV then 
		return maxV 
	end 
	return v 
end


vehicleControlAddon.snapAngles = { 1, 5, 15, 22.5, 45, 90 }
vehicleControlAddon.splitGearRatios = { 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95 }
vehicleControlAddon.factor30pi = 30/math.pi  -- RPM = rotSpeed * vehicleControlAddon.factor30pi
vehicleControlAddon.factorpi30 = math.pi/30  -- rotSpeed = RPM * vehicleControlAddon.factorpi30
vehicleControlAddon.speedRatioOpen       = 1000
vehicleControlAddon.speedRatioClosed0    = -1   -- maxSpeedRatio of diff of diffs
vehicleControlAddon.speedRatioClosed1    = 1.1  -- maxSpeedRatio of diff of wheels
vehicleControlAddon.speedRatioClosed2    = 1.2  -- at least 20% difference 
vehicleControlAddon.distributeTorqueOpen = false  
vehicleControlAddon.minTorqueRatio       = 0.3
vehicleControlAddon.snapRadius           = 10

vehicleControlAddon.colorActive          = { 0.0003, 0.5647, 0.9822, 1 }
vehicleControlAddon.colorInactive        = { 1, 1, 1, 1 }
vehicleControlAddon.colorNormal          = { 1, 1, 1, 1 }
vehicleControlAddon.colorWarning         = { 1, 1, 0, 1 }
vehicleControlAddon.colorError           = { 1, 0, 0, 1 }
vehicleControlAddon.colorBg              = { 0, 0, 0, 0.54 }

vehicleControlAddon.properties = {}
vehicleControlAddon.propertiesIndex = {}

vehicleControlAddon.steeringFalse = "false"
vehicleControlAddon.steeringTrue  = "true"
vehicleControlAddon.steeringSoft  = "soft"

vehicleControlAddon.shiftTimeDefault    = 0
vehicleControlAddon.shiftTimePowershift = 1
vehicleControlAddon.shiftTimeShort      = 2
vehicleControlAddon.shiftTimeMedium     = 3
vehicleControlAddon.shiftTimeLong       = 4

vehicleControlAddon.toolStateLower      = 1
vehicleControlAddon.toolStateActivate   = 2
vehicleControlAddon.toolStateFold       = 3

vehicleControlAddon.DEFAULT_CLUTCH      = 0
vehicleControlAddon.SHUTTLE_CLUTCH      = 1
vehicleControlAddon.ANTI_STALL          = 2
vehicleControlAddon.TURBO_CLUTCH        = 3

vehicleControlAddon.shiftTimes = { 0, 250, 500, 1000 } -- index like vehicleControlAddon.shiftTime...

function vehicleControlAddon.prerequisitesPresent(specializations)
	return true
end

function vehicleControlAddon.createState( name, global, default, propFunc, callback, savegame )
--print(tostring(name)..": "..tostring(global)..", "..tostring(default)..", "..type(propFunc)..", "..type(callback)..", "..tostring(savegame))

	if savegame == nil then savegame = true end 
	if default  == nil then
		if global ~= nil and VCAGlobals[global] ~= nil then 
			default = VCAGlobals[global]
		else 
			default = propFunc.emptyValue
		end 
	end 
	
	table.insert( vehicleControlAddon.propertiesIndex, name )
	vehicleControlAddon.properties[name] = { id       = table.getn(vehicleControlAddon.propertiesIndex),
																					 global   = global,
																					 default  = default,
																					 func     = propFunc,
																					 callback = callback,
																					 savegame = savegame }
end 

function vehicleControlAddon.createStates()
	vehicleControlAddon.createState( "steeringIsOn" , "adaptiveSteering"  , nil  , VCAValueType.string)
	vehicleControlAddon.createState( "peekLeftRight", "peekLeftRight"     , nil  , VCAValueType.bool  )
	vehicleControlAddon.createState( "isForward"    , nil                 , true , VCAValueType.bool, nil, false  )
	vehicleControlAddon.createState( "camRotInside" , "camInsideRotation" , nil  , VCAValueType.int16 )
	vehicleControlAddon.createState( "camRotOutside", "camOutsideRotation",nil  , VCAValueType.int16 )
	vehicleControlAddon.createState( "camRevInside" , nil, vehicleControlAddon.vcaGetDefRevI, VCAValueType.bool  )
	vehicleControlAddon.createState( "camRevOutside", "camRevOutRotation", nil  , VCAValueType.bool  )
	vehicleControlAddon.createState( "warningText"  , nil                , ""   , VCAValueType.string, vehicleControlAddon.vcaOnSetWarningText, false )
	vehicleControlAddon.createState( "limitThrottle", "limitThrottle"    , nil  , VCAValueType.int16 )
	vehicleControlAddon.createState( "snapAngle"    , "snapAngle"        , nil  , VCAValueType.int16 , vehicleControlAddon.vcaOnSetSnapAngle )
	vehicleControlAddon.createState( "snapDistance" , nil                , 0    , VCAValueType.float )
	vehicleControlAddon.createState( "snapOffset"   , nil                , 0    , VCAValueType.float )
	vehicleControlAddon.createState( "snapInvert"   , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "snapEvery90"  , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "snapLeft"     , nil                , 1    , VCAValueType.int16 )
	vehicleControlAddon.createState( "snapRight"    , nil                , 1    , VCAValueType.int16 )
	vehicleControlAddon.createState( "snapIsOn"     , nil                , false, VCAValueType.bool  , vehicleControlAddon.vcaOnSetSnapIsOn, false )
	vehicleControlAddon.createState( "snapDirection", nil                , 0    , VCAValueType.int16 , nil, false )
	vehicleControlAddon.createState( "pathRecording", nil                , false, VCAValueType.bool  , nil, false )
	vehicleControlAddon.createState( "snapPathID"   , nil                , 0    , VCAValueType.int16 )
	vehicleControlAddon.createState( "drawHud"      , "drawHud"          , nil  , VCAValueType.bool  )
	vehicleControlAddon.createState( "inchingIsOn"  , nil                , false, VCAValueType.bool  , nil, false )
	vehicleControlAddon.createState( "noAutoRotBack", nil                , false, VCAValueType.bool  , nil, false )
	vehicleControlAddon.createState( "noARBToggle"  , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "brakeForce"   , "brakeForceFactor" , nil  , VCAValueType.float )
	vehicleControlAddon.createState( "autoShift"    , nil                , false, VCAValueType.bool  ) --, vehicleControlAddon.vcaOnSetAutoShift )
	vehicleControlAddon.createState( "handbrake"    , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "speedLimiter" , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "ksIsOn"       , nil                , false, VCAValueType.bool  , nil, false ) --, vehicleControlAddon.vcaOnSetKSIsOn )
	vehicleControlAddon.createState( "ksIsActive"   , nil                , false, VCAValueType.bool  , nil, false ) --, vehicleControlAddon.vcaOnSetKSIsOn )
	vehicleControlAddon.createState( "keepSpeed"    , nil                , 0    , VCAValueType.float , nil, false )
	vehicleControlAddon.createState( "ksToggle"     , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "ccSpeed2"     , nil                , 10   , VCAValueType.float )
	vehicleControlAddon.createState( "ccSpeed3"     , nil                , 15   , VCAValueType.float )
	vehicleControlAddon.createState( "lastSnapAngle", nil                , 10   , VCAValueType.float ) --, vehicleControlAddon.vcaOnSetLastSnapAngle ) -- value should be between -pi and pi !!!
	vehicleControlAddon.createState( "lastSnapPosX" , nil                , 0    , VCAValueType.float )
	vehicleControlAddon.createState( "lastSnapPosZ" , nil                , 0    , VCAValueType.float )
	vehicleControlAddon.createState( "isEnteredMP"  , nil                , false, VCAValueType.bool  , nil, false )
	vehicleControlAddon.createState( "isBlocked"    , nil                , false, VCAValueType.bool  , nil, false )
	vehicleControlAddon.createState( "snapDraw"     , nil                , 1    , VCAValueType.int16 )
	vehicleControlAddon.createState( "workAreaDraw" , nil                , 0    , VCAValueType.int16 )
	vehicleControlAddon.createState( "snapFactor"   , nil                , 0    , VCAValueType.float , nil, false )
	vehicleControlAddon.createState( "hiredWorker2" , "hiredWorker2"     , nil  , VCAValueType.bool  )
	vehicleControlAddon.createState( "rotSpeedOut"  , "rotSpeedOut"      , nil  , VCAValueType.float )
	vehicleControlAddon.createState( "rotSpeedIn"   , "rotSpeedIn"       , nil  , VCAValueType.float )
	vehicleControlAddon.createState( "antiSlip"     , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "diffLockFront", nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "diffLockAWD"  , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "diffLockBack" , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "diffManual"   , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "diffLockSwap" , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "diffFrontAdv" , nil                , false, VCAValueType.bool  )
	vehicleControlAddon.createState( "idleThrottle" , "idleThrottle"     , nil  , VCAValueType.bool  )
	vehicleControlAddon.createState( "clutchMode"   , nil                , 0    , VCAValueType.int16 )
	vehicleControlAddon.createState( "hasGearsAuto" , nil                , false, VCAValueType.bool  , nil, false )
	vehicleControlAddon.createState( "hasGearsIdle" , nil                , false, VCAValueType.bool  , nil, false )
	vehicleControlAddon.createState( "handThrottle" , nil                , 0    , VCAValueType.float , nil, false )
	vehicleControlAddon.createState( "minGearSpeed" , nil                , 0    , VCAValueType.float , nil, false )
	vehicleControlAddon.createState( "maxGearSpeed" , nil                , 0    , VCAValueType.float , nil, false )
	vehicleControlAddon.createState( "gearRatio"    , nil                , 0    , VCAValueType.float , nil, false )
																																			 
	vehicleControlAddon.createState( "splitGears"   , nil                , 0    , VCAValueType.int16 , vehicleControlAddon.vcaOnSetSplitGears )
	vehicleControlAddon.createState( "splitGearRatio",nil                , 7    , VCAValueType.int16 , vehicleControlAddon.vcaOnSetSplitGearRatio )
	vehicleControlAddon.createState( "gearSplitGear", nil                , 1    , VCAValueType.int16 , vehicleControlAddon.vcaOnSetGearSplitGear, false )
																																			 
	vehicleControlAddon.createState( "gearShiftTime", nil                , vehicleControlAddon.shiftTimeDefault, VCAValueType.int16 )
	vehicleControlAddon.createState( "grpShiftTime" , nil                , vehicleControlAddon.shiftTimeDefault, VCAValueType.int16 )

end 



function vehicleControlAddon.initSpecialization()
	vehicleControlAddon.initSpecializationDone = true 

	local schemaSavegame = Vehicle.xmlSchemaSavegame
	if schemaSavegame then 
		for name, prop in pairs( vehicleControlAddon.properties ) do 
			if prop.savegame then 
				schemaSavegame:register( prop.func.valueType, "vehicles.vehicle(?)." .. g_vehicleControlAddon.vcaSpecName .. "#".. name, "VehicleControlAddon: ".. name)
			end 
		end 
	end 
end 

function vehicleControlAddon.registerEventListeners(vehicleType)
	SpecializationUtil.registerEventListener(vehicleType, "onLoad",                 vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onPostLoad",             vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onPreUpdate",            vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onUpdate",               vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick",           vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onPostUpdate",           vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onDraw",                 vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onEnterVehicle",         vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle",         vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onReadStream",           vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onWriteStream",          vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream",     vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream",    vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "saveToXMLFile",          vehicleControlAddon)
	SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", vehicleControlAddon)
end 

function vehicleControlAddon.registerOverwrittenFunctions(vehicleType)
end

function vehicleControlAddon:mpDebugPrint( ... )
	if VCAGlobals.debugPrint or self == nil or not ( self.isClient ) then 
		print( ... )
	end 
end

local function debugFormat( ... )
	if VCAGlobals.debugPrint then 
		return string.format( ... )
	end 
	return ""
end 

function vehicleControlAddon:vcaIsValidCam( index )
	local i = Utils.getNoNil( index, self.spec_enterable.camIndex )
	
	if      self.spec_enterable            ~= nil 
			and self.spec_enterable.cameras    ~= nil 
			and i ~= nil 
			and self.spec_enterable.cameras[i] ~= nil 
			and self.spec_enterable.cameras[i].vehicle == self
			and self.spec_enterable.cameras[i].isRotatable then
		return true
	end
	
	return false
end

function vehicleControlAddon:vcaIsNonDefaultProp( name )
	if self.spec_vca == nil or self.spec_vca[name] == nil then 
		return false 
	end
	local d = self:vcaGetDefault( name )
	if     d == nil then 
		return false 
	elseif type( self.spec_vca[name] ) == "number" and type( d ) == "number" then 
		if math.abs( self.spec_vca[name] - d ) < 1e-4 then 
			return false 
		end 
		return true 
	elseif self.spec_vca[name] == d then 
		return false 
	end 
	return true  
end 

function vehicleControlAddon:vcaSetState( name, value, noEventSend )
	if self.spec_vca == nil then 
		return
	end 
	if value == nil then 
		return 
	end 
	local c, f
	local prop = vehicleControlAddon.properties[name]
	if prop == nil then 
		c = function( a, b ) return a==b end 
	else 
		c = prop.func.compare
		f = prop.callback
	end 
	if self.spec_vca[name] ~= nil and c( self.spec_vca[name], value ) then 
		return 
	end 
	local o = self.spec_vca[name]
	 
	if type( f ) == "function" then 
		f( self, o, value, noEventSend )
	else 
		self.spec_vca[name] = value
	end 
	
	if     noEventSend then 
	elseif not ( self.spec_vca.isInitialized )
			or vehicleControlAddon.properties[name] == nil
			or value == nil then 
	-- nothing 
		print("VCA MP error, name: "..tostring(name)..", value: "..tostring(value))
	elseif g_server ~= nil then
		g_server:broadcastEvent(vehicleControlAddonEvent.new(self,name,self.spec_vca[name]), nil, nil, self)
	else
		g_client:getServerConnection():sendEvent(vehicleControlAddonEvent.new(self,name,self.spec_vca[name]))
	end
end 

function vehicleControlAddon:vcaSetNewDefault( global, old, new, noEventSend )
	if self.spec_vca ~= nil and self.spec_vca.isInitialized then 
		for vn, vp in pairs( vehicleControlAddon.properties ) do
			if vp.global ~= nil and vp.global == global and self.spec_vca[vn] == old then 
				vcaDebugPrint( "Info: VCA new default value: name: "..tostring(global)..", old: "..tostring(old)..", new: "..tostring(new))
				self:vcaSetState( vn, new, noEventSend )		
			end 
		end 
	end 
end 

function vehicleControlAddon:vcaGetDefault( name )
	local prop = vehicleControlAddon.properties[name]
	if prop == nil or ( prop.default == nil and prop.global == nil ) then 
		return 
	end 
	if     prop.global ~= nil then 
		return VCAGlobals[prop.global]
	elseif type( prop.default ) == "function" then 
		return prop.default( self )
	end 
	return prop.default 
end 

function vehicleControlAddon:vcaGetState( name, default )
	if self.spec_vca == nil then 
		return
	end 
	if default and self.spec_vca[name] == nil then 
		return self:vcaGetDefault( name )
	end 
	return self.spec_vca[name]
end
	
function vehicleControlAddon:onLoad(savegame)
	
	self.spec_vca               = {}
	
	self.vcaNewState            = vehicleControlAddon.vcaNewState	
	self.vcaSetState            = vehicleControlAddon.vcaSetState
	self.vcaSetNewDefault       = vehicleControlAddon.vcaSetNewDefault
	self.vcaGetDefault          = vehicleControlAddon.vcaGetDefault
	self.vcaGetState            = vehicleControlAddon.vcaGetState
	self.vcaIsValidCam          = vehicleControlAddon.vcaIsValidCam
	self.vcaIsActive            = vehicleControlAddon.vcaIsActive
	self.vcaIsNonDefaultProp    = vehicleControlAddon.vcaIsNonDefaultProp
	self.vcaGetSteeringNode     = vehicleControlAddon.vcaGetSteeringNode
	self.vcaIsVehicleControlledByPlayer = vehicleControlAddon.vcaIsVehicleControlledByPlayer
	self.vcaGetShuttleCtrl      = vehicleControlAddon.vcaGetShuttleCtrl
	self.vcaGetAutoHold         = vehicleControlAddon.vcaGetAutoHold
	self.vcaGetIsReverse        = vehicleControlAddon.vcaGetIsReverse
	self.vcaGetDiffState        = vehicleControlAddon.vcaGetDiffState
	self.vcaHasDiffFront        = vehicleControlAddon.vcaHasDiffFront
	self.vcaHasDiffAWD          = vehicleControlAddon.vcaHasDiffAWD 
	self.vcaHasDiffBack         = vehicleControlAddon.vcaHasDiffBack 
	self.vcaSetSnapFactor       = vehicleControlAddon.vcaSetSnapFactor
	self.vcaGetCurrentSnapAngle = vehicleControlAddon.vcaGetCurrentSnapAngle
	self.vcaGetSnapWillInvert   = vehicleControlAddon.vcaGetSnapWillInvert
	self.vcaGetSnapIsInverted   = vehicleControlAddon.vcaGetSnapIsInverted
	self.vcaGetSnapDistance     = vehicleControlAddon.vcaGetSnapDistance
	self.vcaSetCruiseSpeed      = vehicleControlAddon.vcaSetCruiseSpeed
	self.vcaSpeedToString       = vehicleControlAddon.vcaSpeedToString
	self.vcaSetToolStateRec     = vehicleControlAddon.vcaSetToolStateRec
	self.vcaSetToolStateNew     = vehicleControlAddon.vcaSetToolStateNew
	
-- called by FS22_HeadlandManagement
	self.vcaSnapReverseLeft     = vehicleControlAddon.vcaSnapReverseLeft
	self.vcaSnapReverseRight    = vehicleControlAddon.vcaSnapReverseRight
-- called by FS22_HeadlandManagement

	for name,prop in pairs( vehicleControlAddon.properties ) do 
		self:vcaSetState( name, self:vcaGetDefault( name ), true )
	end 
	
	self.spec_vca.movingDir     = 0
	self.spec_vca.lastFactor    = 0
	self.spec_vca.warningTimer  = 0
	self.spec_vca.tickDt        = 16.667
	self.spec_vca.isEntered     = false
	self.spec_vca.keepCamRot    = false 
	self.spec_vca.kRToggleIn    = false 
	self.spec_vca.kRToggleOut   = false 
  self.spec_vca.snapDisabled  = false 
  self.spec_vca.snapPossible  = false 
  self.spec_vca.gpsProSeed    = false 
	self.spec_vca.snapNoDisableTimer = 0

	self.spec_vca.maxWheelSlip  = 0
	self.spec_vca.maxBrakePedal = 1
	self.spec_vca.maxThrottle   = 1
	self.spec_vca.maxThrottleT  = 0
	
	self.spec_vca.lasWheelSlip  = 0
	
	self.spec_vca.keepRotPressed   = false 
	self.spec_vca.inchingPressed   = false 
	self.spec_vca.keepSpeedPressed = false 		
	
	self.spec_vca.lastGearShiftTime = vehicleControlAddon.shiftTimeDefault
	self.spec_vca.lastGrpShiftTime  = vehicleControlAddon.shiftTimeDefault

	if self.isClient then 
		if vehicleControlAddon.snapOnSample == nil then 
			local fileName = Utils.getFilename( "ogg/GPS_on.ogg", g_vehicleControlAddon.vcaDirectory)
			vehicleControlAddon.snapOnSample = createSample("AutoSteerOnSound")
			loadSample(vehicleControlAddon.snapOnSample, fileName, false)
		end 
		
		if vehicleControlAddon.snapOffSample == nil then 
			local fileName = Utils.getFilename( "ogg/GPS_off.ogg", g_vehicleControlAddon.vcaDirectory)
			vehicleControlAddon.snapOffSample = createSample("AutoSteerOffSound")
			loadSample(vehicleControlAddon.snapOffSample, fileName, false)
		end 

		if vehicleControlAddon.ovDiffLockFront == nil then
			vehicleControlAddon.ovDiffLockFront  = createImageOverlay( Utils.getFilename( "dds/diff_front.dds",     g_vehicleControlAddon.vcaDirectory))
			vehicleControlAddon.ovDiffLockMid    = createImageOverlay( Utils.getFilename( "dds/diff_middle.dds",    g_vehicleControlAddon.vcaDirectory))
			vehicleControlAddon.ovDiffLockBack   = createImageOverlay( Utils.getFilename( "dds/diff_back.dds",      g_vehicleControlAddon.vcaDirectory))
			vehicleControlAddon.ovDiffLockFL     = createImageOverlay( Utils.getFilename( "dds/diff_wheels_fl.dds", g_vehicleControlAddon.vcaDirectory))
			vehicleControlAddon.ovDiffLockFR     = createImageOverlay( Utils.getFilename( "dds/diff_wheels_fr.dds", g_vehicleControlAddon.vcaDirectory))
			vehicleControlAddon.ovDiffLockRL     = createImageOverlay( Utils.getFilename( "dds/diff_wheels_rl.dds", g_vehicleControlAddon.vcaDirectory))
			vehicleControlAddon.ovDiffLockRR     = createImageOverlay( Utils.getFilename( "dds/diff_wheels_rr.dds", g_vehicleControlAddon.vcaDirectory))
			vehicleControlAddon.ovDiffLockBg     = createImageOverlay( Utils.getFilename( "dds/diff_bg.dds",        g_vehicleControlAddon.vcaDirectory))
			local r, g, b, a = unpack(vehicleControlAddon.colorBg)
			setOverlayColor( vehicleControlAddon.ovDiffLockBg, r, g, b, a )
			vehicleControlAddon.ovGearSpeedBg    = createImageOverlay( Utils.getFilename( "dds/gear_bg.dds",      g_vehicleControlAddon.vcaDirectory))
			setOverlayColor( vehicleControlAddon.ovGearSpeedBg, r, g, b, a )
			setOverlayUVs( vehicleControlAddon.ovGearSpeedBg, unpack( GuiUtils.getUVs( { 0, 0, 1024, 128 } ) ) )
			vehicleControlAddon.ovExtraTextBg    = createImageOverlay( Utils.getFilename( "dds/gear_bg.dds",      g_vehicleControlAddon.vcaDirectory))
			setOverlayColor( vehicleControlAddon.ovExtraTextBg, r, g, b, a )
			setOverlayUVs( vehicleControlAddon.ovExtraTextBg, unpack( GuiUtils.getUVs( { 0, 129, 1024, 256 } ) ) )
		end 
	end 
end

function vehicleControlAddon:onPostLoad(savegame)
	if self.spec_vca == nil then return end 

	if savegame ~= nil then
		if not ( vehicleControlAddon.initSpecializationDone ) then 
			print("Warning: calling vehicleControlAddon.initSpecialization during load")
			vehicleControlAddon.initSpecialization() 
		end 
		for name,prop in pairs( vehicleControlAddon.properties ) do 
			if prop.savegame then 
				local v = savegame.xmlFile:getValue(savegame.key .. "." .. g_vehicleControlAddon.vcaSpecName .. "#" .. name )
				if v ~= nil then 
					self:vcaSetState( name, v, true )
				end 
			end 
		end 
	end
	
	self.spec_vca.isForward = true  
	
	self.spec_vca.diffHasF = false 
	self.spec_vca.diffHasM = false 
	self.spec_vca.diffHas2 = false 
	self.spec_vca.diffHasB = false 
	self.spec_vca.wheelIndexFL = -1
	self.spec_vca.wheelIndexFR = -1
	self.spec_vca.wheelIndexRL = -1
	self.spec_vca.wheelIndexRR = -1
	
	if type( self.functionStatus ) == "function" and self:functionStatus("differential") then 
	-- TSX diffs ...
	elseif true then 
	-- disable for now
	elseif self.spec_crawlers ~= nil and #self.spec_crawlers.crawlers > 0 then 
	-- crawlers => not support as visible wheel will not turn in most cases
	elseif ( self.spec_articulatedAxis ~= nil and self.spec_articulatedAxis.componentJoint ~= nil )
			or ( self.numComponents == 2 and self.spec_crawlers ~= nil and #self.spec_crawlers.crawlers > 0 ) then 
	-- articulated axis 
		local spec       = self.spec_motorized
		local specWheels = self.spec_wheels
		local noPattern  = false 
		local rootNode1, rootNode2 
				
		if self.spec_articulatedAxis ~= nil and self.spec_articulatedAxis.componentJoint ~= nil then 
			local componentJoint = self.spec_articulatedAxis.componentJoint
			rootNode1 = self.components[componentJoint.componentIndices[1]].node
			rootNode2 = self.components[componentJoint.componentIndices[2]].node
		else 
		-- let's hope that the 2nd node is the front axle 
			rootNode1 = self.components[2].node 
			rootNode2 = self.components[1].node 
		end 
		
		local wx, wy, wz, lz1, lz2
		wx, wy, wz = getWorldTranslation(rootNode1)
		_,_, lz1 = worldToLocal(self:vcaGetSteeringNode(), wx, wy, wz)
		wx, wy, wz = getWorldTranslation(rootNode2)
		_,_, lz2 = worldToLocal(self:vcaGetSteeringNode(), wx, wy, wz)
		
		if lz1 > lz2 then 
			rootNode1, rootNode2 = rootNode2, rootNode1
		end 
		
		local function checkWheelsOfDiff( rootNode1, rootNode2, index, isWheel, depth )
			local d2 = 2 
			if type( depth ) == 'number' then 
				if depth > #spec.differentials then 
					print("VCA: found recursion in differential definition")
					noPattern = true 
					return false, false, false 
				end 
				d2 = depth + 1
			end 
			if isWheel then 
				local wheel    = self:getWheelFromWheelIndex( index )
				local rootNode = self:getParentComponent(wheel.repr)
				if     rootNode == rootNode1 then
					return true, false, true  
				elseif rootNode == rootNode2 then 
					return false, true, true 
				else 
					return false, false, false 
				end 
			else 				
				local diff = spec.differentials[index+1] 				
				local c11, c21, a1 = checkWheelsOfDiff( rootNode1, rootNode2, diff.diffIndex1, diff.diffIndex1IsWheel, d2 )
				local c12, c22, a2 = checkWheelsOfDiff( rootNode1, rootNode2, diff.diffIndex2, diff.diffIndex2IsWheel, d2 )				
				return c11 or c12, c21 or c22, a1 and a2 
			end 
		end 
		
		for k,differential in pairs(spec.differentials) do
			local c1, c2, all = checkWheelsOfDiff( rootNode1, rootNode2, k-1, false )
			if all and ( c1 or c2 ) then  

				local lx1, lx2 = nil, nil 
				if differential.diffIndex1IsWheel and differential.diffIndex2IsWheel then 
					local wx, wy, wz
					wx, wy, wz = getWorldTranslation( self:getWheelFromWheelIndex( differential.diffIndex1 ).node )
					lx1,_,_ = worldToLocal(self:vcaGetSteeringNode(), wx, wy, wz)
					wx, wy, wz = getWorldTranslation( self:getWheelFromWheelIndex( differential.diffIndex2 ).node )
					lx2,_,_ = worldToLocal(self:vcaGetSteeringNode(), wx, wy, wz)
				end 

				if c1 and c2 then 
					vcaDebugPrint("Diff. "..tostring(k-1).." is in the middle")
					differential.vcaMode = 'M' 
					self.spec_vca.diffHasM     = true 
				elseif c1 then 
					vcaDebugPrint("Diff. "..tostring(k-1).." is at the front")
					differential.vcaMode = 'F' 

					if self.spec_vca.diffHasF or lx1 == nil or lx2 == nil then 
						self.spec_vca.wheelIndexFL = -1
						self.spec_vca.wheelIndexFR = -1
					elseif lx1 > lx2 then 
						self.spec_vca.wheelIndexFL = differential.diffIndex1
						self.spec_vca.wheelIndexFR = differential.diffIndex2
					else
						self.spec_vca.wheelIndexFL = differential.diffIndex2
						self.spec_vca.wheelIndexFR = differential.diffIndex1 
					end 

					self.spec_vca.diffHasF     = true 
				else --if c2 then; is always true 
					vcaDebugPrint("Diff. "..tostring(k-1).." is at the back")
					differential.vcaMode = 'B' 

					if self.spec_vca.diffHasB or lx1 == nil or lx2 == nil then 
						self.spec_vca.wheelIndexRL = -1
						self.spec_vca.wheelIndexRR = -1
					elseif lx1 > lx2 then 
						self.spec_vca.wheelIndexRL = differential.diffIndex1
						self.spec_vca.wheelIndexRR = differential.diffIndex2
					else
						self.spec_vca.wheelIndexRL = differential.diffIndex2
						self.spec_vca.wheelIndexRR = differential.diffIndex1 
					end 

					self.spec_vca.diffHasB     = true 
				end 
			else 
				vcaDebugPrint("Diff. "..tostring(k-1).." is mixed: "..tostring(c1)..", "..tostring(c2)..", "..tostring(all))
				noPattern = true 
			end 
			differential.vcaTorqueRatio   = differential.torqueRatio
			differential.vcaMaxSpeedRatio = differential.maxSpeedRatio
			differential.vcaIndex         = k-1
		end 
		
		if noPattern then 
			self.spec_vca.diffHasF = false 
			self.spec_vca.diffHasM = false 
			self.spec_vca.diffHasB = false 
		end 
	else 
	-- hopefully, a normal vehicle 
		local spec = self.spec_motorized
		local noPattern  = false 
				
		local function getMinMaxRotSpeed( index, isWheel, depth ) 
			local d2 = 2 
			if type( depth ) == 'number' then 
				if depth > #spec.differentials then 
					print("VCA: found recursion in differential definition")
					noPattern = true 
					return 0, 0
				end 
				d2 = depth + 1
			end 
			if isWheel then 
				local wheel = self:getWheelFromWheelIndex( index )
				if not wheel.showSteeringAngle then 
					return 0, 0
				elseif wheel.rotSpeed == nil then 
					return 0, 0
				else 
					local r = math.abs( wheel.rotSpeed ) 
					return r, r 
				end 
			else 
				local diff = spec.differentials[index+1] 
				
				local rMin1, rMax1 = getMinMaxRotSpeed( diff.diffIndex1, diff.diffIndex1IsWheel, d2 )
				local rMin2, rMax2 = getMinMaxRotSpeed( diff.diffIndex2, diff.diffIndex2IsWheel, d2 )
				
				return math.min( rMin1, rMin2 ), math.max( rMax1, rMax2 )
			end 
		end 
		
		for k,differential in pairs(spec.differentials) do
			local rMin1, rMax1 = getMinMaxRotSpeed( k-1, false )
			
			local lx1, lx2 = nil, nil 
			if differential.diffIndex1IsWheel and differential.diffIndex2IsWheel then 
				local wx, wy, wz
				wx, wy, wz = getWorldTranslation( self:getWheelFromWheelIndex( differential.diffIndex1 ).node )
				lx1,_,_ = worldToLocal(self:vcaGetSteeringNode(), wx, wy, wz)
				wx, wy, wz = getWorldTranslation( self:getWheelFromWheelIndex( differential.diffIndex2 ).node )
				lx2,_,_ = worldToLocal(self:vcaGetSteeringNode(), wx, wy, wz)
			end 
			
			if    rMax1 < 0.1 then 
				differential.vcaMode = 'B' -- back axle, no steering

				if self.spec_vca.diffHasB or lx1 == nil or lx2 == nil then 
					self.spec_vca.wheelIndexRL = -1
					self.spec_vca.wheelIndexRR = -1
				elseif lx1 > lx2 then 
					self.spec_vca.wheelIndexRL = differential.diffIndex1
					self.spec_vca.wheelIndexRR = differential.diffIndex2
				else
					self.spec_vca.wheelIndexRL = differential.diffIndex2
					self.spec_vca.wheelIndexRR = differential.diffIndex1 
				end 

				self.spec_vca.diffHasB = true 

			elseif rMin1 > 0.1 then 
				differential.vcaMode = 'F' -- front axle, with steering

				if self.spec_vca.diffHasF or lx1 == nil or lx2 == nil then 
					self.spec_vca.wheelIndexFL = -1
					self.spec_vca.wheelIndexFR = -1
				elseif lx1 > lx2 then 
					self.spec_vca.wheelIndexFL = differential.diffIndex1
					self.spec_vca.wheelIndexFR = differential.diffIndex2
				else
					self.spec_vca.wheelIndexFL = differential.diffIndex2
					self.spec_vca.wheelIndexFR = differential.diffIndex1 
				end 

				self.spec_vca.diffHasF = true 

			elseif not differential.diffIndex1IsWheel and not differential.diffIndex2IsWheel then 
				differential.vcaMode = 'M' -- mid differential, between front and back
				self.spec_vca.diffHasM = true 
				
				local rMin1, rMax1 = getMinMaxRotSpeed( differential.diffIndex1, differential.diffIndex1IsWheel )
				local rMin2, rMax2 = getMinMaxRotSpeed( differential.diffIndex2, differential.diffIndex2IsWheel )
				if     rMin1 > 0.1 and rMax2 < 0.1 then 
					differential.vcaTorqueRatioOpen = 0
					self.spec_vca.diffHas2 = true 
				elseif rMin2 > 0.1 and rMax1 < 0.1 then 
					differential.vcaTorqueRatioOpen = 1 
					self.spec_vca.diffHas2 = true 
				end 
			else 
				differential.vcaMode = '-' -- bad 
				noPattern = true 
			end 
			differential.vcaTorqueRatio   = differential.torqueRatio
			differential.vcaMaxSpeedRatio = differential.maxSpeedRatio
			differential.vcaIndex         = k-1
		end 
		
		if noPattern then 
			self.spec_vca.diffHasF = false 
			self.spec_vca.diffHasM = false 
			self.spec_vca.diffHasB = false 
			self.spec_vca.diffHas2 = false 
		end 
	end
	
	self.spec_vca.isInitialized = true 				
end 

function vehicleControlAddon:saveToXMLFile(xmlFile, key, usedModNames)
	if not ( vehicleControlAddon.initSpecializationDone ) then 
		print("Warning: calling vehicleControlAddon.initSpecialization during save")
		vehicleControlAddon.initSpecialization() 
	end 

	for name,prop in pairs( vehicleControlAddon.properties ) do 
		if prop.savegame and self:vcaIsNonDefaultProp( name ) then 
			xmlFile:setValue(key .. "#" .. name, self.spec_vca[name])
		end 
	end 
end 

function vehicleControlAddon.isMPMaster()
	if g_vehicleControlAddon.isMP then 
		return g_currentMission.isMasterUser 
	end 
	return g_server ~= nil 
end 

function vehicleControlAddon:vcaSnapReverseLeft()
-- called by FS22_HeadlandManagement
	self:vcaSetState( "snapIsOn", true )
	if     self.spec_vca.snapDirection == 1 then
		self:vcaSetState( "snapDirection", 3 )
	elseif self.spec_vca.snapDirection == 2 then
		self:vcaSetState( "snapDirection", 4 )
	elseif self.spec_vca.snapDirection == 3 then
		self:vcaSetState( "snapDirection", 1 )
	elseif self.spec_vca.snapDirection == 4 then
		self:vcaSetState( "snapDirection", 2 )
	end 
	local l,r = self.spec_vca.snapLeft, self.spec_vca.snapRight 
	self:vcaSetState( "snapFactor", -self.spec_vca.snapFactor + l )
	self:vcaSetState( "snapLeft", r )
	self:vcaSetState( "snapRight", l )
	self.spec_vca.snapPosTimer = math.max( Utils.getNoNil( self.spec_vca.snapPosTimer , 0 ), 3000 )
	self.spec_vca.snapNoDisableTimer = g_currentMission.time + 2000
end 

function vehicleControlAddon:vcaSnapReverseRight()
-- called by FS22_HeadlandManagement
	self:vcaSetState( "snapIsOn", true )
	if     self.spec_vca.snapDirection == 1 then
		self:vcaSetState( "snapDirection", 3 )
	elseif self.spec_vca.snapDirection == 2 then
		self:vcaSetState( "snapDirection", 4 )
	elseif self.spec_vca.snapDirection == 3 then
		self:vcaSetState( "snapDirection", 1 )
	elseif self.spec_vca.snapDirection == 4 then
		self:vcaSetState( "snapDirection", 2 )
	end 
	local l,r = self.spec_vca.snapLeft, self.spec_vca.snapRight 
	self:vcaSetState( "snapFactor", -self.spec_vca.snapFactor - r )
	self:vcaSetState( "snapLeft", r )
	self:vcaSetState( "snapRight", l )
	self.spec_vca.snapPosTimer = math.max( Utils.getNoNil( self.spec_vca.snapPosTimer , 0 ), 3000 )
	self.spec_vca.snapNoDisableTimer = g_currentMission.time + 2000
end 

function vehicleControlAddon:onRegisterActionEvents(isSelected, isOnActiveVehicle)
	self.spec_vca.activeActionEvents = nil 
	if self.spec_vca == nil then return end 
	
	if self.isClient and self:getIsActiveForInput(true, true) then
		if self.spec_vca.actionEvents == nil then 
			self.spec_vca.actionEvents = {}
		else	
			self:clearActionEventsTable( self.spec_vca.actionEvents )
		end 
		self.spec_vca.activeActionEvents = {}
		
		for _,actionName in pairs({ "vcaCamera",
																"vcaDiffManual",
																"vcaMotorMod",
																"vcaClutchMod",
                            --	"vcaGearSplit", 
                                "vcaSNAPWIDTH",  
                                
                                "vcaUP",        
                                "vcaDOWN",      
                                "vcaLEFT",      
                                "vcaRIGHT",     
                                "vcaSnapUP",        
                                "vcaSnapDOWN",      
                                "vcaSnapLEFT",       
                                "vcaSnapRIGHT", 
																"vcaNO_ARB",
																"vcaINCHING",
																"vcaKEEPROT",
																"vcaKEEPROT2",
																"vcaKEEPSPEED",
																"vcaKEEPSPEED2",
																"vcaKSAXIS",
																"vcaSWAPSPEED",
                                "vcaSNAP",
                                "vcaSNAP_A_B",
                                "vcaSNAPPREV",
                                "vcaSNAPCONT",
                                "vcaSNAPNEXT",
																"vcaSNAPLEFT",
																"vcaSNAPRIGHT",
                                "vcaSNAPRESET",
                                "vcaSNAPDIST",
																"vcaDiffLockF",
																"vcaDiffLockM",
																"vcaDiffLockB",
																"vcaHandRpm",
																"vcaLowerF",
																"vcaLowerB",
																"vcaFoldF",
																"vcaFoldB",
																"vcaActivateF",
																"vcaActivateB",
																"vcaHandbrake",
																"vcaLimitSpeed",
																"vcaAutoShift",
																"vcaWORKAREA",
																"vcaSNAPPATH",
																"vcaGearSplitUp",
																"vcaGearSplitDown",
															}) do
																
			local addThis = InputAction[actionName] ~= nil   
			
			if actionName == "vcaGLOBALS" then 
				addThis = vehicleControlAddon.isMPMaster()
			end 
			
			if      addThis 
					and ( isOnActiveVehicle 
						or  actionName == "vcaUP"
						or  actionName == "vcaDOWN"
						or  actionName == "vcaLEFT"
						or  actionName == "vcaRIGHT"
						or  actionName == "vcaKEEPROT"
						or  actionName == "vcaSWAPSPEED") then 
				-- above actions are still active for hired worker
				local triggerKeyUp, triggerKeyDown, triggerAlways, isActive = false, true, false, true 
				if     actionName == "vcaUP"
						or actionName == "vcaDOWN"
						or actionName == "vcaLEFT"
						or actionName == "vcaRIGHT" 
						or actionName == "vcaNO_ARB"
						then 
					triggerKeyUp   = true 
				elseif actionName == "vcaKEEPSPEED" 
						or actionName == "vcaKEEPROT"
						or actionName == "vcaINCHING"
						or actionName == "vcaSnapDOWN"
						or actionName == "vcaSnapUP"
						or actionName == "vcaSnapLEFT"
						or actionName == "vcaSnapRIGHT"
						then 
					triggerKeyUp   = true 
					triggerKeyDown = false 
					triggerAlways  = true 
				elseif actionName == "vcaHandRpm" 
						or actionName == "vcaKSAXIS"
						then 
					triggerAlways  = true 
				end 
				
				if     actionName == "vcaSNAP"
						or actionName == "vcaSNAP_A_B"
						or actionName == "vcaSnapUP"     
						or actionName == "vcaSnapDOWN"     
						or actionName == "vcaSnapLEFT"    
						or actionName == "vcaSnapRIGHT"
						or actionName == "vcaSNAPPREV"
						or actionName == "vcaSNAPCONT"
						or actionName == "vcaSNAPNEXT"
						or actionName == "vcaSNAPLEFT"
						or actionName == "vcaSNAPRIGHT"
						or actionName == "vcaSNAPRESET"
						or actionName == "vcaSNAPDIST" 
						or actionName == "vcaKSAXIS"
						or actionName == "vcaDiffManual"
						or actionName == "vcaDiffLockF"
						or actionName == "vcaDiffLockM"
						or actionName == "vcaDiffLockB"
						then 
					isActive = false 
				end 
				
				local _, eventName = self:addActionEvent(self.spec_vca.actionEvents, InputAction[actionName], self, vehicleControlAddon.actionCallback, triggerKeyUp, triggerKeyDown, triggerAlways, isActive, nil);

				if not isActive then 
					self.spec_vca.activeActionEvents[actionName] = false 
				end 
				
				if      g_inputBinding                   ~= nil 
						and g_inputBinding.events            ~= nil 
						and g_inputBinding.events[eventName] ~= nil
						then 
					if     actionName == "vcaCamera"
							or actionName == "vcaDiffManual"
							or actionName == "vcaMotorMod"
							or actionName == "vcaClutchMod"
							or actionName == "vcaGearSplit" 
							or actionName == "vcaAutoShift"
							then 
						if isSelected then
							g_inputBinding.events[eventName].displayPriority = 1
						elseif  isOnActiveVehicle then
							g_inputBinding.events[eventName].displayPriority = 4
						end
					elseif actionName == "vcaSNAP_A_B" then
						g_inputBinding.events[eventName].displayPriority = 3
					end 
				end
			end
		end
		
	--vehicleControlAddon.updateActionEvents(self)
	end
end

function vehicleControlAddon:updateActionEvents()
	if self.spec_vca == nil or self.spec_vca.activeActionEvents == nil then  
		return 
	end 
	
	for actionName, wasActive in pairs( self.spec_vca.activeActionEvents ) do
		local actionEvent = nil
		if InputAction[actionName] ~= nil then 
			actionEvent = self.spec_vca.actionEvents[InputAction[actionName]]
		end 
		if actionEvent ~= nil then 
			local isActive = false 
			if     actionName == "vcaSNAP" then 
				isActive = not self.spec_vca.snapDisabled
			elseif actionName == "vcaSNAP_A_B" then 
				if not self.spec_vca.snapDisabled then 
					if     self.spec_vca.lastSnapAngle == 10 then 
						isActive = true 
					elseif self.spec_vca.lastSnapAngle == 20 then 
						isActive = true 
					end 
				end 
			elseif actionName == "vcaSnapUP"     
					or actionName == "vcaSnapDOWN"     
					or actionName == "vcaSnapLEFT"    
					or actionName == "vcaSnapRIGHT"
					or actionName == "vcaSNAPPREV"
					or actionName == "vcaSNAPCONT"
					or actionName == "vcaSNAPNEXT"
					or actionName == "vcaSNAPLEFT"
					or actionName == "vcaSNAPRIGHT"
					or actionName == "vcaSNAPRESET"
					or actionName == "vcaSNAPDIST" 
					then 
				isActive = self.spec_vca.snapPossible 
			elseif actionName == "vcaKSAXIS" then 
				isActive = self.spec_vca.ksIsOn 
			elseif actionName == "vcaMotorMod" then 
				isActive = vehicleControlAddon.vcaUIShowidleThrottle( self )
			elseif actionName == "vcaClutchMod" then 
				isActive = vehicleControlAddon.vcaUIShowautoShift( self )
			elseif actionName == "vcaAutoShift" then 
				isActive = vehicleControlAddon.vcaUIShowautoShift( self )
			elseif actionName == "vcaDiffManual" then
				isActive = ( self.spec_vca.diffHasF or self.spec_vca.diffHasM or self.spec_vca.diffHasB )
			elseif actionName == "vcaDiffLockF" then
				isActive = self:vcaHasDiffFront()
			elseif actionName == "vcaDiffLockM" then
				isActive = self:vcaHasDiffAWD()
			elseif actionName == "vcaDiffLockB" then
				isActive = self:vcaHasDiffBack()
			end 
			
			self.spec_vca.activeActionEvents[actionName] = isActive
			
			g_inputBinding:setActionEventActive(actionEvent.actionEventId, isActive)						
		end 
	end 
end 

function vehicleControlAddon:actionCallback(actionName, keyStatus, callbackState, isAnalog, isMouse, deviceCategory)
	
--vcaDebugPrint(actionName..", "..tostring(keyStatus))
	
	if     actionName == "vcaSnapDOWN"
			or actionName == "vcaSnapUP"
			or actionName == "vcaSnapLEFT"
			or actionName == "vcaSnapRIGHT"
			then  
		if self.spec_vca.actionTimer == nil then 
			self.spec_vca.actionTimer = {} 
		end 
		
		if keyStatus < 0.5 then 
			self.spec_vca.actionTimer[actionName] = nil 
			return 
		elseif self.spec_vca.actionTimer[actionName] == nil then 
			self.spec_vca.actionTimer[actionName] = g_currentMission.time + 500 
		elseif self.spec_vca.actionTimer[actionName] > g_currentMission.time then 
			return 
		else 
			self.spec_vca.actionTimer[actionName] = g_currentMission.time + 100
		end 
	end 
	
	
	if     actionName == "vcaDiffManual" then 
		if     self.spec_vca.diffHasF 
				or self.spec_vca.diffHasM 
				or self.spec_vca.diffHasB then
			if     not self.spec_vca.diffManual then 
				self:vcaSetState( "diffManual", true )
				self:vcaSetState( "diffFrontAdv", false )
				self:vcaSetState( "diffLockSwap", false )
				self:vcaSetState( "warningText", "AWD equal wheels" )

			elseif not self.spec_vca.diffFrontAdv
			    and self.spec_vca.diffHasM then 
				self:vcaSetState( "diffFrontAdv", true )
				self:vcaSetState( "diffLockSwap", false )
				self:vcaSetState( "warningText", "AWD smaller front wheels" )
			else
				self:vcaSetState( "diffManual", false )
				self:vcaSetState( "diffFrontAdv", false )
				self:vcaSetState( "diffLockSwap", false )
				self:vcaSetState( "warningText", "automatic diff lock" )
			end 
		end 
	
	elseif actionName == "vcaMotorMod" then 
		self:vcaSetState( "idleThrottle", not self.spec_vca.idleThrottle )
		self:vcaSetState( "warningText", "Motor modifications: "..tostring( self.spec_vca.idleThrottle ) )
		
	elseif actionName == "vcaClutchMod" then 
		local clutchText = vehicleControlAddon.getText("vcaClutchDefault", "default" )
		
		if     self.spec_vca.clutchMode == vehicleControlAddon.DEFAULT_CLUTCH then      
	  	self:vcaSetState( "clutchMode", vehicleControlAddon.SHUTTLE_CLUTCH )
			clutchText = vehicleControlAddon.getText("vcaClutchShuttle", "shuttle" ) 
	  elseif self.spec_vca.clutchMode == vehicleControlAddon.SHUTTLE_CLUTCH then 
	  	self:vcaSetState( "clutchMode", vehicleControlAddon.ANTI_STALL )
			clutchText = vehicleControlAddon.getText("vcaClutchAntiStall", "anti stall" ) 
	  elseif self.spec_vca.clutchMode == vehicleControlAddon.ANTI_STALL     then 
	  	self:vcaSetState( "clutchMode", vehicleControlAddon.TURBO_CLUTCH )
			clutchText = vehicleControlAddon.getText("vcaClutchTurbo", "turbo" ) 
	  else --if self.spec_vca.clutchMode == vehicleControlAddon.TURBO_CLUTCH   then  
			self:vcaSetState( "clutchMode", vehicleControlAddon.DEFAULT_CLUTCH )
		end 
	
		self:vcaSetState( "warningText", "Clutch modifications: "..clutchText )
		
	elseif actionName == "vcaCamera" then 
	
		if self.spec_vca.camRevOutside then 
			self:vcaSetState( "camRotInside" , 0 )	
			self:vcaSetState( "camRotOutside", 0 )
			self:vcaSetState( "camRevInside" , false )
			self:vcaSetState( "camRevOutside", false )
			self:vcaSetState( "warningText", "Camera rotation off" )
		elseif self.spec_vca.camRevInside then 
			self:vcaSetState( "camRotInside" , 2 )	
			self:vcaSetState( "camRotOutside", 2 )
			self:vcaSetState( "camRevInside" , true )
			self:vcaSetState( "camRevOutside", true )
			self:vcaSetState( "warningText", "Camera rotation in- and outside" )
		else 
			self:vcaSetState( "camRotInside" , 2 )		
			self:vcaSetState( "camRotOutside", 0 )
			self:vcaSetState( "camRevInside" , true )	
			self:vcaSetState( "camRevOutside", false )
			self:vcaSetState( "warningText", "Camera rotation inside" )
		end 
	
	elseif actionName == "vcaGearSplit" then 
	
	
	
	
	elseif actionName == "vcaKEEPROT" then 
		self.spec_vca.keepRotPressed   = keyStatus >= 0.5 			
	elseif actionName == "vcaKEEPROT2" then 
		
		local krToggle = "kRToggleOut"
		if self.spec_vca.camIsInside then 
			krToggle = "kRToggleIn"
		end 
		self.spec_vca[krToggle] = not self.spec_vca[krToggle] 
		self.spec_vca.keepCamRot = self.spec_vca[krToggle] 
			
	elseif actionName == "vcaINCHING" then 
		self.spec_vca.inchingPressed   = keyStatus >= 0.5 
	elseif actionName == "vcaKEEPSPEED" then 
		self.spec_vca.keepSpeedPressed = keyStatus >= 0.5 		
	elseif actionName == "vcaKEEPSPEED2" then 
		self:vcaSetState( "ksToggle", not self.spec_vca.ksToggle )
		if self.spec_vca.ksToggle then 
			self:vcaSetState( "keepSpeed", self.lastSpeed * 3600 )
			self:vcaSetState( "ksIsOn", true )
		else 
			self:vcaSetState( "ksIsOn", false )
		end 
	elseif actionName == "vcaLimitSpeed" then 
		self:vcaSetState( "speedLimiter", not self.spec_vca.speedLimiter)
		
	elseif actionName == "vcaUP"
			or actionName == "vcaDOWN"
			or actionName == "vcaLEFT"
			or actionName == "vcaRIGHT" then

		if not ( self.spec_vca.peekLeftRight ) then 
			if     actionName == "vcaUP" then
				self.spec_vca.newRotCursorKey = 0
			elseif actionName == "vcaDOWN" then
				self.spec_vca.newRotCursorKey = math.pi
			elseif actionName == "vcaLEFT" then
				if not ( self.spec_vca.isForward ) then
					self.spec_vca.newRotCursorKey =  0.7*math.pi
				else 
					self.spec_vca.newRotCursorKey =  0.3*math.pi
				end 
			elseif actionName == "vcaRIGHT" then
				if not ( self.spec_vca.isForward ) then
					self.spec_vca.newRotCursorKey = -0.7*math.pi
				else 
					self.spec_vca.newRotCursorKey = -0.3*math.pi
				end 
			end
			self.spec_vca.prevRotCursorKey  = nil 
		elseif keyStatus >= 0.5 then 
			local i = self.spec_enterable.camIndex
			local r = nil
			if i ~= nil and self.spec_enterable.cameras[i].rotY and self.spec_enterable.cameras[i].origRotY ~= nil then 
				r = vehicleControlAddon.normalizeAngle( self.spec_enterable.cameras[i].rotY - self.spec_enterable.cameras[i].origRotY )
			end

			if     actionName == "vcaUP" then
				if     r == nil then 
					self.spec_vca.newRotCursorKey = math.pi
				elseif math.abs( r ) < 0.3 then
					if     r < -0.1 then 
						self.spec_vca.newRotCursorKey = - math.pi
					elseif r >  0.1 then 
						self.spec_vca.newRotCursorKey = math.pi
					else                              
						self.spec_vca.newRotCursorKey = - math.pi
					end 
				else 
					self.spec_vca.newRotCursorKey = 0
				end 
				self.spec_vca.prevRotCursorKey  = nil 
				r = nil
			elseif actionName == "vcaDOWN" then
				if     r == nil then 
					self.spec_vca.newRotCursorKey = nil
				elseif math.abs( r ) < 0.5 * math.pi then
					if     r < -0.1 then 
						self.spec_vca.newRotCursorKey = - math.pi
					elseif r >  0.1 then 
						self.spec_vca.newRotCursorKey = math.pi
					else 
						self.spec_vca.newRotCursorKey = - math.pi
					end 
				else 
					self.spec_vca.newRotCursorKey = 0
				end 
			elseif actionName == "vcaLEFT" then
				if     r ~= nil and math.abs( r ) > 0.7 * math.pi then
					self.spec_vca.newRotCursorKey =  0.7*math.pi
				elseif r ~= nil and math.abs( r ) < 0.3 * math.pi then
					self.spec_vca.newRotCursorKey =  0.3*math.pi
				else 
					self.spec_vca.newRotCursorKey =  0.5*math.pi
				end 
			elseif actionName == "vcaRIGHT" then
				if     r ~= nil and math.abs( r ) > 0.7 * math.pi then
					self.spec_vca.newRotCursorKey = -0.7*math.pi
				elseif r ~= nil and math.abs( r ) < 0.3 * math.pi then
					self.spec_vca.newRotCursorKey = -0.3*math.pi
				else 
					self.spec_vca.newRotCursorKey = -0.5*math.pi
				end 
			end
			
			if self.spec_vca.prevRotCursorKey == nil and r ~= nil then 
				self.spec_vca.prevRotCursorKey = r 
			end 
		elseif self.spec_vca.prevRotCursorKey ~= nil then 
			self.spec_vca.newRotCursorKey  = self.spec_vca.prevRotCursorKey
			self.spec_vca.prevRotCursorKey = nil
		end
	elseif actionName == "vcaSWAPSPEED" then 
		local temp = self.spec_drivable.cruiseControl.speed
    self:vcaSetCruiseSpeed( self.spec_vca.ccSpeed2 )
		self:vcaSetState( "ccSpeed2", self.spec_vca.ccSpeed3 )
		self:vcaSetState( "ccSpeed3", temp )
	elseif actionName == "vcaNO_ARB" then 
		self:vcaSetState( "noAutoRotBack", keyStatus >= 0.5 )
		
	elseif  self.spec_vca.snapPossible
			and ( actionName == "vcaSnapLEFT" or actionName == "vcaSnapRIGHT" ) then
			
		self.spec_vca.snapPosTimer  = math.max( Utils.getNoNil( self.spec_vca.snapPosTimer , 0 ), 3000 )
		
		local lx,_,lz = localDirectionToWorld( self:vcaGetSteeringNode(), 0, 0, 1 )			
		local d = 0
		if lx*lx+lz*lz > 1e-6 then 
			d = math.atan2( lx, lz )
		end 
		local a  = self:vcaGetCurrentSnapAngle( d )
		local dx = math.sin( a )
		local dz = math.cos( a )			
		local fx = 0
		local fz = 0
		
		if     actionName == "vcaSnapLEFT"  then
			fx = 0.1 
		else
			fx = -0.1 
		end 

		self:vcaSetState( "lastSnapPosX", self.spec_vca.lastSnapPosX + fz * dx + fx * dz )
		self:vcaSetState( "lastSnapPosZ", self.spec_vca.lastSnapPosZ + fz * dz - fx * dx )
		
	elseif  self.spec_vca.snapPossible
			and actionName == "vcaSnapDOWN" then 
		self.spec_vca.snapPosTimer  = math.max( Utils.getNoNil( self.spec_vca.snapPosTimer , 0 ), 3000 )
		self:vcaSetState( "lastSnapAngle", vehicleControlAddon.normalizeAngle( self.spec_vca.lastSnapAngle - math.rad(0.1*vehicleControlAddon.snapAngles[self.spec_vca.snapAngle])))
		self:vcaSetSnapFactor()
	elseif  self.spec_vca.snapPossible
			and actionName == "vcaSnapUP" then 
		self.spec_vca.snapPosTimer  = math.max( Utils.getNoNil( self.spec_vca.snapPosTimer , 0 ), 3000 )
		self:vcaSetState( "lastSnapAngle", vehicleControlAddon.normalizeAngle( self.spec_vca.lastSnapAngle + math.rad(0.1*vehicleControlAddon.snapAngles[self.spec_vca.snapAngle])))
		self:vcaSetSnapFactor()
	elseif actionName == "vcaSNAPRESET" then
		self:vcaSetState( "lastSnapAngle", 10 )
		self:vcaSetState( "lastSnapPosX", 0 )
		self:vcaSetState( "lastSnapPosZ", 0 )
		self:vcaSetState( "snapIsOn", false )
		self:vcaSetState( "snapDirection", 0 )
		self.spec_vca.currentPath = nil 
	elseif actionName == "vcaSNAPDIST" then
		local d, o, p = self:vcaGetSnapDistance()
		self:vcaSetState( "snapDistance", d )
		self:vcaSetState( "snapOffset",   o )
		self:vcaSetState( "snapInvert",   p )
		self:vcaSetState( "warningText", vehicleControlAddon.getText("vcaDISTANCE", "Width")..": "..vehicleControlAddon.formatNumber( self.spec_vca.snapDistance ) )
		self.spec_vca.snapPosTimer = math.max( Utils.getNoNil( self.spec_vca.snapPosTimer , 0 ), 3000 )
	elseif actionName == "vcaSNAP" then
		if false and ( self.spec_articulatedAxis ~= nil and self.spec_articulatedAxis.componentJoint ~= nil ) then 
			self:vcaSetState("warningText",vehicleControlAddon.getText("vcaSnapArtAxis", "VCA does not support articulated axis"))
		else 
			self:vcaSetState( "snapIsOn", not self.spec_vca.snapIsOn )
			if self.spec_vca.snapIsOn then 
				self:vcaSetState( "snapDirection", 0 )
			end 
			self:vcaSetSnapFactor()
		end 
	elseif actionName == "vcaSNAPPREV" 
			or actionName == "vcaSNAPNEXT" then
		if actionName == "vcaSNAPNEXT" then
			self:vcaSetState( "snapFactor", self.spec_vca.snapFactor + 1 )
		else 
			self:vcaSetState( "snapFactor", self.spec_vca.snapFactor - 1 )
		end 
		self.spec_vca.snapPosTimer = math.max( Utils.getNoNil( self.spec_vca.snapPosTimer , 0 ), 3000 )
	elseif actionName == "vcaSNAPLEFT" then 
		self:vcaSnapReverseLeft()
	elseif actionName == "vcaSNAPRIGHT" then
		self:vcaSnapReverseRight()
	elseif actionName == "vcaSNAPCONT" then
		if  -4 <= self.spec_vca.lastSnapAngle and self.spec_vca.lastSnapAngle <= 4 and self.spec_vca.snapDistance >= 0.25 then 
			self:vcaSetState( "snapIsOn", true )
		end 
	elseif actionName == "vcaAutoShift" then
		self:vcaSetState( "autoShift", not self.spec_vca.autoShift )
	elseif actionName == "vcaHandbrake" then
		self:vcaSetState( "handbrake", not self.spec_vca.handbrake )
	elseif actionName == "vcaDiffLockF" then
		if self:vcaIsVehicleControlledByPlayer() and self:vcaHasDiffFront() then
			self:vcaSetState( "diffLockFront", not self.spec_vca.diffLockFront )
		end 
	elseif actionName == "vcaDiffLockM" then
		if self:vcaIsVehicleControlledByPlayer() and self:vcaHasDiffAWD() then  
			self:vcaSetState( "diffLockAWD", not self.spec_vca.diffLockAWD )
		end 
	elseif actionName == "vcaDiffLockB" then
		if self:vcaIsVehicleControlledByPlayer() and self:vcaHasDiffBack() then
			self:vcaSetState( "diffLockBack", not self.spec_vca.diffLockBack )
		end 
	elseif actionName == "vcaKSAXIS" and self.spec_vca.ksIsOn then 
		if self.spec_vca.keepSpeedTemp == nil then 
			self.spec_vca.keepSpeedTemp = self.spec_vca.keepSpeed
		end 
		if     math.abs( self.spec_vca.keepSpeedTemp ) <  1 then 
			if     keyStatus > 0 then 
				self.spec_vca.keepSpeedTemp = 1 
			elseif keyStatus < 0 then 
				self.spec_vca.keepSpeedTemp = -1 
			end 
		elseif isAnalog then 
			self.spec_vca.keepSpeedTemp = self.spec_vca.keepSpeedTemp + keyStatus
		elseif math.abs( self.spec_vca.keepSpeedTemp ) <  5 then 
			self.spec_vca.keepSpeedTemp = 0.1 * math.floor( 10 * self.spec_vca.keepSpeedTemp + keyStatus + 0.5 )
		elseif math.abs( self.spec_vca.keepSpeedTemp ) < 10 then 
			self.spec_vca.keepSpeedTemp = 0.2 * math.floor(  5 * self.spec_vca.keepSpeedTemp + keyStatus + 0.5 )
		elseif math.abs( self.spec_vca.keepSpeedTemp ) < 20 then 
			self.spec_vca.keepSpeedTemp = 0.4 * math.floor(2.5 * self.spec_vca.keepSpeedTemp + keyStatus + 0.5 )
		elseif math.abs( self.spec_vca.keepSpeedTemp ) < 30 then 
			self.spec_vca.keepSpeedTemp = 0.5 * math.floor(  2 * self.spec_vca.keepSpeedTemp + keyStatus + 0.5 )
		else
			self.spec_vca.keepSpeedTemp = math.floor(self.spec_vca.keepSpeedTemp + keyStatus + 0.5 )
		end 
	elseif actionName == "vcaHandRpm" then 
		local h = 0
		if self.spec_vca.handThrottle ~= nil and self.spec_vca.handThrottle > 0 then 
			h = self.spec_vca.handThrottle
		end 
		
		if     isAnalog then 
			if keyStatus > 0.5 then 
				self.spec_vca.handRpmFullAxis = true 
			end 
			if self.spec_vca.handRpmFullAxis then 
				h = 0.5 * ( 1 + keyStatus ) 
			else 
				h = 1 + keyStatus
			end 
		else
			if      h <= 0
					and type ( self.getMotorRpmPercentage ) == "function"
					and ( keyStatus > 0.5 
						or  self.spec_vca.locHandRpmTime == nil 
						or  g_currentMission.time > self.spec_vca.locHandRpmTime + 2000 )
					then 
				h = vehicleControlAddon.mbClamp( self:getMotorRpmPercentage() )
			end 
			self.spec_vca.locHandRpmTime = g_currentMission.time
			
			if keyStatus > 0.5 then 
				h = math.min( 1, h + 0.0005 * self.spec_vca.tickDt )
			elseif keyStatus < 0.5 then 
				h = math.max( 0, h - 0.0005 * self.spec_vca.tickDt )
			end 
		end 
		
		self:vcaSetState( "handThrottle", vehicleControlAddon.mbClamp( h, 0, 1 ) )

		if not isAnalog then 
			if h <= 0 then 
				self:vcaSetState( "warningText", vehicleControlAddon.getText( "vcaHANDTHROTTLE", "" )..": off" )
			elseif  self.spec_motorized ~= nil 
					and self.spec_motorized.motor ~= nil 
					and self.spec_motorized.motor.maxRpm ~= nil 
					and self.spec_motorized.motor.maxRpm > 0 then 
				local r = self.spec_motorized.motor.minRpm + h * ( self.spec_motorized.motor.maxRpm - self.spec_motorized.motor.minRpm )
				self:vcaSetState( "warningText", string.format("%s: %4.0f %s", vehicleControlAddon.getText( "vcaHANDTHROTTLE", "" ), r, vehicleControlAddon.getText( "vcaValueRPM", "RPM"  ) ) )
			end 
		end 
	elseif actionName == "vcaWORKAREA" then 
		if self.spec_vca.workAreaDraw >= 2 then 
			self:vcaSetState( "workAreaDraw", 0 )
		else 
			self:vcaSetState( "workAreaDraw", self.spec_vca.workAreaDraw + 1 )
		end
	elseif actionName == "vcaSNAPPATH" then 
		self:vcaSetState( "pathRecording", not self.spec_vca.pathRecording )
  elseif actionName == "vcaLowerF" then 
		self:vcaSetToolStateNew( vehicleControlAddon.toolStateLower, true, false )
  elseif actionName == "vcaLowerB" then 
		self:vcaSetToolStateNew( vehicleControlAddon.toolStateLower, false, true )
  elseif actionName == "vcaFoldF" then 
		self:vcaSetToolStateNew( vehicleControlAddon.toolStateFold, true, false )
  elseif actionName == "vcaFoldB" then 
		self:vcaSetToolStateNew( vehicleControlAddon.toolStateFold, false, true )
  elseif actionName == "vcaActivateF" then 
		self:vcaSetToolStateNew( vehicleControlAddon.toolStateActivate, true, false )
  elseif actionName == "vcaActivateB" then 
		self:vcaSetToolStateNew( vehicleControlAddon.toolStateActivate, false, true )
  elseif actionName == "vcaGearSplitDown" and self.spec_vca.gearSplitGear > 1 then  
		self:vcaSetState( "gearSplitGear", self.spec_vca.gearSplitGear - 1 )
  elseif actionName == "vcaGearSplitUp" and self.spec_vca.gearSplitGear <= self.spec_vca.splitGears then  
		self:vcaSetState( "gearSplitGear", self.spec_vca.gearSplitGear + 1 )
  elseif actionName == "vcaSNAP_A_B" then  
		if     self.spec_vca.lastSnapAngle == 10 then 
			vcaDebugPrint("Set point A")
			local wx,_,wz = getWorldTranslation( self:vcaGetSteeringNode() )
			self:vcaSetState( "lastSnapPosX", wx )
			self:vcaSetState( "lastSnapPosZ", wz )
			self:vcaSetState( "lastSnapAngle", 20 )
			self:vcaSetState( "warningText", vehicleControlAddon.getText("vcaSnapSetA", "Set point B"))
		elseif self.spec_vca.lastSnapAngle == 20 then 
			local wx,_,wz = getWorldTranslation( self:vcaGetSteeringNode() )
			local lx = wx - self.spec_vca.lastSnapPosX
			local lz = wz - self.spec_vca.lastSnapPosZ
			
		
			if lx*lx+lz*lz > 1 then 
				vcaDebugPrint("Set point B: "..tostring(lx)..", "..tostring(lz))
				
				local rot    = math.atan2( lx, lz )
				local d      = 0.1
			
				if self:getIsVehicleControlledByPlayer() then 
					self.spec_vca.snapPosTimer = 20000
				end 

				local target = 0
				local diff   = math.pi+math.pi

				for i=0,360,d do 
					local a = math.rad( i )
					local b = math.abs( vehicleControlAddon.normalizeAngle( a - rot ) )
					if b < diff then 
						target = a 
						diff   = b
					end 
				end 
				
				self:vcaSetState( "lastSnapAngle", vehicleControlAddon.normalizeAngle( target ) )
				self:vcaSetState( "snapFactor", 0 )
				
			else 
				vcaDebugPrint("Set point B failed")
			end 
		else 
			vcaDebugPrint("Set point A-B failed")
			self:vcaSetState( "warningText", vehicleControlAddon.getText("vcaSnapSetB", "Reset"))
		end 
	end
end

function vehicleControlAddon:vcaSetToolStateRec( lowered, active, front, back, forceState )
	local state = 0
	if     lowered then 
		state = vehicleControlAddon.toolStateLower 
	elseif active  then 
		state = vehicleControlAddon.toolStateActivate 
	else 
		state = vehicleControlAddon.toolStateFold 
	end 
	self:vcaSetToolStateNew( state, front, back, forceState )
end

function vehicleControlAddon:vcaSetToolStateNew( state, front, back, forceState )

-- AttacherJoints:handleLowerImplementByAttacherJointIndex(attacherJointIndex, direction)
-- if direction == nil then direction = not attacherJoint.moveDown end


--  if self:getCanToggleTurnedOn() and self:getCanBeTurnedOn() then self:setIsTurnedOn(not self:getIsTurnedOn()) ...

	local newState  = forceState 
	local recursive = true 

	if self.spec_attacherJoints ~= nil then 
		vcaDebugPrint(tostring(self.configFileName)..": "..tostring(state)..", "..tostring(front)..", "..tostring(back)..", "..tostring(forceState))
	
		local spec = self.spec_attacherJoints
		for _,attachedImplement in pairs( spec.attachedImplements ) do 
			local jointDesc = spec.attacherJoints[attachedImplement.jointDescIndex]
			
			local doit = false 
			if front and back then 
				doit = true 
			else 
				local wx, wy, wz = getWorldTranslation(jointDesc.jointTransform)
				local lx, ly, lz = worldToLocal(self:vcaGetSteeringNode(), wx, wy, wz)
				
				if lz > 0 then 
					doit = front 
				else 
					doit = back 
				end 
			end 
			
			if doit and attachedImplement.object ~= nil then 
				local object = attachedImplement.object 
				
				if state == vehicleControlAddon.toolStateLower then 
					if newState == nil then 
						-- getIsLowered is overwritte by Pickup and Foldable 
						-- but getAllowsLowering is not overwritten 
						if     object:getAllowsLowering()
								or object.spec_pickup     ~= nil
								or ( object.spec_foldable ~= nil 
								 and object.spec_foldable.foldMiddleAnimTime ~= nil
								 and object:getIsFoldMiddleAllowed())
								then 
							newState = not object:getIsLowered()
						end 
					end 
					
					if newState ~= nil and object.setLoweredAll ~= nil then 
						object:setLoweredAll(newState, attachedImplement.jointDescIndex)
					end 
				
				elseif state == vehicleControlAddon.toolStateActivate then 
					if object.spec_plow ~= nil then 
						-- rotate plow
						local spec = object.spec_plow
						if spec.rotationPart.turnAnimation ~= nil then
							if object:getIsPlowRotationAllowed() then
								object:setRotationMax(not spec.rotationMax)
								recursive = false 
							end
						end
					elseif object.getIsTurnedOn ~= nil then 
						-- turn on 
						if newState == nil then 
							newState = not object:getIsTurnedOn() 
						end 
						
						if object:getCanToggleTurnedOn() and object:getCanBeTurnedOn() then
							object:setIsTurnedOn(newState)
						end 
					end 

				elseif state == vehicleControlAddon.toolStateFold then 
					if object.spec_foldable ~= nil then 
						-- foldable 
						local spec = object.spec_foldable
						if #spec.foldingParts > 0 then
							local toggleDirection = object:getToggledFoldDirection()
							local allowed, warning = object:getIsFoldAllowed(toggleDirection, false)

							if allowed then
								if newState == nil then 
									newState = toggleDirection == spec.turnOnFoldDirection 
								end 
								if newState then
									object:setFoldState(toggleDirection, true)
								else
									object:setFoldState(toggleDirection, false)

									if object:getIsFoldMiddleAllowed() and object.getAttacherVehicle ~= nil then
										local attacherVehicle = object:getAttacherVehicle()
										local attacherJointIndex = attacherVehicle:getAttacherJointIndexFromObject(object)

										if attacherJointIndex ~= nil then
											local moveDown = attacherVehicle:getJointMoveDown(attacherJointIndex)
											local targetMoveDown = toggleDirection == spec.turnOnFoldDirection

											if targetMoveDown ~= moveDown then
												attacherVehicle:setJointMoveDown(attacherJointIndex, targetMoveDown)
											end
										end
									end
								end
							elseif warning then 
								g_currentMission:showBlinkingWarning(warning, 2000)
							end 
						end 
					end 
				end 
				
				if recursive then
					vehicleControlAddon.vcaSetToolStateNew( object, state, true, true, newState )
				end 
			end 
		end 
	end 
end 

function vehicleControlAddon:vcaSetSnapFactor()
	if 			self.spec_vca.snapDistance >= 0.25 
			and self.spec_vca.snapIsOn
			and -4 <= self.spec_vca.lastSnapAngle and self.spec_vca.lastSnapAngle <= 4 then 
		local wx,wy,wz = getWorldTranslation( self:vcaGetSteeringNode() )
		local lx,_,lz = localDirectionToWorld( self:vcaGetSteeringNode(), 0, 0, 1 )			
		local d = 0
		if lx*lx+lz*lz > 1e-6 then 
			d = math.atan2( lx, lz )
		end 
		
		if type( self.spec_vca.currentPath ) == "table" and table.getn( self.spec_vca.currentPath ) > 1 then 
			local minT = nil
			local minS = nil
			local minI = nil
			local minP = nil 
			for indx,point in pairs( self.spec_vca.currentPath ) do 
				local sx = point[1]
				local sz = point[2]
				local a  = point[3]
				local l  = point[4]
				local dx = math.sin(a)
				local dz = math.cos(a)
				local t  = ( wx - sx ) * dx + ( wz - sz ) * dz 
				local s  = ( wx - sx ) * dz - ( wz - sz ) * dx 
				
				local p  = math.max( 0, math.min( t, l ))
				local px = sx + p * dx 
				local pz = sz + p * dz 
				local p2 = (wx - px)^2 + (wz - pz)^2
				
				if minP == nil or p2 <= minP then 
					minP = p2 
					minS = s 
					minT = t 
					minI = indx 
				end 
			end 
			
			print(tostring(minI).." ("..tostring(minT)..", "..tostring(minS)..", "..tostring(minP)..")")

			if minS ~= nil then 
				local i = math.floor( 0.5 - minS / self.spec_vca.snapDistance )
				self:vcaSetState( "snapFactor", i )
			end 
				
		else
			local curSnapAngle, _, curSnapOffset = self:vcaGetCurrentSnapAngle( d )
			local dx    = math.sin( curSnapAngle )
			local dz    = math.cos( curSnapAngle )			
			local distX = wx - self.spec_vca.lastSnapPosX
			local distZ = wz - self.spec_vca.lastSnapPosZ
			local i     = self.spec_vca.snapFactor
			local function getDistance(i) 
				return distX * dz - distZ * dx + i * self.spec_vca.snapDistance + curSnapOffset
			end 
			local dist  = getDistance(i)

			while dist+dist > self.spec_vca.snapDistance do 
				i     = i - 1
				dist  = getDistance(i)
			end 
			while dist+dist <-self.spec_vca.snapDistance do 
				i     = i + 1
				dist  = getDistance(i)
			end 			
			self:vcaSetState( "snapFactor", i )
		end 
	end 
end

function vehicleControlAddon:onEnterVehicle( isControlling )
	self:vcaSetState( "isEnteredMP", true )
	self:vcaSetState( "isBlocked", false )
	self:vcaSetState( "ksIsOn", self.spec_vca.ksToggle )
end 

function vehicleControlAddon:onLeaveVehicle()
	if self.spec_vca.isEntered then 
		self:vcaSetState( "noAutoRotBack", false )
		self.spec_vca.newRotCursorKey  = nil
		self.spec_vca.prevRotCursorKey = nil
	--self:vcaSetState( "snapIsOn", false )
		self:vcaSetState( "inchingIsOn", false )
		self:vcaSetState( "ksIsOn", false )
		self:vcaSetState( "keepSpeed", 0 )
		self:vcaSetState( "isEnteredMP", false )
		self:vcaSetState( "isBlocked", false )
		self:vcaSetState( "isForward", true )
	--self:vcaSetState( "handThrottle", 0 )
		self.spec_vca.movingDir     = 1
		self.spec_vca.keepCamRot    = false 
	end 

	self.spec_vca.isEntered  = false 
end 

function vehicleControlAddon:vcaCruiseNotEntered()
	if      self.spec_vca ~= nil
			and self.spec_vca.farmId ~= nil and self.spec_vca.farmId ~= 0
			and self.spec_drivable.cruiseControl.state == Drivable.CRUISECONTROL_STATE_ACTIVE then
		return true 
	end 
	return false 
end 


function vehicleControlAddon:vcaIsVehicleControlledByPlayer()
	if self:getIsVehicleControlledByPlayer() then 
		return true 
	end 
	if not self:getIsControlled() then 
		return false 
	end 
	if self.spec_globalPositioningSystem == nil then 
		return false 
	end 
	if self.spec_globalPositioningSystem.guidanceSteeringIsActive then 
		return true 
	end 
	return false 
end 

function vehicleControlAddon:vcaIsActive()
	if self.spec_vca ~= nil and self:getIsEntered() and self:vcaIsVehicleControlledByPlayer() then 
		return true 
	end 
	return false
end 

function vehicleControlAddon:vcaGetShuttleCtrl()
	if			self.spec_motorized ~= nil 
			and self.spec_motorized.motor ~= nil 
			and self.spec_motorized.motor.directionChangeMode == VehicleMotor.DIRECTION_CHANGE_MODE_MANUAL
			then 
		return true 
	end 
	return false  
end 

function vehicleControlAddon:vcaGetIsReverse()
	return not self.spec_vca.isForward
end 

function vehicleControlAddon:vcaHasDiffFront()
	if not ( self.spec_vca.diffManual and self.spec_vca.diffHasF ) then 
		return false 
	end 
	if not self.spec_vca.diffHas2     then 
		return true 
	elseif self.spec_vca.diffLockSwap then 
		return true 
	elseif self.spec_vca.diffLockAWD  then 
		return true 
	end 
	return false 
end 

function vehicleControlAddon:vcaHasDiffAWD()
	if not ( self.spec_vca.diffManual and self.spec_vca.diffHasM ) then 
		return false 
	end 
	return true 
end 
	
function vehicleControlAddon:vcaHasDiffBack()
	if not ( self.spec_vca.diffManual and self.spec_vca.diffHasB ) then 
		return false 
	end 
	if not self.spec_vca.diffHas2     then 
		return true 
	elseif not self.spec_vca.diffLockSwap then 
		return true 
	elseif self.spec_vca.diffLockAWD  then 
		return true 
	end 
	return false 
end 
		
function vehicleControlAddon:vcaGetDiffState()
	if not ( self.spec_vca ~= nil
			 and ( self:vcaIsVehicleControlledByPlayer() or ( self.spec_vca.hiredWorker2 and self:getIsActive() ) )
			 and self:getIsMotorStarted()
			 and self.spec_vca.diffManual ) then  
	-- hired worker or motor off 
		return 0, 0, 0
	elseif  not self.spec_vca.diffHasF 
			and not self.spec_vca.diffHasM 
			and not self.spec_vca.diffHasB then
	-- no diffs or not the standard pattern
		return 0, 0, 0 
	end 
	
	local f, m, b = 0, 0, 0
	
	if self:vcaHasDiffFront() then 
		if self.spec_vca.diffLockFront then 
			f = 2 
		else 
			f = 1 
		end 
	end 
	
	if self:vcaHasDiffBack() then 
		if self.spec_vca.diffLockBack then 
			b = 2 
		else 
			b = 1 
		end 
	end 
	
	if self:vcaHasDiffAWD() then 
		if self.spec_vca.diffLockAWD then 
			m = 2 
		else 
			m = 1
		end 
	end 
	
	return f, m, b
end 

--******************************************************************************************************************************************
-- ProSeed
function vehicleControlAddon:vcaGetHasGuidanceSystem( superFunc )
	if self ~= nil and self.spec_vca ~= nil and self.spec_vca.snapPossible then 
		return true 
	end 
	if superFunc ~= nil then 
		return superFunc( self )
	end 
	return false 
end 
function vehicleControlAddon:vcaGetGuidanceData( superFunc )
	if not ( self ~= nil and self.spec_vca ~= nil and self.spec_vca.snapPossible ) then 
		if superFunc ~= nil then 
			return superFunc( self )
		end 
		return
	end 

	local data = {}
	
  data.width            = 3
  data.offsetWidth      = 0
  data.movingDirection  = 1
  data.isReverseDriving = false
  data.movingForwards   = false
  data.snapDirectionMultiplier = 1
  data.alphaRad         = 0
  data.currentLane      = 0
	data.snapDirection    = { 0, 0, 0, 0 }
  data.driveTarget      = { 0, 0, 0, 0, 0 }
	data.width            = self.spec_vca.snapDistance

	local wx,wy,wz = getWorldTranslation( self:vcaGetSteeringNode() )
	local lx,_,lz = localDirectionToWorld( self:vcaGetSteeringNode(), 0, 0, 1 )			
	local d = 0
	if lx*lx+lz*lz > 1e-6 then 
		d = math.atan2( lx, lz )
	end 
	if not self.spec_vca.snapIsOn then 
		d = math.atan2( lx, lz )
	elseif self.spec_vca.snapDirection == 1 then 
		d = self.spec_vca.lastSnapAngle 
	elseif self.spec_vca.snapDirection == 2 then 
		d = self.spec_vca.lastSnapAngle + 0.5 * math.pi 
	elseif self.spec_vca.snapDirection == 3 then 
		d = self.spec_vca.lastSnapAngle + math.pi 
	elseif self.spec_vca.snapDirection == 4 then 
		d = self.spec_vca.lastSnapAngle - 0.5 * math.pi 
	else
		d = math.atan2( lx, lz )
	end 
	local curSnapAngle, curSnapOffset1, curSnapOffset2 = self:vcaGetCurrentSnapAngle( d )
	local dx    = math.sin( curSnapAngle )
	local dz    = math.cos( curSnapAngle )			

	data.snapDirection = { dz, dx, self.spec_vca.lastSnapPosX, self.spec_vca.lastSnapPosZ }

	if self.spec_vca.snapIsOn then 
		data.currentLane = self.spec_vca.snapFactor
	else
		data.currentLane = 0
		if self.spec_vca.snapDistance >= 0.25 then 
			local distX = wx - self.spec_vca.lastSnapPosX
			local distZ = wz - self.spec_vca.lastSnapPosZ 	
			local dist  = distX * dz - distZ * dx + curSnapOffset2
			while dist+dist > self.spec_vca.snapDistance do 
				dist = dist - self.spec_vca.snapDistance
				data.currentLane = data.currentLane - 1 
			end 
			while dist+dist <-self.spec_vca.snapDistance do 
				dist = dist + self.spec_vca.snapDistance
				data.currentLane = data.currentLane + 1 
			end 
		end 
	end 
	
	return data 
end 

function vehicleControlAddon:onPreUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
--******************************************************************************************************************************************


	if      self.isClient
			and self.spec_vca.isEntered
			and self.spec_vca.snapIsOn
			and self.spec_drivable ~= nil
			and self:getIsActiveForInput(true, true)
			and self:getIsVehicleControlledByPlayer()
			and math.abs( self.spec_drivable.lastInputValues.axisSteer ) > 0.15
			and self.spec_vca.snapNoDisableTimer < g_currentMission.time then 
		self:vcaSetState( "snapIsOn", false )
	end 
	
	self.spec_vca.snapDisabled = false 
	self.spec_vca.snapPossible = false 
	if self.spec_globalPositioningSystem ~= nil and self.spec_globalPositioningSystem.guidanceIsActive then
		self.spec_vca.snapDisabled = true 
	elseif not self:vcaIsVehicleControlledByPlayer()
			or self.aiveAutoSteer then 
		self.spec_vca.snapDisabled = true 
	elseif  -4 <= self.spec_vca.lastSnapAngle and self.spec_vca.lastSnapAngle <= 4
			and self.spec_vca.snapDistance >= 0.25
			and vehicleControlAddon.snapAngles[self.spec_vca.snapAngle] ~= nil then 
		self.spec_vca.snapPossible = true 
	end 

	if self.spec_vca.snapDisabled and self.spec_vca.snapIsOn then
		self:vcaSetState( "snapIsOn", false )
	end 
	
--******************************************************************************************************************************************
-- adaptive steering 	
	local lastAxisSteer, lastAxisSteerTime1, lastAxisSteerTime2
	
	if self.spec_vca.lastAxisSteer ~= nil then 
		lastAxisSteer               = self.spec_vca.lastAxisSteer
		lastAxisSteerTime1          = self.spec_vca.lastAxisSteerTime1
		lastAxisSteerTime2          = self.spec_vca.lastAxisSteerTime2
		self.spec_vca.lastAxisSteer       = nil
		self.spec_vca.lastAxisSteerAnalog = nil
		self.spec_vca.lastAxisSteerDevice = nil
		self.spec_vca.lastAxisSteerTime1  = nil
		self.spec_vca.lastAxisSteerTime2  = nil
	end 
	
  local resetRotSpeed = true 

	if      self.isClient
			and self.getIsEntered  ~= nil and self:getIsEntered()
			and self.spec_drivable ~= nil
			and not ( FS19_TrailerAssist ~= nil 
						and FS19_TrailerAssist.trailerAssist ~= nil
						and type( FS19_TrailerAssist.trailerAssist.isActive ) == "function"
						and FS19_TrailerAssist.trailerAssist.isActive( self ) )
      and self:getIsActiveForInput(true, true)
      and self:getIsVehicleControlledByPlayer() then 
			
		local noARB = self.spec_vca.noARBToggle 
		local ana   = self.spec_drivable.lastInputValues.axisSteerIsAnalog 
		local dev   = self.spec_drivable.lastInputValues.axisSteerDeviceCategory 
		if      self.spec_drivable.lastInputValues.axisSteer == 0
				and self.spec_vca.lastAxisSteer ~= nil then 
			ana = self.spec_vca.lastAxisSteerAnalog
			dev = self.spec_vca.lastAxisSteerDevice
		end 
		if  not VCAGlobals.mouseAutoRotateBack
				and ana 
				and dev == InputDevice.CATEGORY.KEYBOARD_MOUSE then 
			noARB = true 
		end 
		if self.spec_vca.noAutoRotBack then 
			noARB = not noARB
		end 
		
		local rso = 1
		if not ( 0.49 < self.spec_vca.rotSpeedOut and self.spec_vca.rotSpeedOut < 0.51 ) then 
			rso = vehicleControlAddon.mbClamp( self.spec_vca.rotSpeedOut + self.spec_vca.rotSpeedOut, 0.01, 2 )
		end 
		local rsi = 1
		if math.abs( self.spec_vca.rotSpeedIn - self.spec_vca.rotSpeedOut ) > 0.01 then 
			rsi = vehicleControlAddon.mbClamp( self.spec_vca.rotSpeedIn + self.spec_vca.rotSpeedIn, 0.01, 2 ) / rso 
		end 
		
		if self.spec_vca.steeringIsOn == nil then 
			self.spec_vca.steeringIsOn = vehicleControlAddon.steeringFalse
		end 
		
		--********************************************************************
		-- Old, soft steering with reduced steering speed 
		if self.spec_vca.steeringIsOn == vehicleControlAddon.steeringSoft then 
			resetRotSpeed = false 
			local speed = math.abs( self.lastSpeed * 3600 )
			local f = 1
			if     speed <= 12.5 then 
				f = 2 - 0.8 * speed / 12.5
			elseif speed <= 25 then 
				f = 1.2 - 0.5 * ( speed - 12.5 ) / 12.5 
			elseif speed <= 50 then 
				f = 0.7 - 0.3 * ( speed - 25 ) / 25 
			elseif speed <= 100 then 
				f = 0.4 - 0.3 * ( speed - 50 ) / 50 
			else 
				f = 0.1
			end 
			
			self.spec_vca.rotSpeedFactor      = f
			self.spec_vca.autoRotateBackSpeed = self.autoRotateBackSpeed
			
			for i,w in pairs( self.spec_wheels.wheels ) do 
				local attr = "rotSpeed"
				if w.rotSpeedLimit ~= nil then
					attr = "rotSpeedDefault"
				end 

				if w[attr] ~= nil then 
					if w.vcaRotSpeed == nil then 
						w.vcaRotSpeed = w[attr]
					end 				
					w[attr] = w.vcaRotSpeed * self.spec_vca.rotSpeedFactor
				end 
	
				attr = "rotSpeedNeg" 
				if w.rotSpeedLimit ~= nil then
					attr = "rotSpeedNegDefault" 
				end 
	
				if w[attr] ~= nil then 
					if w.vcaRotSpeedNeg == nil then 
						w.vcaRotSpeedNeg = w[attr] 
					end 				
					w[attr] = w.vcaRotSpeedNeg * self.spec_vca.rotSpeedFactor
				end 
			end 
			
			if noARB then 
				self.autoRotateBackSpeed = 0 
			end 
		--********************************************************************
		-- adaptive steering
		elseif self.spec_vca.steeringIsOn == vehicleControlAddon.steeringTrue or noARB then 
			if lastAxisSteer == nil then 
				self.spec_vca.lastAxisSteer       = self.spec_drivable.lastInputValues.axisSteer 
				self.spec_vca.lastAxisSteerAnalog = self.spec_drivable.lastInputValues.axisSteerIsAnalog 
				self.spec_vca.lastAxisSteerDevice = self.spec_drivable.lastInputValues.axisSteerDeviceCategory
				lastAxisSteerTime1          = nil 
				lastAxisSteerTime2          = nil 
			else  
				self.spec_vca.lastAxisSteer       = lastAxisSteer
			end 
			
			if self.spec_drivable.lastInputValues.axisSteer ~= 0 then 
				self.spec_vca.lastAxisSteerAnalog = self.spec_drivable.lastInputValues.axisSteerIsAnalog 
				self.spec_vca.lastAxisSteerDevice = self.spec_drivable.lastInputValues.axisSteerDeviceCategory
			end 
			
			if VCAGlobals.analogSteering then 
				self.spec_vca.lastAxisSteerAnalog = false 
			end 

			local s = math.abs( self.lastSpeed * 3600 )
			
			if noARB or ( not self.spec_vca.lastAxisSteerAnalog and math.abs( self.spec_drivable.lastInputValues.axisSteer ) > 0.01 ) then 
				local f = dt * 0.0005
				if self.spec_vca.lastAxisSteerAnalog then 
					f = dt * 0.002
				elseif s < 1 then 
					f = dt * 0.001
				elseif ( self.spec_vca.lastAxisSteer > 0 and self.spec_drivable.lastInputValues.axisSteer < 0 )
						or ( self.spec_vca.lastAxisSteer < 0 and self.spec_drivable.lastInputValues.axisSteer > 0 ) then 
					f = rsi * dt * 0.002 * math.min( 1, 0.25 + math.abs( self.spec_vca.lastAxisSteer ) * 2 )	
				elseif not noARB then 
					if lastAxisSteerTime1 == nil then 
						self.spec_vca.lastAxisSteerTime1 = g_currentMission.time
					else 
						self.spec_vca.lastAxisSteerTime1 = lastAxisSteerTime1
					end 
					local tMax = 3000
					if s >= 60 then 
						tMax = 200 
					elseif s >= 10 then 
						tMax = 200 + 88 * ( 85 - s )
					end 
					f = dt * 1e-6 * vehicleControlAddon.mbClamp( g_currentMission.time - self.spec_vca.lastAxisSteerTime1, 25, tMax )
					if s < 21 then 
						f = math.max( f, dt * 5e-5 * ( 21 - s ) )
					end 
				else 
					f = dt * 0.001 * math.min( 1, 0.25 + math.abs( self.spec_vca.lastAxisSteer ) * 2 ) 
				end 
				f = f * rso
				self.spec_vca.lastAxisSteer = vehicleControlAddon.mbClamp( self.spec_vca.lastAxisSteer + f * self.spec_drivable.lastInputValues.axisSteer, -1, 1 )
			elseif self.spec_vca.lastAxisSteerAnalog then 
				self.spec_vca.lastAxisSteer = self.spec_drivable.lastInputValues.axisSteer 
			elseif s > 1 then 
				local a = 1
				local f = 0.0006
				if self.autoRotateBackSpeed ~= nil then 
					a = self.autoRotateBackSpeed 
				end 
				if s <= 26 then 
					a = a * ( s - 1 ) * 0.04 
				end
				if s <= 11 then
					f = math.max( ( 11 - s  ) * 0.1, f ) 
				end 

				if lastAxisSteerTime2 == nil then 
					self.spec_vca.lastAxisSteerTime2 = g_currentMission.time 
					a = 0
				else 
					self.spec_vca.lastAxisSteerTime2 = lastAxisSteerTime2
					a = a * vehicleControlAddon.mbClamp( f * ( g_currentMission.time - lastAxisSteerTime2 - 50 ), 0, 1 ) ^1.5
				end 
													
				if self.spec_vca.lastAxisSteer > 0 then 
					self.spec_vca.lastAxisSteer = math.max( 0, self.spec_vca.lastAxisSteer - dt * 0.001 * a )
				elseif self.spec_vca.lastAxisSteer < 0 then                                        
					self.spec_vca.lastAxisSteer = math.min( 0, self.spec_vca.lastAxisSteer + dt * 0.001 * a )
				end 
			end 
			
			self.spec_drivable.lastInputValues.axisSteer = self.spec_vca.lastAxisSteer
			self.spec_drivable.lastInputValues.axisSteerIsAnalog = true 
			self.spec_drivable.lastInputValues.axisSteerDeviceCategory = InputDevice.CATEGORY.UNKNOWN
		end 
	end 
	--********************************************************************
	-- Reset of old, soft steering with reduced steering speed 
	if resetRotSpeed and self.spec_vca.rotSpeedFactor ~= nil then
		for i,w in pairs( self.spec_wheels.wheels ) do 
			local attr = "rotSpeed"
			if w.rotSpeedLimit ~= nil then
				attr = "rotSpeedDefault"
			end 

			if w[attr] ~= nil and w.vcaRotSpeed ~= nil then 			
				w[attr] = w.vcaRotSpeed
				w.vcaRotSpeed = nil 
			end 
			
			attr = "rotSpeedNeg" 
			if w.rotSpeedLimit ~= nil then
				attr = "rotSpeedNegDefault" 
			end 
			
			if w[attr] ~= nil and w.vcaRotSpeedNeg ~= nil then 
				w[attr] = w.vcaRotSpeedNeg
				w.vcaRotSpeedNeg = nil
			end 
		end 
		self.spec_vca.rotSpeedFactor = nil
		self.autoRotateBackSpeed = self.spec_vca.autoRotateBackSpeed 
		self.spec_vca.autoRotateBackSpeed = nil 
	end 
	--********************************************************************

--******************************************************************************************************************************************

	if self.isServer then 
		self:vcaSetState( "hasGearsAuto", vehicleControlAddon.vcaUIShowautoShift( self ))
		self:vcaSetState( "hasGearsIdle", vehicleControlAddon.vcaUIShowidleThrottle( self ))
	end 
--******************************************************************************************************************************************
end

function vehicleControlAddon:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
	if self.spec_vca == nil then return end

	self.spec_vca.tickDt = self.spec_vca.tickDt + 0.05 * ( dt - self.spec_vca.tickDt )

	local lastIsEntered = self.spec_vca.isEntered

	if self.isClient and self.getIsEntered ~= nil and self:getIsControlled() and self:getIsEntered() then 
		self.spec_vca.isEntered = true
		self:vcaSetState( "isEnteredMP", true )

		local isBlocked = false 
		if     self.spec_drivable.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF then
		elseif self.spec_vca.ksIsOn then 
		elseif g_gui.currentListener ~= nil then 
			if     ChatDialog ~= nil and g_gui.currentListener == g_gui.screens[ChatDialog] then 
				isBlocked = false  
			else
				isBlocked = true 
			end 
		end 
		self:vcaSetState( "isBlocked", isBlocked )
	else 
		self.spec_vca.isEntered = false 
	end 	

	if self.spec_vca.isEntered and self.spec_vca.cruiseControlStateIsPressed then 
		if     self.spec_vca.cruiseControlStateTimer == nil then 
			self.spec_vca.cruiseControlStateTimer = Drivable.CRUISECONTROL_FULL_TOGGLE_TIME
		elseif self.spec_vca.cruiseControlStateTimer >= 0   then 
			self.spec_vca.cruiseControlStateTimer = self.spec_vca.cruiseControlStateTimer - dt 
		else 
			self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_FULL)
		end 
	elseif self.spec_vca.cruiseControlStateTimer ~= nil then 
		if     self.spec_vca.cruiseControlStateTimer <= 0 and self.spec_drivable.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_FULL then 
			self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_FULL)
		elseif self.spec_drivable.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF then
			self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE)
		else
			self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF)
		end 
		self.spec_vca.cruiseControlStateTimer = nil
	end 	
	self.spec_vca.cruiseControlStateIsPressed = nil 

	if self.spec_drivable.cruiseControl.state == Drivable.CRUISECONTROL_STATE_ACTIVE then 
		self.spec_vca.farmId = self:getActiveFarm()
	else 
		self.spec_vca.farmId = nil 
	end 
	
	if self.spec_vca.isEntered then
		self.spec_vca.keepCamRot     = self.spec_vca.keepRotPressed 
		self.spec_vca.keepRotPressed = false 
		
		local krToggle = self.spec_vca.kRToggleOut
		if self.spec_vca.camIsInside then 
			krToggle = self.spec_vca.kRToggleIn 
		end 
		if krToggle then 
			self.spec_vca.keepCamRot = not self.spec_vca.keepCamRot
		end
		
		self:vcaSetState( "inchingIsOn", self.spec_vca.inchingPressed )

		local isPressed = self.spec_vca.keepSpeedPressed
		if self.spec_vca.ksToggle then 
			isPressed = not isPressed
		end 
		if isPressed and not self.spec_vca.ksIsOn then 
			self:vcaSetState( "keepSpeed", self.lastSpeed * 3600 )
		end 
		self:vcaSetState( "ksIsOn", isPressed )

	elseif not self.spec_vca.isEnteredMP then 
		self.spec_vca.keepCamRot = false 
		self:vcaSetState( "inchingIsOn", false )
		self:vcaSetState( "ksIsOn", false )
		self:vcaSetState( "ksIsActive", false )
		self:vcaSetState( "keepSpeed", 0 )
	end 

	self.spec_vca.keepRotPressed   = false 
	self.spec_vca.inchingPressed   = false 
	self.spec_vca.keepSpeedPressed = false 	
	
	--*******************************************************************
	if not ( self.spec_vca.isEntered ) then 
		self.spec_vca.warningText  = ""
		self.spec_vca.warningTimer = 0
	elseif self.spec_vca.warningTimer ~= nil and self.spec_vca.warningTimer > 0  then 
		self.spec_vca.warningTimer = self.spec_vca.warningTimer - dt
	elseif self.spec_vca.warningText ~= nil  and self.spec_vca.warningText ~= "" then
		self.spec_vca.warningText = ""
	end 
	
	local newRotCursorKey = self.spec_vca.newRotCursorKey
	local i               = self.spec_enterable.camIndex
	local lastWorldRotation = self.spec_vca.camRotWorld
			
	self.spec_vca.newRotCursorKey = nil
	self.spec_vca.camRotWorld     = nil
	
	if self.spec_vca.inchingIsOn and self.spec_vca.isEntered and self.spec_drivable.cruiseControl.state == Drivable.CRUISECONTROL_STATE_ACTIVE then
		local limitThrottleRatio     = 0.75
		if self.spec_vca.limitThrottle < 11 then
			limitThrottleRatio     = 0.45 + 0.05 * self.spec_vca.limitThrottle
		else
			limitThrottleRatio     = 1.5 - 0.05 * self.spec_vca.limitThrottle
		end
		if self.spec_vca.speedLimit == nil then 
			self.spec_vca.speedLimit = self:getCruiseControlSpeed()
		end 
		self:vcaSetCruiseSpeed( self.spec_vca.speedLimit * limitThrottleRatio )
	elseif self.spec_vca.speedLimit ~= nil then 
		self:vcaSetCruiseSpeed( self.spec_vca.speedLimit )
		self.spec_vca.speedLimit = nil
	end 	
	-- overwrite or reset some values 
	--*******************************************************************
	
	-- reduce automatic brake force above 1 m/s^2
	if self.spec_motorized ~= nil and self.spec_motorized.motor ~= nil and self.spec_motorized.motor.lowBrakeForceScale ~= nil then
		if self:getIsVehicleControlledByPlayer() and not ( 0.99 < self.spec_vca.brakeForce and self.spec_vca.brakeForce < 1.01 ) then      
			if self.spec_vca.origLowBrakeForceScale == nil then 
				self.spec_vca.origLowBrakeForceScale      = self.spec_motorized.motor.lowBrakeForceScale
				self.spec_vca.origLowBrakeForceSpeedLimit = self.spec_motorized.motor.lowBrakeForceSpeedLimit
			end 			
			self.spec_motorized.motor.lowBrakeForceScale = self.spec_vca.origLowBrakeForceScale * self.spec_vca.brakeForce
			if self.spec_vca.brakeForce < 1 then 
				self.spec_motorized.motor.lowBrakeForceSpeedLimit = math.min( 1, self.spec_vca.origLowBrakeForceSpeedLimit )
			end 
		elseif self.spec_vca.origLowBrakeForceScale ~= nil then 
			self.spec_motorized.motor.lowBrakeForceScale      = self.spec_vca.origLowBrakeForceScale
			self.spec_motorized.motor.lowBrakeForceSpeedLimit = self.spec_vca.origLowBrakeForceSpeedLimit
			self.spec_vca.origLowBrakeForceScale              = nil 
			self.spec_vca.origLowBrakeForceSpeedLimit         = nil 
		end 
	end 
	
	--*******************************************************************
	-- moving direction
	local moveDir = self.movingDirection
	
	if self:getIsActive() and self.isServer then
		if math.abs( self:getLastSpeed() ) < 5 then 
			if self.spec_motorized ~= nil and self.spec_motorized.motor ~= nil then 
				local motor = self.spec_motorized.motor
				if     motor.currentDirection < 0 then 
					self:vcaSetState( "isForward", false )
				elseif motor.currentDirection > 0 then 
					self:vcaSetState( "isForward", true )
				end 
			end 
		elseif moveDir < 0 then 
			self:vcaSetState( "isForward", false )
		elseif moveDir > 0 then 
			self:vcaSetState( "isForward", true )
		end 
	end

	--*******************************************************************
	-- Keep Speed 
	if self.spec_vca.isEntered then 
		local doKeepSpeed = false 
		local doTMS       = false 

		if self.spec_vca.ksIsOn then 
			doKeepSpeed = true 
		elseif  self.spec_vca.speedLimiter
				and self.spec_drivable.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF
				and self.spec_drivable.axisForward >= 0
				and self:vcaGetShuttleCtrl()
				and not ( self.spec_vca.maxGearSpeed ~= 0 and not self.spec_motorized.motor:getUseAutomaticGearShifting() )
				then 
			doTMS = true 
		end 
	
		local keepSpeedTMS = self.spec_vca.keepSpeedTMS
		self.spec_vca.keepSpeedTMS = nil 
		
		if doKeepSpeed or doTMS then	
			local m
			if self:vcaGetShuttleCtrl() then
				m = self.spec_motorized.motor.currentDirection
			elseif self.lastSpeedReal * 3600 < 0.5 then 
				m = 0
			elseif moveDir < 0 then 
				m = -1 
			else
				m = 1
			end
			
			local sl  = self:getSpeedLimit(true)
			local slf = -math.min( sl, self.spec_drivable.cruiseControl.maxSpeedReverse )
			local slt =  math.min( sl, self.spec_drivable.cruiseControl.maxSpeed )
			if self:vcaGetShuttleCtrl() then 
				if m < 0 then 
					slt = 0 
				else 
					slf = 0 
				end 
			end 
			
			if self.spec_vca.maxGearSpeed ~= 0 and not self.spec_motorized.motor:getUseAutomaticGearShifting() and math.abs( self.spec_vca.keepSpeed ) > 0.5 then 
				if m < 0 then 
					slf = math.max( slf,-3.6 * self.spec_vca.maxGearSpeed )
					slt = math.min( slt,-3.6 * self.spec_vca.minGearSpeed )
				else 
					slf = math.max( slf, 3.6 * self.spec_vca.minGearSpeed )
					slt = math.min( slt, 3.6 * self.spec_vca.maxGearSpeed )
				end 
			end 
			
			if     self.spec_drivable.cruiseControl.state == Drivable.CRUISECONTROL_STATE_FULL then 
				if m < 0 then 
					self.spec_vca.keepSpeedTemp = slf
				else
					self.spec_vca.keepSpeedTemp = slt
				end 
				self:setCruiseControlState( Drivable.CRUISECONTROL_STATE_OFF )		
			elseif self.spec_drivable.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF then 		
				if m < 0 then 
					self.spec_vca.keepSpeedTemp = vehicleControlAddon.mbClamp( -self.spec_drivable.cruiseControl.speed, slf, slt )
				else 
					self.spec_vca.keepSpeedTemp = vehicleControlAddon.mbClamp(  self.spec_drivable.cruiseControl.speed, slf, slt )
				end 
				self:setCruiseControlState( Drivable.CRUISECONTROL_STATE_OFF )		
			end 

			local k = self.spec_vca.keepSpeed
			
			if self:vcaGetShuttleCtrl() then 
				if m < 0 then 
					k = -math.abs( k )
				else 
					k = math.abs( k )
				end 
			end 
			
			local setKsIsActive = true 
			
			if     doTMS then 
				local t = vehicleControlAddon.mbClamp( math.abs( self.spec_drivable.axisForward ), 0, 1 ) * math.min( sl, self.spec_drivable.cruiseControl.speed )
				
				local limitThrottleRatio     = 0.75
				local limitThrottleIfPressed = true
				if self.spec_vca.limitThrottle < 11 then
					limitThrottleIfPressed = false
					limitThrottleRatio     = 0.45 + 0.05 * self.spec_vca.limitThrottle
				else
					limitThrottleIfPressed = true
					limitThrottleRatio     = 1.5 - 0.05 * self.spec_vca.limitThrottle
				end
					
				if self.spec_vca.inchingIsOn == limitThrottleIfPressed then
					t = t * limitThrottleRatio
				end
				
				if keepSpeedTMS == nil then 
					keepSpeedTMS = self.lastSpeedReal * 3600 * moveDir
				end 
				
				if     ( moveDir > 0 and m < 0 )
						or ( moveDir < 0 and m > 0 ) then 
					self.spec_vca.keepSpeedTMS = nil 
					t = 0 
				else 
					local s  = self.lastSpeedReal * 3600
					local k = 0
					if m < 0 then 
						k  = math.max( 0, -keepSpeedTMS )
					else
						k  = math.max( 0, keepSpeedTMS )
					end
					local kp = math.max( s, k + dt * 0.020 )
					local km = math.min( s, k - dt * 0.002 )
					if km > sl then 
						km = math.min( s, k - dt * 0.020 )
						kp = math.max( sl, km )
					elseif self.spec_motorized.motor.smoothedLoadPercentage > 0.8 and s > 5 then 
						local f = 5 * self.spec_motorized.motor.smoothedLoadPercentage - 4 
						kp = math.min( s + dt * 0.020 * ( 1 - f ), kp )
					else 
						kp = math.min( s + 2, kp )
					end 
					
					t = vehicleControlAddon.mbClamp( t, km, kp )
					self.spec_vca.keepSpeedTMS = m * t 
				end 
				
				if t < 0.5 then 
					setKsIsActive = false 
				end 
				self:vcaSetState( "keepSpeed", m * t )
			elseif math.abs( self.spec_drivable.axisForward ) > 0.01 then 
				self.spec_vca.keepSpeedTemp = nil 
			
				local s = self.lastSpeedReal * 3600 * moveDir					
				local f = slf
				local t = slt
				local a = self.spec_drivable.axisForward
				local l = self.spec_motorized.motor:getSmoothLoadPercentage()
				
				-- joystick
				if     self:vcaGetShuttleCtrl() then 
					if not self.spec_vca.isForward then 
						a = -a 
					end 
				-- w/o shuttle control
				elseif self.spec_drivable.reverserDirection ~= nil then 
					a = a * self.spec_drivable.reverserDirection
				end 
				if     m > 0 then 
					if a > 0.01 then 
						f = math.max( f, 0.5, s )
						a = a * vehicleControlAddon.mbClamp( 1 - l, 0.2, 1 )
						t = math.min( t, math.max( k, s + 1 ) )
					else
						f = 0 
						t = math.min( t, s )
					end 
					if t < f then 
						t = f 
					end 
					self.spec_vca.lastKSStopTimer = g_currentMission.time + 2000 
				elseif m < 0 then
					if a < -0.01 then 
						t = math.min( t, -0.5, s )
						a = a * vehicleControlAddon.mbClamp( 1 - l, 0.2, 1 )
						f = math.max( f, math.min( k, s - 1 ) )
					else 
						t = 0 
						f = math.max( f, s )
					end 
					if f > t then 
						f = t 
					end 
					self.spec_vca.lastKSStopTimer = g_currentMission.time + 2000 
				elseif self.spec_vca.lastKSStopTimer == nil then 
					self.spec_vca.lastKSStopTimer = g_currentMission.time 
				elseif g_currentMission.time < self.spec_vca.lastKSStopTimer then 
					-- wait two seconds 
					f = 0
					t = 0
				end 
				
				if f < -sl then 
					f = -sl 
				end 
				if t > sl then	
					t = sl 
				end 
				
				self:vcaSetState( "keepSpeed", vehicleControlAddon.mbClamp( k + a * 0.01 * dt, f, t )  )
				self.spec_drivable.axisForward = 0
				
			else
				if self.spec_vca.keepSpeedTemp ~= nil then 		
					if not self:vcaGetShuttleCtrl() then 
						self.spec_vca.keepSpeedTemp = vehicleControlAddon.mbClamp( self.spec_vca.keepSpeedTemp, slf, slt )
					elseif m < 0 then 	
						self.spec_vca.keepSpeedTemp = vehicleControlAddon.mbClamp( -math.abs( self.spec_vca.keepSpeedTemp ), slf, slt )
					else 
						self.spec_vca.keepSpeedTemp = vehicleControlAddon.mbClamp( math.abs( self.spec_vca.keepSpeedTemp ), slf, slt )
					end 
					if math.abs( k - self.spec_vca.keepSpeedTemp ) < 0.1 then 
						k = self.spec_vca.keepSpeedTemp
						self.spec_vca.keepSpeedTemp = nil 
					else 
						local l = self.spec_motorized.motor:getSmoothLoadPercentage()
						local a = vehicleControlAddon.mbClamp( 1 - l, 0.2, 1 )
						k = k + vehicleControlAddon.mbClamp( self.spec_vca.keepSpeedTemp - k, -a * 0.01 * dt, a * 0.01 * dt )
					end 
				end 
				
				self:vcaSetState( "keepSpeed", vehicleControlAddon.mbClamp( k, slf, slt ) )
			end 
			self:vcaSetState( "ksIsActive", setKsIsActive )
		else 
			self.spec_vca.keepSpeedTemp = nil 
			self:vcaSetState( "ksIsActive", false )
		end 
	end 
	
	--*******************************************************************
	-- Camera Rotation
	if      self:getIsActive() 
			and self.isClient 
			and self.spec_vca ~= nil 
			and self:vcaIsVehicleControlledByPlayer()
			and self:vcaIsValidCam() then
			
		local camera  = self.spec_enterable.cameras[i]
		local rotIsOn = self.spec_vca.camRotOutside
		local revIsOn = self.spec_vca.camRevOutside 
		self.spec_vca.camIsInside   = false
		
		if camera.isInside then 
			self.spec_vca.camIsInside = true 

			if      g_gameSettings:getValue(GameSettings.SETTING.IS_HEAD_TRACKING_ENABLED)
					and isHeadTrackingAvailable() 
					and camera.headTrackingNode ~= nil
					and not VCAGlobals.rotateHeadTracking then 
				rotIsOn = 0 
				revIsOn = false 
			else 
				rotIsOn = self.spec_vca.camRotInside
				revIsOn = self.spec_vca.camRevInside
			end 
		end 
		
		if self.spec_crabSteering ~= nil and rotIsOn > 0 then  
			local spec = self.spec_crabSteering
			if not ( spec.steeringModes == nil or spec.state == nil or spec.steeringModes[spec.state] == nil ) then 
				entry = spec.steeringModes[spec.state]
				local mode = 0
				if     entry.inputAction == nil then 
				-- do nothing  
				elseif entry.inputAction == "CRABSTEERING_ALLWHEEL" then 
					mode = 2
				elseif entry.inputAction == "CRABSTEERING_CRABLEFT"
						or entry.inputAction == "CRABSTEERING_CRABRIGHT" then 
					mode = 3
				end 
				if mode <= 0 then 
					if     entry.name == g_i18n:getText( "action_steeringModeAllWheel"   ) then 
						mode = 2
					elseif entry.name == g_i18n:getText( "action_steeringModeFrontWheel" ) then 
						mode = 1
					elseif entry.name == g_i18n:getText( "action_steeringModeCrabLeft"   ) then 
						mode = 3
					elseif entry.name == g_i18n:getText( "action_steeringModeCrabRight"  ) then 
						mode = 3
					end 
				end 
				if     mode == 2 then 
					rotIsOn = rotIsOn * 1.25 
				elseif mode == 3 then 
					rotIsOn = rotIsOn * 0.8 
				end 
			end 
		end 
		
	--vcaDebugPrint( "Cam: "..tostring(rotIsOn)..", "..tostring(revIsOn)..", "..tostring(self.spec_vca.lastCamIndex)..", "..tostring(self.spec_vca.lastCamFwd))

		if     self.spec_vca.lastCamIndex == nil 
				or self.spec_vca.lastCamIndex ~= i then
				
			if      self.spec_vca.lastCamIndex ~= nil
					and self.spec_vca.zeroCamRotY  ~= nil
					and self.spec_vca.lastCamRotY  ~= nil
					and self:vcaIsValidCam( self.spec_vca.lastCamIndex ) then
				local oldCam = self.spec_enterable.cameras[self.spec_vca.lastCamIndex]
				
				if (oldCam.resetCameraOnVehicleSwitch == nil and g_gameSettings:getValue("resetCamera")) or oldCam.resetCameraOnVehicleSwitch then
				-- camera is automatically reset
				elseif ( not oldCam.isInside and self.spec_vca.camRotOutside > 0 )
						or (     oldCam.isInside and self.spec_vca.camRotInside  > 0 ) then 
					oldCam.rotY = vehicleControlAddon.normalizeAngleCam( self.spec_vca.zeroCamRotY + oldCam.rotY - self.spec_vca.lastCamRotY )
				end 
			end 
				
			self.spec_vca.lastCamIndex = self.spec_enterable.camIndex
			self.spec_vca.zeroCamRotY  = camera.rotY
			self.spec_vca.lastCamRotY  = camera.rotY 
			self.spec_vca.lastCamFwd   = nil
			self.spec_vca.camRotWorld  = nil
			
			if self.spec_vca.camIsInside then 
				self.spec_vca.keepCamRot = self.spec_vca.kRToggleIn 
			else 
				self.spec_vca.keepCamRot = self.spec_vca.kRToggleOut 
			end 
						
		elseif rotIsOn > 0
				or revIsOn
				or self.spec_vca.keepCamRot
				or lastWorldRotation ~= nil
				or newRotCursorKey   ~= nil then 

			local pi2 = math.pi / 2
			local eps = 1e-6
			oldRotY = camera.rotY
			local diff = oldRotY - self.spec_vca.lastCamRotY
			
			if     self.spec_vca.keepCamRot then 
				self.spec_vca.camRotWorld = vehicleControlAddon.vcaGetRelativeYRotation(g_currentMission.terrainRootNode,self:vcaGetSteeringNode())
			elseif lastWorldRotation ~= nil then 
			-- reset to old rotation 	
			elseif newRotCursorKey ~= nil then
				self.spec_vca.zeroCamRotY = camera.origRotY + newRotCursorKey
			elseif rotIsOn > 0 then
				self.spec_vca.zeroCamRotY = self.spec_vca.zeroCamRotY + diff
			else
				self.spec_vca.zeroCamRotY = camera.rotY
			end
				
		--diff = math.abs( vehicleControlAddon.vcaGetAbsolutRotY( self, i ) )
			local isRev = false
			local aRotY = vehicleControlAddon.normalizeAngle( vehicleControlAddon.vcaGetAbsolutRotY( self, i ) - camera.rotY + self.spec_vca.zeroCamRotY )
			if -pi2 < aRotY and aRotY < pi2 then
				isRev = true
			end
			
			if revIsOn and not ( self.spec_vca.keepCamRot ) then
				if     newRotCursorKey ~= nil then
				-- nothing
				elseif self.spec_vca.lastCamFwd == nil or self.spec_vca.lastCamFwd ~= self.spec_vca.isForward then
					if isRev == self.spec_vca.isForward then
						if math.abs( self.spec_vca.zeroCamRotY - math.pi ) < 0.1 then 
							self.spec_vca.zeroCamRotY = 0
						elseif self.spec_vca.zeroCamRotY > math.pi+math.pi - 0.1 then 
							self.spec_vca.zeroCamRotY = math.pi
						elseif self.spec_vca.zeroCamRotY < 0.1 then 
							self.spec_vca.zeroCamRotY = math.pi
						elseif self.spec_vca.zeroCamRotY >= math.pi then 
							self.spec_vca.zeroCamRotY = self.spec_vca.zeroCamRotY - math.pi 
						else 
							self.spec_vca.zeroCamRotY = self.spec_vca.zeroCamRotY + math.pi 
						end 
						isRev = not isRev						
					end
				end
				self.spec_vca.lastCamFwd = self.spec_vca.isForward
			end
			
			local newRotY = self.spec_vca.zeroCamRotY
			
			if self.spec_vca.keepCamRot then 
				if newRotCursorKey ~= nil then
					newRotY = camera.origRotY + newRotCursorKey 
				else 
					newRotY = camera.rotY 
				end 
				if lastWorldRotation ~= nil then 
					newRotY = newRotY + ( self.spec_vca.camRotWorld - lastWorldRotation )
				end 
			elseif rotIsOn > 0 then
				
				local f = 0
				if     self.rotatedTime > 0 then
					f = self.rotatedTime / self.maxRotTime
				elseif self.rotatedTime < 0 then
					f = self.rotatedTime / self.minRotTime
				end
				if f < 0.1 then
					f = 0
				else
					f = 1.2345679 * ( f - 0.1 ) ^2 / 0.81
				--f = 1.1111111 * ( f - 0.1 )
				end
				f = f * 0.5 * rotIsOn
				if self.rotatedTime < 0 then
					f = -f
				end
				
				local g = self.spec_vca.lastFactor
				self.spec_vca.lastFactor = self.spec_vca.lastFactor + vehicleControlAddon.mbClamp( f - self.spec_vca.lastFactor, -VCAGlobals.cameraRotTime*dt, VCAGlobals.cameraRotTime*dt )
				if math.abs( self.spec_vca.lastFactor - g ) > 0.01 then
					f = self.spec_vca.lastFactor
				else
					f = g
				end
				
				if isRev then
					newRotY = newRotY - VCAGlobals.cameraRotFactorRev * f				
				else
					newRotY = newRotY + VCAGlobals.cameraRotFactor * f
				end	
				
			else
				self.spec_vca.lastFactor = 0
			end
			
			local spec_cabView = nil 
			if self["spec_FS22_CabView.cabView"] ~= nil then 
				spec_cabView = self["spec_FS22_CabView.cabView"]
			elseif self.spec_cabView ~= nil then 
				spec_cabView = self.spec_cabView 
			end 
			
			if not ( type( spec_cabView ) == "table" and camera.isInside ) then  
				camera.rotY = vehicleControlAddon.normalizeAngleCam( newRotY )	
			elseif spec_cabView.rotationOffset == nil then 
				camera.rotY = math.max( 0, math.min( newRotY, math.pi+math.pi ))
			else 
				local minRot = -0.1 * math.pi+spec_cabView.rotationOffset
				local maxRot =  2.1 * math.pi+spec_cabView.rotationOffset
				camera.rotY = math.max( minRot, math.min( newRotY, maxRot ))
			end 
		end
		
		self.spec_vca.lastCamRotY = camera.rotY
	elseif self.spec_vca.lastCamIndex ~= nil then 
		if self.spec_vca.zeroCamRotY and self:vcaIsValidCam( self.spec_vca.lastCamIndex ) then
			self.spec_enterable.cameras[self.spec_vca.lastCamIndex].rotY = self.spec_vca.zeroCamRotY 
		end 
		self.spec_vca.lastCamIndex = nil
		self.spec_vca.zeroCamRotY  = nil
		self.spec_vca.lastCamRotY  = nil
		self.spec_vca.lastCamFwd   = nil
		self.spec_vca.camIsInside  = nil
		self.spec_vca.keepCamRot   = false  
	end	

--******************************************************************************************************************************************
	if self.isServer and self:getIsActive() and self.spec_vca ~= nil and self.spec_vca.isInitialized then  
		if      self.spec_vca.diffManual 
				and not self.spec_vca.diffHasF
				and not self.spec_vca.diffHasM
				and not self.spec_vca.diffHasB then 
			self:vcaSetState( "diffManual", false )
		end 
	
		local spec = self.spec_motorized 
		local gearRatio = 0
		if spec.motor ~= nil and spec.motor.gearRatio ~= nil then 
			gearRatio = spec.motor.gearRatio
		end 
		
		if      VCAGlobals.turnOffAWDSpeed > 0
				and self.lastSpeed * 3600 > VCAGlobals.turnOffAWDSpeed
				and self:vcaIsVehicleControlledByPlayer() then 
			self:vcaSetState( "diffLockFront", false )
			self:vcaSetState( "diffLockBack",  false )
			if self.spec_vca.diffFrontAdv then  
				self:vcaSetState( "diffLockAWD", false )
			end 
		end 

		local f, m, b = self:vcaGetDiffState()
		
		local updateDiffs  = false 
		local vehicleSpeed = self.lastSpeedReal*1000*moveDir
		local avgWheelSpeed,n=0,0
		local minWheelSpeed, maxWheelSpeed
		
		self.spec_vca.debugS = debugFormat("%6.3f",vehicleSpeed)
		
		local function updateWheelSpeed( wheel )
			if wheel.node ~= nil and wheel.wheelShape ~= nil then 
				local s = getWheelShapeAxleSpeed(wheel.node, wheel.wheelShape) * wheel.radius 
				wheel.vcaNoPower = false 
				if wheel.vcaSpeed == nil then 
					wheel.vcaSpeed = s 
				else 
					wheel.vcaSpeed = wheel.vcaSpeed + 0.08 * ( s - wheel.vcaSpeed )
				end 
				self.spec_vca.debugS = self.spec_vca.debugS..debugFormat(", %6.3f",wheel.vcaSpeed)
				avgWheelSpeed = avgWheelSpeed + wheel.vcaSpeed
				n = n + 1 
				if minWheelSpeed == nil or minWheelSpeed > wheel.vcaSpeed then 
					minWheelSpeed = wheel.vcaSpeed 
				end 
				if maxWheelSpeed == nil or maxWheelSpeed < wheel.vcaSpeed then 
					maxWheelSpeed = wheel.vcaSpeed 
				end 
			end 
		end 
		
		for d,diff in pairs(spec.differentials) do 
			diff.vcaEnabled = 3 
			if diff.diffIndex1IsWheel then
				local wheel = self:getWheelFromWheelIndex( diff.diffIndex1 )
				updateWheelSpeed( wheel )
			end
			if diff.diffIndex2IsWheel then
				local wheel = self:getWheelFromWheelIndex( diff.diffIndex2 )
				updateWheelSpeed( wheel )
			end 
		end 					
			
		if n > 1 then avgWheelSpeed = avgWheelSpeed / n end 	
			
		if minWheelSpeed ~= nil and avgWheelSpeed ~= nil and maxWheelSpeed ~= nil then 
			self.spec_vca.debugS = self.spec_vca.debugS..debugFormat("\n%6.3f .. %6.3f .. %6.3f",minWheelSpeed, avgWheelSpeed, maxWheelSpeed)
		end 
		
		local ws = 0
		if     math.abs( vehicleSpeed ) < 0.1 then 
		elseif gearRatio > 0 and vehicleSpeed >  0.1 and maxWheelSpeed ~= nil and maxWheelSpeed > vehicleSpeed then 
			ws = maxWheelSpeed / vehicleSpeed - 1
		elseif gearRatio < 0 and vehicleSpeed < -0.1 and minWheelSpeed ~= nil and minWheelSpeed < vehicleSpeed then 
			ws = minWheelSpeed / vehicleSpeed - 1
		end 
		self.spec_vca.maxWheelSlip = self.spec_vca.maxWheelSlip + 0.04 * ( ws - self.spec_vca.maxWheelSlip )
		
		if minWheelSpeed == nil or minWheelSpeed < 1 then 
			minWheelSpeed = 1 
		end 
		if maxWheelSpeed == nil or maxWheelSpeed > -1 then 
			maxWheelSpeed = -1 
		end 

    local function getDiffSpeed(index)
			local speed1, speed2, diffOfWheels
			local diff = spec.differentials[index]
			if diff.diffIndex1IsWheel then
				local wheel = self:getWheelFromWheelIndex( diff.diffIndex1 )
				speed1 = wheel.vcaSpeed
				diffOfWheels = true  
			else
				local s1,s2 = getDiffSpeed(diff.diffIndex1+1);
				speed1 = (s1+s2)/2
				diffOfWheels = false  
			end
			if diff.diffIndex2IsWheel then
				local wheel = self:getWheelFromWheelIndex( diff.diffIndex2 )
				speed2 = wheel.vcaSpeed
			else
				local s1,s2 = getDiffSpeed(diff.diffIndex2+1);
				speed2 = (s1+s2)/2
				diffOfWheels = false 
			end
			return speed1,speed2,diffOfWheels
    end
		
		local function disableDiff( index, isWheel )
			if isWheel then
				local wheel = self:getWheelFromWheelIndex( index )
				wheel.vcaNoPower = true 
			else 
			--vcaDebugPrint( "Disabling diff(3) "..tostring(index) )
				local diff = spec.differentials[index+1]
				diff.vcaEnabled = 0
				disableDiff( diff.diffIndex1, diff.diffIndex1IsWheel ) 
				disableDiff( diff.diffIndex2, diff.diffIndex2IsWheel ) 
			end 
		end 

    local function getClosedTorqueRatio(r0, s1, s2, m0)
			local r = r0 
			local m = Utils.getNoNil( m0, vehicleControlAddon.speedRatioClosed1 )
			
			if gearRatio < 0 then 
				s1 = -s1
				s2 = -s2 
			end 
				
			if     s1 < 0.1389 and s2 < 0.1389 then 
			elseif not ( -0.2778 < s1 and s1 < 90 
			         and -0.2778 < s2 and s2 < 90 ) then 
				m = math.max( m, vehicleControlAddon.speedRatioClosed1 )
			elseif s1 < s2 then  
				q = ( math.max( s1, 0 ) / s2 ) 
				r =  1 - q * ( 1 - r )
				m = math.max( m, 1 + ( vehicleControlAddon.speedRatioClosed1 - 1 ) * ( 1 - q ) )
			elseif s2 < s1 then 
				q = ( math.max( s2, 0 ) / s1 ) 
				r = r * q
				m = math.max( m, 1 + ( vehicleControlAddon.speedRatioClosed1 - 1 ) * ( 1 - q ) )
			end 
			
			if math.abs( r - r0 ) < 0.05 then 
				r = r0 
			end 
							
			return r, m
		end
		
		local function setDiff( index, newState, torqueRatioOpen, advanceSpeed )
		
			-- torqueRatio = 1 => all power goes to 1st part 
			-- torqueRatio = 0 => all power goes to 2nd part 
		
			if index <= 0 then 
				return 0
			end 
			
			local diff  = spec.differentials[index]		
			
			if newState == nil or diff.vcaEnabled <= 0 then 
				newState = 0 
			end 
			
			local r, m   = diff.vcaTorqueRatio, diff.vcaMaxSpeedRatio
			local r1, r2 = vehicleControlAddon.minTorqueRatio, 1-vehicleControlAddon.minTorqueRatio
			
			if     newState == 1 then 
				if torqueRatioOpen ~= nil then 
					r = torqueRatioOpen
				end 

				if r1 <= r and r <= r2 then   
					local s1,s2 = getDiffSpeed(index)
					if self.spec_vca.antiSlip then 
						r = vehicleControlAddon.mbClamp( getClosedTorqueRatio( r, s1, s2 ), r1, r2 )	
					elseif vehicleControlAddon.distributeTorqueOpen then
						-- inverse torque ratio => put more torque on turning wheel
						r = vehicleControlAddon.mbClamp( getClosedTorqueRatio( r, s2, s1 ), r1, r2 )
          end 						
				end 				
				m = vehicleControlAddon.speedRatioOpen
			elseif newState == 2 then 			

				local s1,s2,dow = getDiffSpeed(index)
				
				if dow then 
					m = vehicleControlAddon.speedRatioClosed1
				elseif vehicleControlAddon.speedRatioClosed0 >= 0 then 
					m = vehicleControlAddon.speedRatioClosed0 
				end 
					
				-- advance speed by 7% (minus 2% error)
				if     torqueRatioOpen == nil or not self.spec_vca.diffFrontAdv then  
				elseif torqueRatioOpen > r2 then 
					m  = math.max( m, vehicleControlAddon.speedRatioClosed2 )
					s1 = s1 * 1.035
					s2 = s1 / 1.035
				elseif torqueRatioOpen < r1 then 
					m  = math.max( m, vehicleControlAddon.speedRatioClosed2 )
					s1 = s1 / 1.035
					s2 = s2 * 1.035
				end 
				
				r, m = getClosedTorqueRatio( r, s1, s2, m )
				r = vehicleControlAddon.mbClamp( r, r1, r2 )
			end 
			
			local ovr = "vcaOvrDT"..tostring(index) 
			if self[ovr] ~= nil and self[ovr] >= 0 then 
				r = self[ovr]
			end 
			local ovr = "vcaOvrDM"..tostring(index) 
			if self[ovr] ~= nil and self[ovr] >= 0 then 
				m = self[ovr] 
			end 
			
			if newState > 0 then 
				if     r > r2 then 
					if diff.diffIndex2IsWheel then
						r = r2
					else 
						r = 1
						diff.vcaEnabled = 1  						
						disableDiff( diff.diffIndex2, diff.diffIndex2IsWheel ) 
					end 
				elseif r < r1 then 
					if diff.diffIndex1IsWheel then                              
						r = r1
					else 
						r = 0
						diff.vcaEnabled = 2                            
						disableDiff( diff.diffIndex1, diff.diffIndex1IsWheel )
					end 			
				end 
				if m < 1 then 
					m = 1 
				end 
			end 			
			
			local eOld = ( r1 <= diff.torqueRatio and diff.torqueRatio <= r2 ) 
			local eNew = ( r1 <= r and r <= r2 )
			
			if     eOld ~= eNew then 
				diff.torqueRatio    = r 
				diff.maxSpeedRatio  = m
				updateDiffs        = true 
			elseif math.abs( diff.torqueRatio - r ) > 1e-3 or math.abs( diff.maxSpeedRatio - m ) > 1e-3 then 
				diff.torqueRatio   = r 
				diff.maxSpeedRatio = m
				if diff.vcaIndex ~= nil and diff.vcaIndex >= 0 then 
					updateDifferential( spec.motorizedNode, diff.vcaIndex, diff.torqueRatio, diff.maxSpeedRatio )
				end 
			end 
					
			return newState 
		end 
		
		local vanilla = false 
		if f <= 0 and m <= 0 and b <= 0 then 
			if self.spec_vca.diffHasF or self.spec_vca.diffHasM or self.spec_vca.diffHasB then
				for i=#spec.differentials,1,-1 do 
					setDiff( i, 0 )
				end 
			end 
		else 
			for i=#spec.differentials,1,-1 do 
				local diff = spec.differentials[i] 
				if     diff.vcaMode == 'M' then 
					local o = diff.vcaTorqueRatioOpen 
					if o ~= nil and self.spec_vca.diffLockSwap then 
						o = 1 - o 
					end 
					setDiff( i, m, o ) 
				elseif diff.vcaMode == 'B' then 
					setDiff( i, b ) 
				elseif diff.vcaMode == 'F' then 
					setDiff( i, f ) 
				else 
					setDiff( i, 0 )
				end 
			end 
		end 
				
		if updateDiffs then 		
			--re-create all diffs
			removeAllDifferentials( spec.motorizedNode ) 

			local diffMap = {}			
			local j = 0
			for i, differential in pairs(spec.differentials) do
				if     differential.vcaEnabled == 1 then 	
					if not differential.diffIndex1IsWheel then
						diffMap[i-1] = diffMap[differential.diffIndex1]
					else
						print("Error: VCA differential "..tostring(i-1).." is partly disabled ("..tostring(differential.vcaEnabled)..") but contains wheels")
					end 
				elseif differential.vcaEnabled == 2 then 
					if not differential.diffIndex2IsWheel then
						diffMap[i-1] = diffMap[differential.diffIndex2]
					else
						print("Error: VCA differential "..tostring(i-1).." is partly disabled ("..tostring(differential.vcaEnabled)..") but contains wheels")
					end 
				elseif differential.vcaEnabled == 3 then 
					diffMap[i-1] = j
					j = j + 1
				end 
			end 
			
			j = 0
			for i, differential in pairs(spec.differentials) do
				vcaDebugPrint("Diff "..tostring(i-1).." s: "..tostring(differential.vcaEnabled)
										..", v: "..tostring(differential.vcaMode)
										..", t: "..tostring(differential.torqueRatio)
										..", m: "..tostring(differential.maxSpeedRatio))
				if differential.vcaEnabled == 3 then 
					local diffIndex1 = differential.diffIndex1
					local diffIndex2 = differential.diffIndex2
					if differential.diffIndex1IsWheel then
						local wheel = self:getWheelFromWheelIndex(diffIndex1)
						diffIndex1 = wheel.wheelShape
					else 
						vcaDebugPrint("1: New index of "..tostring(diffIndex1).." is "..tostring(diffMap[diffIndex1]))
						diffIndex1 = diffMap[diffIndex1]
					end
					if differential.diffIndex2IsWheel then
						local wheel = self:getWheelFromWheelIndex(diffIndex2)
						diffIndex2 = wheel.wheelShape
					else 
						vcaDebugPrint("2: New index of "..tostring(diffIndex2).." is "..tostring(diffMap[diffIndex2]))
						diffIndex2 = diffMap[diffIndex2]
					end
					if diffIndex1 == nil or diffIndex2 == nil then 
						print("Error: VCA calculation of differential "..tostring(i-1)
																				.." failed: "..tostring(diffIndex1)
																							.. ", "..tostring(differential.diffIndex1)
																							.. ", "..tostring(differential.diffIndex1IsWheel)
																							.. ", "..tostring(diffIndex2)
																							.. ", "..tostring(differential.diffIndex2)
																							.. ", "..tostring(differential.diffIndex2IsWheel))
					else 
						differential.vcaIndex = j
						j = j + 1 
						addDifferential( spec.motorizedNode,
														 diffIndex1,
														 differential.diffIndex1IsWheel,
														 diffIndex2,
														 differential.diffIndex2IsWheel,
														 differential.torqueRatio,
														 differential.maxSpeedRatio )
					end 
				else 
					differential.vcaIndex = -1
				end 
			end
			self:updateMotorProperties()
		end 
		
		self.spec_vca.debugD = nil
	--if VCAGlobals.debugPrint then 
	--	for i, differential in pairs(spec.differentials) do
	--		if self.spec_vca.debugD == nil then 
	--			self.spec_vca.debugD = ""
	--		else 
	--			self.spec_vca.debugD = self.spec_vca.debugD .. string.format("\n")
	--		end 
	--
	--		local s1,s2 = getDiffSpeed(i)
	--		
	--		self.spec_vca.debugD = self.spec_vca.debugD .. string.format( "%d: (%d, %d, %s), speed: %6.3f, %6.3f => tr: %6.3f sr: %6.1f",
	--																											i, 
	--																											Utils.getNoNil( differential.vcaEnabled, -1 ),
	--																											Utils.getNoNil( differential.vcaIndex, -1 ),
	--																											Utils.getNoNil( differential.vcaMode, '?' ),
	--																											s1, s2,
	--																											differential.torqueRatio,
	--																											differential.maxSpeedRatio )
	--	end 
	--end 
	end 
	
--******************************************************************************************************************************************
-- path recording 
	if not self.spec_vca.isEntered or self.spec_vca.snapIsOn then 
		self:vcaSetState( "pathRecording", false ) 
		self.spec_vca.prevPathRecording = nil 
	end 
	
	if self.spec_vca.pathRecording then 
		local wx,_,wz = getWorldTranslation( self:vcaGetSteeringNode() )
		if self.spec_vca.currentPath == nil or not ( self.spec_vca.prevPathRecording ) then 
			local lx,_,lz = localDirectionToWorld( self:vcaGetSteeringNode(), 0, 0, 1 )			
			self.spec_vca.currentPath = {} 
			self.spec_vca.lastPathX = wx 
			self.spec_vca.lastPathZ = wz 
			self.spec_vca.lastPathA = math.atan2(lx,lz) 
		else 
			local dx = wx - self.spec_vca.lastPathX
			local dz = wz - self.spec_vca.lastPathZ
			local l2 = dx*dx + dz*dz
			if l2 >= 0.25 then 
				local a = math.atan2( dx, dz )
				local deltaA = math.abs( vehicleControlAddon.normalizeAngle( a - self.spec_vca.lastPathA ) )
				if l2 >= 25 or deltaA > 0.1309 then -- 90° divided by 12
					table.insert( self.spec_vca.currentPath, { self.spec_vca.lastPathX, self.spec_vca.lastPathZ, a, math.sqrt( l2 ) })
					self.spec_vca.lastPathX = wx 
					self.spec_vca.lastPathZ = wz 
					self.spec_vca.lastPathA = a 
				end
			end 
		end 
		self.spec_vca.prevPathRecording = true 
	elseif self.spec_vca.prevPathRecording and type( self.spec_vca.currentPath ) == "table" then 
		self.spec_vca.prevPathRecording = nil 
		local wx,_,wz = getWorldTranslation( self:vcaGetSteeringNode() )
		local dx = wx - self.spec_vca.lastPathX
		local dz = wz - self.spec_vca.lastPathZ
		local l2 = dx*dx + dz*dz
		if l2 >= 0.25 then 
			local a = math.atan2( dx, dz )
			table.insert( self.spec_vca.currentPath, { self.spec_vca.lastPathX, self.spec_vca.lastPathZ, a, math.sqrt( l2 ) })
		end
	end 

--******************************************************************************************************************************************
-- ProSeed 

	if self.spec_vca.snapPossible and not self.spec_vca.gpsProSeed then  
		self.spec_vca.gpsProSeed  = true 
		if self.getHasGuidanceSystem == nil then 
			self.getHasGuidanceSystem = vehicleControlAddon.vcaGetHasGuidanceSystem
		else 
			self.getHasGuidanceSystem = Utils.overwrittenFunction( self.getHasGuidanceSystem, vehicleControlAddon.vcaGetHasGuidanceSystem )
		end 
		if self.getGuidanceData == nil then 
			self.getGuidanceData = vehicleControlAddon.vcaGetGuidanceData
		else 
			self.getGuidanceData = Utils.overwrittenFunction( self.getGuidanceData,      vehicleControlAddon.vcaGetGuidanceData )
		end 
	end 	
	
--******************************************************************************************************************************************
-- time to shift 
	if     self.spec_motorized.motor.gearChangeTimeOrig == nil then 
		self.spec_vca.lastGearShiftTime = vehicleControlAddon.shiftTimeDefault

	elseif self.spec_vca.lastGearShiftTime ~= self.spec_vca.gearShiftTime then 
		self.spec_vca.lastGearShiftTime = self.spec_vca.gearShiftTime
		
		if self.spec_vca.origGearShiftTime == nil then 
			self.spec_vca.origGearShiftTime = self.spec_motorized.motor.gearChangeTimeOrig
		end
		
		local gearShiftTime = self.spec_vca.origGearShiftTime
		if self.spec_vca.gearShiftTime ~= vehicleControlAddon.shiftTimeDefault then 
			gearShiftTime = vehicleControlAddon.shiftTimes[self.spec_vca.gearShiftTime]
		end
		if gearShiftTime ~= nil then 
			self.spec_motorized.motor:setGearChangeTime( gearShiftTime )
		end 
	end 
			 
	if     self.spec_motorized.motor.groupType       == nil 
			or self.spec_motorized.motor.groupChangeTime == nil then 
		self.spec_vca.lastGrpShiftTime = vehicleControlAddon.shiftTimeDefault
		
	elseif self.spec_vca.lastGrpShiftTime ~= self.spec_vca.grpShiftTime then 
		self.spec_vca.lastGrpShiftTime = self.spec_vca.grpShiftTime
		
		if self.spec_vca.origGrpShiftTime == nil then 
			self.spec_vca.origGroupType    = self.spec_motorized.motor.groupType
			self.spec_vca.origGrpShiftTime = self.spec_motorized.motor.groupChangeTime
		end
		
		local groupType       = self.spec_vca.origGroupType
		local groupChangeTime = self.spec_vca.origGrpShiftTime
		if self.spec_vca.grpShiftTime ~= vehicleControlAddon.shiftTimeDefault then 
			groupChangeTime = vehicleControlAddon.shiftTimes[self.spec_vca.grpShiftTime]
			if groupChangeTime > 0 then 
				groupType = VehicleMotor.TRANSMISSION_TYPE.DEFAULT
			else 
				groupType = VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT
			end 
		end
		if groupType ~= nil then 
			self.spec_motorized.motor.groupType = groupType
		end 
		if groupChangeTime ~= nil then 
			self.spec_motorized.motor.groupChangeTime = groupChangeTime
		end 
	end 

end  

function vehicleControlAddon:onUpdateTick( dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected )
	if self.isClient and isActiveForInputIgnoreSelection then
		vehicleControlAddon.updateActionEvents( self )
	end 
end 

function vehicleControlAddon:onPostUpdate( dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected ) 
	if self.isServer then 
		local gearRatio, minGearSpeed, maxGearSpeed = 0, 0, 0
		if       self:getIsVehicleControlledByPlayer()
				and self.spec_motorized                     ~= nil 
				and self.spec_motorized.motor               ~= nil 	
				and self.spec_motorized.motorizedNode       ~= nil
				and next(self.spec_motorized.differentials) ~= nil
				and self:getIsMotorStarted()
				then 
			local motor = self.spec_motorized.motor 
			if      motor.gear ~= nil
					and motor.currentGears ~= nil
					and motor.currentGears[motor.gear] ~= nil
					then
				gearRatio    = motor.currentGears[motor.gear].ratio * motor:getGearRatioMultiplier()
				local factor = math.pi / ( math.abs( gearRatio ) * 30 ) 
				minGearSpeed = motor.minRpm * factor
				maxGearSpeed = motor.maxRpm * factor
			end 
		end 
		self:vcaSetState( "gearRatio",    gearRatio )
		self:vcaSetState( "minGearSpeed", minGearSpeed )
		self:vcaSetState( "maxGearSpeed", maxGearSpeed )
	end 
end 

function vehicleControlAddon:onDraw()

	if self.spec_vca.isEntered then

		if self.spec_vca.warningText ~= nil and self.spec_vca.warningText ~= "" then
			g_currentMission:addExtraPrintText( self.spec_vca.warningText )
		end		
		
		local refPos = {}
		refPos.speedMeter = { gaugeCenterX          = 0.8,
		                      gaugeCenterY          = 0.2,
													speedIndicatorRadiusY = 0.15,
													gearTextSize          = 0.02,
                          gearBgOverlayX        = 0.8,
													gearBgOverlayY        = 0.2,
													gearBgOverlayWidth    = 0.2,
                          gearBgOverlayHeight   = 0.2,
													gaugeBackgroundElementOverlayWidth = 0.2 }

		local x = refPos.speedMeter.gaugeCenterX
		local y = refPos.speedMeter.gaugeCenterY + refPos.speedMeter.speedIndicatorRadiusY * 1.6
		local l = refPos.speedMeter.gearTextSize --getCorrectTextSize(0.02)
		local w = 0.015 * vehicleControlAddon.getUiScale()
		local h = w * g_screenAspectRatio
		
		setTextAlignment( RenderText.ALIGN_CENTER ) 
		setTextVerticalAlignment( RenderText.VERTICAL_ALIGN_MIDDLE )
		
		local lx,_,lz = localDirectionToWorld( self:vcaGetSteeringNode(), 0, 0, 1 )			
		local d = 0
		if not self.spec_vca.snapIsOn then 
			d = math.atan2( lx, lz )
		elseif self.spec_vca.snapDirection == 1 then 
			d = self.spec_vca.lastSnapAngle 
		elseif self.spec_vca.snapDirection == 2 then 
			d = self.spec_vca.lastSnapAngle + 0.5 * math.pi 
		elseif self.spec_vca.snapDirection == 3 then 
			d = self.spec_vca.lastSnapAngle + math.pi 
		elseif self.spec_vca.snapDirection == 4 then 
			d = self.spec_vca.lastSnapAngle - 0.5 * math.pi 
		else
			d = math.atan2( lx, lz )
		end 
		local curSnapAngle, curSnapOffset1, curSnapOffset2 = self:vcaGetCurrentSnapAngle( d )
		
		if self.spec_vca.drawHud then 

			local posX   = refPos.speedMeter.gearBgOverlayX     
			local posY   = refPos.speedMeter.gearBgOverlayY     
			local width  = refPos.speedMeter.gearBgOverlayWidth 
			local height = refPos.speedMeter.gearBgOverlayHeight
			
			height = width * g_screenAspectRatio
			if height > posY then 
				height = posY 
				posY   = 0 
			else 
				posY   = 0.5 * ( posY - height  )
			end 
		
			local f, m, b = self:vcaGetDiffState()
			if f > 0 or m > 0 or b > 0 then
				local function getRenderColor( state )
					if     state == nil then 
						return 0,0,0,1
					elseif state == 0 then 
						return 0.35,0.35,0.35,1
					elseif state == 1 then 
						return 1,1,1,1
					elseif state == 2 then 
						return 0,1,0,1
					end 
					return 0,0,0,1
				end 
				
							
				setOverlayColor( vehicleControlAddon.ovDiffLockFront, getRenderColor( f ) )
				setOverlayColor( vehicleControlAddon.ovDiffLockMid  , getRenderColor( m ) )
				setOverlayColor( vehicleControlAddon.ovDiffLockBack , getRenderColor( b ) )
				
				local vehicleSpeed = math.max( self.lastSpeed * 1000, 0.277 )
				local function setWheelColor( idx, ov )
					local ws = nil 
					if      idx ~= nil
							and self.spec_wheels ~= nil
							and self.spec_wheels.wheels ~= nil
							and self.spec_wheels.wheels[idx] ~= nil
							and self.spec_wheels.wheels[idx].vcaSpeed   ~= nil 
							and self.spec_wheels.wheels[idx].vcaNoPower ~= nil 
							and self.spec_wheels.wheels[idx].vcaNoPower ~= true then 
						ws = self.spec_wheels.wheels[idx].vcaSpeed
						if self:vcaGetShuttleCtrl() then 
							if self.spec_motorized.motor.currentDirection < 0 then 
								ws = -ws 
							end 
						else 
							if self.movingDirection < 0 then 
								ws = -ws 
							end 
						end 
					end 
					local r,g,b = 0.35,0.35,0.35
					if ws ~= nil then 
						if     ws <= 1.05 * vehicleSpeed then 
							r = 1
							g = 1
							b = 1
						elseif 0  >= vehicleSpeed then 
						elseif ws >= 1.25 * vehicleSpeed then 
							r = 1
							g = 0 
							b = 0
						elseif ws >= 1.15 * vehicleSpeed then  
							r = 1
							g = 1 - 10 * ( ws / vehicleSpeed - 1.15 )
							b = 0
						else 
							r = 1
							g = 1 
							b = 1 - 10 * ( ws / vehicleSpeed - 1.05 )
						end 
					elseif not self.spec_vca.diffHas2 then 
						r = 0.7
						g = 0.7
						b = 0.7
					end 
					setOverlayColor( ov, r,g,b, 1 )
				end 
				
				setWheelColor( self.spec_vca.wheelIndexFL, vehicleControlAddon.ovDiffLockFL )
				setWheelColor( self.spec_vca.wheelIndexFR, vehicleControlAddon.ovDiffLockFR )
				setWheelColor( self.spec_vca.wheelIndexRL, vehicleControlAddon.ovDiffLockRL )
				setWheelColor( self.spec_vca.wheelIndexRR, vehicleControlAddon.ovDiffLockRR )
				
				renderOverlay( vehicleControlAddon.ovDiffLockBg    , posX, posY, width, height )
				local prevWidth = width 
				width = math.min( width, height / g_screenAspectRatio )
				posX = posX + 0.5 * ( prevWidth - width )
				renderOverlay( vehicleControlAddon.ovDiffLockFL   , posX, posY, width, height )
				renderOverlay( vehicleControlAddon.ovDiffLockFR   , posX, posY, width, height )
				renderOverlay( vehicleControlAddon.ovDiffLockRL   , posX, posY, width, height )
				renderOverlay( vehicleControlAddon.ovDiffLockRR   , posX, posY, width, height )
				renderOverlay( vehicleControlAddon.ovDiffLockFront, posX, posY, width, height )
				renderOverlay( vehicleControlAddon.ovDiffLockMid  , posX, posY, width, height )
				renderOverlay( vehicleControlAddon.ovDiffLockBack , posX, posY, width, height )
			else 
				local prevWidth = width 
				width = math.min( width, height / g_screenAspectRatio )
				posX = posX + 0.5 * ( prevWidth - width )
			end 
			
			setTextBold(false)

			if self.lastSpeed > 0.0003 then 
				local ws = 0
				if self.spec_vca.wheelSlip ~= nil then 
					ws = math.max( 0, math.min( math.floor( 100 * ( self.spec_vca.wheelSlip - 1 ) + 0.5 ), 100 ) )
				end 
				
				local g, b = 1, 1
				if     ws >= 25 then
					g = 0 
					b = 0 
				elseif ws >= 15 then 
					g = 1 - 0.1 * ( ws - 15 )
					b = 0
				elseif ws >=  5 then 
					g = 1 
					b = 1 - 0.1 * ( ws - 5 ) 
				end 
				setTextColor(1,g,b,1)
				
				if ws > 99 then 	
					renderText( 0.5 * ( posX + width + 1 ), posY + 0.5 * height, l, ">99%" )
				else 
					renderText( 0.5 * ( posX + width + 1 ), posY + 0.5 * height, l, string.format( "%2.0f%%", ws ) )
				end 
			end 

			setTextColor(unpack(vehicleControlAddon.colorNormal))

			width = refPos.speedMeter.gaugeBackgroundElementOverlayWidth * 0.6
			
			if height > 1.4 * l then 
				posY = posY + 0.5 * ( height - 1.4 * l )
				height = 1.4 * l
			end 
			
			if      self.spec_vca.minGearSpeed ~= nil
					and self.spec_vca.maxGearSpeed ~= nil
					and self.spec_vca.maxGearSpeed ~= 0
				--and self:getGearShiftMode()    ~= VehicleMotor.SHIFT_MODE_AUTOMATIC
				--and not self.spec_vca.autoShift 
					then 
				
				renderOverlay( vehicleControlAddon.ovGearSpeedBg, x - 0.5 * width, posY, width, height )

				if vehicleControlAddon.vcaUIShowautoShift( self ) then 
					if self.spec_vca.autoShift then 
						setTextColor(unpack(vehicleControlAddon.colorInactive))
					else
						setTextColor(unpack(vehicleControlAddon.colorActive))
					end 
				else 
					setTextColor(unpack(vehicleControlAddon.colorActive))
				end 

				setTextBold(true)					
				renderText( x, posY + 0.55 * height, height * 0.7142857,
										self:vcaSpeedToString( self.spec_vca.minGearSpeed, "%5.1f", true ).." .. "..self:vcaSpeedToString( self.spec_vca.maxGearSpeed, "%5.1f" ))
			elseif vehicleControlAddon.vcaUIShowautoShift( self ) then  
				renderOverlay( vehicleControlAddon.ovGearSpeedBg, x - 0.5 * width, posY, width, height )
			end 			
			
			setTextColor(unpack(vehicleControlAddon.colorNormal))
			setTextBold(false)
		
		--if VCAGlobals.snapAngleHudX >= 0 then
		--	x = VCAGlobals.snapAngleHudX
		--	setTextAlignment( RenderText.ALIGN_LEFT ) 
		--end 
		--if VCAGlobals.snapAngleHudY >= 0 then
		--	y = VCAGlobals.snapAngleHudY
		--	setTextVerticalAlignment( RenderText.VERTICAL_ALIGN_BASELINE )
		--else 
				y = y + l * 1.2
		--end 
			
			local text = ""
			if self.spec_vca.keepCamRot then 
				if text ~= "" then text = text ..", " end 
				text = text .. "keep rot."
			end 
			if self.spec_vca.ksIsOn then 
				if text ~= "" then text = text ..", " end 
				text = text .. "keep speed"
			elseif  self.spec_vca.speedLimiter 
					and self.spec_drivable.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF then 
				if text ~= "" then text = text ..", " end 
				text = text .. "speed limiter"
			end 
			if self.spec_vca.inchingIsOn then 
				if text ~= "" then text = text ..", " end 
				text = text .. "inching"
			end 
			
			local i = 0
			if ( self.spec_vca.ksIsOn or self.spec_vca.ksIsActive ) and self.spec_drivable.cruiseControl.state == 0 then 
				i = i + 1 
			end 
			if self.spec_vca.snapPossible then 
				i = i + 1 
			end 
-- ovExtraTextBg
			if i > 0 or text ~= "" then  
				local ovY = y 
				local ovH = l * 1.2 * i + 0.4 * l
				if text ~= "" then 
					ovY = ovY - l * 0.75
					ovH = ovH + l * 0.75
				end 
				renderOverlay( vehicleControlAddon.ovExtraTextBg, x - 0.5 * width, ovY, width, ovH )
			end 
			
			if text ~= "" then 
				renderText(x, y, 0.75*l, text)
			end
			y = y + l * 0.9	
			
			if ( self.spec_vca.ksIsOn or self.spec_vca.ksIsActive ) and self.spec_drivable.cruiseControl.state == 0 then 
				renderText(x, y, l, self:vcaSpeedToString( Utils.getNoNil( self.spec_vca.keepSpeedTMS, Utils.getNoNil( self.spec_vca.keepSpeedTemp, self.spec_vca.keepSpeed ) ) / 3.6, "%5.1f" ))
				y = y + l * 1.2	
			end
		
			if not self:getIsVehicleControlledByPlayer() or self.spec_vca.snapDisabled then
			elseif self.spec_vca.snapIsOn then 
				setTextColor(unpack(vehicleControlAddon.colorActive))
				if self.spec_vca.snapDistance >= 0.25 then 
					renderText(x, y, l, string.format( "%4.1f° / %4.1fm", math.deg( math.pi - curSnapAngle ), self.spec_vca.snapDistance))
				else
					renderText(x, y, l, string.format( "%4.1f° / %4.1f°", math.deg( math.pi - curSnapAngle ), math.deg( math.pi - d )))
				end 
				y = y + l * 1.2	
			elseif -4 <= self.spec_vca.lastSnapAngle and self.spec_vca.lastSnapAngle <= 4 and not ( self.aiveAutoSteer ) then 
				setTextColor(unpack(vehicleControlAddon.colorInactive)) 
				renderText(x, y, l, string.format( "%4.1f° / %4.1f°", math.deg( math.pi - curSnapAngle ), math.deg( math.pi - d )))
				y = y + l * 1.2	
			end
			setTextColor(unpack(vehicleControlAddon.colorNormal)) 
			
		end 
		
		if not ( self.spec_vca.snapIsOn ) and self.spec_vca.drawSnapIsOn == nil then 
		-- leave vcaDrawSnapIsOn nil 
		elseif self.spec_vca.snapIsOn and not ( -4 <= self.spec_vca.lastSnapAngle and self.spec_vca.lastSnapAngle <= 4 ) then 
		-- start new 
			self.spec_vca.snapPosTimer  = 20000
			self.spec_vca.drawSnapIsOn  = true 
		else 
			if     self.spec_vca.snapIsOn and self.spec_vca.drawSnapIsOn == nil  then 
				self.spec_vca.snapPosTimer  = math.max( Utils.getNoNil( self.spec_vca.snapPosTimer , 0 ), 3000 )
			elseif self.spec_vca.snapIsOn and not ( self.spec_vca.drawSnapIsOn ) then 
				self.spec_vca.snapDrawTimer = 3000
			elseif self.spec_vca.drawSnapIsOn and not ( self.spec_vca.snapIsOn ) then 
				self.spec_vca.snapDrawTimer = 20000
			end 
			self.spec_vca.drawSnapIsOn = self.spec_vca.snapIsOn
		end 
		
		local snapDraw = false
		
		if not self.spec_vca.snapPossible then 
			self.spec_vca.snapDrawTimer = nil
			self.spec_vca.snapPosTimer  = nil 
			self.spec_vca.drawSnapIsOn  = nil 
		elseif self.spec_vca.snapPosTimer ~= nil then 
			snapDraw = true
		elseif self.spec_vca.snapDraw <= 0 then 
			self.spec_vca.snapDrawTimer = nil
			self.spec_vca.snapPosTimer  = nil 
		elseif self.spec_vca.snapDraw == 2 or self.spec_vca.snapDraw >= 4 then 
			snapDraw = true 
		elseif self.spec_vca.snapDrawTimer ~= nil then 
			if math.abs( self.lastSpeedReal ) * 3600 > 1 or self.spec_vca.snapDrawTimer > 3000 then 
				self.spec_vca.snapDrawTimer = self.spec_vca.snapDrawTimer - self.spec_vca.tickDt 
			end 
			if self.spec_vca.snapDrawTimer < 0 then 
				self.spec_vca.snapDrawTimer = nil 
			else 
				snapDraw = true 
			end 
		end 		
				
		if snapDraw then
			local wx,wy,wz = getWorldTranslation( self:vcaGetSteeringNode() )
			
			local dx    = math.sin( curSnapAngle )
			local dz    = math.cos( curSnapAngle )			
			local distX = wx - self.spec_vca.lastSnapPosX
			local distZ = wz - self.spec_vca.lastSnapPosZ 	
			
			local dist  = distX * dz - distZ * dx + curSnapOffset2

			local r, g, b, a = unpack(vehicleControlAddon.colorInactive)
			if self.spec_vca.snapIsOn then 
				dist = dist + self.spec_vca.snapFactor * self.spec_vca.snapDistance
				r, g, b, a = unpack(vehicleControlAddon.colorActive)

				if math.abs( dist ) > 1 then 
					if self.spec_vca.snapPosTimer == nil or self.spec_vca.snapPosTimer < 1000 then 
						self.spec_vca.snapPosTimer = 1000
					end
				end 
			elseif self.spec_vca.snapDistance >= 0.25 then 
				while dist+dist > self.spec_vca.snapDistance do 
					dist = dist - self.spec_vca.snapDistance
				end 
				while dist+dist <-self.spec_vca.snapDistance do 
					dist = dist + self.spec_vca.snapDistance
				end 
			end 

			setTextAlignment( RenderText.ALIGN_CENTER ) 
			setTextVerticalAlignment( RenderText.VERTICAL_ALIGN_BASELINE )
				
			local text = {'|','»»»»»»»»»»»»»»»»'}
			if not self.spec_vca.snapIsOn or self.spec_vca.snapDirection <1 then 
				text = {'|','------------------'}
			end 
			local xMax = 1 
			if ( self.spec_vca.snapDraw == 1 or self.spec_vca.snapDraw == 3 ) and self.spec_vca.snapIsOn and self.spec_vca.snapPosTimer == nil then 
				xMax = 0 
			end 
			for x=-xMax,xMax do 
				for zi=-1,2,0.2 do
					local z = 10 
					if self.spec_reverseDriving  ~= nil and self.spec_reverseDriving.isReverseDriving then			
						if zi < 1 then 
							z = z - 20 * zi 
						else 
							z = z - 10 * zi * zi
						end 
					else 
						if zi < 1 then 
							z = z + 20 * zi 
						else 
							z = z + 20 * zi * zi
						end 
					end 
					local fx = 0
					if x ~= 0 then 
						fx = x * 0.5 * self.spec_vca.snapDistance + curSnapOffset1
						setTextColor(r,g,b, 0.5*a) 
					else
						setTextColor(r,g,b, a) 
					end
					local px = wx - dist * dz - fx * dz + z * dx 
					local pz = wz + dist * dx + fx * dx + z * dz 
					local py = getTerrainHeightAtWorldPos( g_currentMission.terrainRootNode, px, 0, pz ) 
					
					local a = 0
					for _,t in pairs(text) do
						if self.spec_vca.snapDraw > 2 then 
							renderText3D( px,py+0.5,pz, 0,curSnapAngle-a,0, 0.52, t )
							renderText3D( px,py+1.0,pz, 0,curSnapAngle-a,0, 0.52, t )
							renderText3D( px,py+1.5,pz, 0,curSnapAngle-a,0, 0.52, t )
						else 
							renderText3D( px,py,pz, 0,curSnapAngle-a,0, 0.52, t )
						end 
						if self.spec_reverseDriving  ~= nil and self.spec_reverseDriving.isReverseDriving then			
							a = -0.5*math.pi 
						else 
							a =  0.5*math.pi 
						end 
					end 
				end  
			end 
			dx, dz = -dz, dx
		end 
		
		if self.spec_vca.snapPosTimer ~= nil then 
			self.spec_vca.snapPosTimer = self.spec_vca.snapPosTimer - self.spec_vca.tickDt 
			if self.spec_vca.snapPosTimer < 0 then 
				self.spec_vca.snapPosTimer = nil 
			end 
		end 
		
		if      self.spec_vca.workAreaDraw > 0
				and not self.spec_vca.snapIsOn
				and not snapDraw then 
			local wx,wy,wz = getWorldTranslation( self:vcaGetSteeringNode() )
			
			local dx    = lx
			local dz    = lz			
			local angle = math.atan2( lx, lz )

			local r, g, b, a = unpack(vehicleControlAddon.colorActive)
			setTextColor(r,g,b, a) 
			setTextAlignment( RenderText.ALIGN_CENTER ) 
			setTextVerticalAlignment( RenderText.VERTICAL_ALIGN_BASELINE )
				
			local text = {'|','------------------'}

			for x=-1,1 do 
				for zi=0,1,0.2 do
					local z = 0 
					if self.spec_reverseDriving  ~= nil and self.spec_reverseDriving.isReverseDriving then			
						z = z - 20 * zi 
					else 
						z = z + 20 * zi 

					end 
					local fx = 0
					if x ~= 0 then 
						fx = x * 0.5 * self.spec_vca.snapDistance + curSnapOffset1
					end
					local px = wx - fx * dz + z * dx 
					local pz = wz + fx * dx + z * dz 
					local py = getTerrainHeightAtWorldPos( g_currentMission.terrainRootNode, px, 0, pz ) 
					
					local a = 0
					for _,t in pairs(text) do
						renderText3D( px,py,pz, 0,angle-a,0, 0.52, t )
						if self.spec_vca.workAreaDraw > 1 then 
							renderText3D( px,py+0.5,pz, 0,angle-a,0, 0.52, t )
							renderText3D( px,py+1.0,pz, 0,angle-a,0, 0.52, t )
							renderText3D( px,py+1.5,pz, 0,angle-a,0, 0.52, t )
						end 
						if self.spec_reverseDriving  ~= nil and self.spec_reverseDriving.isReverseDriving then			
							a = -0.5*math.pi 
						else 
							a =  0.5*math.pi 
						end 
					end 
				end  
			end 
			dx, dz = -dz, dx
		end 
		
		if type( self.spec_vca.currentPath ) == "table" then 
			local r, g, b, a = unpack(vehicleControlAddon.colorActive)
			setTextColor(r,g,b, a) 
			setTextAlignment( RenderText.ALIGN_CENTER ) 
			setTextVerticalAlignment( RenderText.VERTICAL_ALIGN_BASELINE )

			for indx, point in pairs( self.spec_vca.currentPath ) do 
				local px    = point[1]
				local pz    = point[2]			
				local angle = point[3]
				local dx    = math.sin(angle)
				local dz    = math.cos(angle)			
				local py    = getTerrainHeightAtWorldPos( g_currentMission.terrainRootNode, px, 0, pz ) 
				
				local a = 0
				for _,t in pairs({string.format("%d",indx),">>>>>"}) do
					renderText3D( px,py,pz, 0,angle-a,0, 0.52, t )
					if self.spec_reverseDriving  ~= nil and self.spec_reverseDriving.isReverseDriving then			
						a = -0.5*math.pi 
					else 
						a =  0.5*math.pi 
					end 
				end 
			end
		end 

		setTextAlignment( RenderText.ALIGN_LEFT ) 
		setTextVerticalAlignment( RenderText.VERTICAL_ALIGN_BASELINE )
		setTextColor(1, 1, 1, 1) 
	end 	
end


function vehicleControlAddon:onReadStream(streamId, connection)

	self.spec_vca.diffHasF = streamReadBool( streamId )
	self.spec_vca.diffHasM = streamReadBool( streamId )
	self.spec_vca.diffHasB = streamReadBool( streamId )
	
	for i,name in pairs( vehicleControlAddon.propertiesIndex ) do 
		local prop = vehicleControlAddon.properties[name]
		self:vcaSetState( name, prop.func.streamRead( streamId ), true )
	end 
	
end

function vehicleControlAddon:onWriteStream(streamId, connection)

	streamWriteBool( streamId, self.spec_vca.diffHasF )
	streamWriteBool( streamId, self.spec_vca.diffHasM )
	streamWriteBool( streamId, self.spec_vca.diffHasB )

	for i,name in pairs( vehicleControlAddon.propertiesIndex ) do 
		local prop = vehicleControlAddon.properties[name]
		prop.func.streamWrite( streamId , self:vcaGetState( name, true ) )
	end 

end 

function vehicleControlAddon:onReadUpdateStream(streamId, timestamp, connection)
	if connection.isServer then
		if streamReadBool(streamId) then
			local wheelSlip = streamReadUInt8(streamId)
			if wheelSlip <= 0 then 
				self.spec_vca.wheelSlip = nil 
			else 
				self.spec_vca.wheelSlip = 1 + wheelSlip / 255
			end 
		end 
	end 
end

function vehicleControlAddon:onWriteUpdateStream(streamId, connection, dirtyMask)
	if not connection.isServer then
		local wheelSlip = 0
		if self.spec_vca ~= nil and self.spec_vca.wheelSlip ~= nil and self.spec_vca.wheelSlip > 1 then  
			wheelSlip = math.max( 0, math.min( math.floor( ( self.spec_vca.wheelSlip - 1 ) * 255 + 0.5 ), 255 ) )
		end 
		if self.spec_vca ~= nil and wheelSlip ~= self.spec_vca.lastWheelSlip then 
			self.spec_vca.lastWheelSlip = wheelSlip
			streamWriteBool(streamId, true )
			streamWriteUInt8(streamId, wheelSlip )
		else 
			streamWriteBool(streamId, false )
		end 
	end
end

function vehicleControlAddon:vcaGetSteeringNode()
	local n
	if type( self.getAIRootNode ) == "function" then 
		n = self:getAIRootNode()
		if n ~= nil then 
			return n
		end 
	end 
	if self.steeringAxleNode ~= nil and self.steeringAxleNode ~= 0 then 
		return self.steeringAxleNode
	end 
	return self.components[1].node  
end 

function vehicleControlAddon:vcaGetCurrentSnapAngle(curRot)

	if self.spec_vca.lastSnapAngle == nil or curRot == nil then 
		return 0 
	end
	
	local a = self.spec_vca.lastSnapAngle
	local o = self.spec_vca.snapOffset
	local p = o 
	local d = 0
	local e = - ( self.spec_vca.snapOffset + self.spec_vca.snapOffset )
	if self.spec_vca.snapInvert then 
		e = 0 
		if not self:vcaGetSnapWillInvert() then 
			p = -o 
		elseif self:vcaGetSnapIsInverted() then 
			o = -o 
			p =  o
		end 
	end 
	local c = curRot 
	local f = math.pi * 0.5 -- 0.5 for 180° and 0.25 for 90°
	local i = 0
	local r = false 
	if self.spec_vca.snapEvery90 then 
		f = f * 0.5 
	end 

	while a - c <= -f do 
		a = a + f+f
		i = i + 1 
		if not self.spec_vca.snapEvery90 then  
			d,e = e,d
			o,p = p,o
		elseif r then 
			r = false 
			d,e = e,d
			o,p = p,o
		else 
			r = true 
		end 
	end 
	while a - c > f do 
		a = a - f-f
		i = i - 1 
		if not self.spec_vca.snapEvery90 then  
			d,e = e,d
			o,p = p,o
		elseif r then 
			r = false 
			d,e = e,d
			o,p = p,o
		else 
			r = true 
		end 
	end

	return a, o, d, i
end 

function vehicleControlAddon.getRelativeTranslation( refNode, node )
	local wx, wy, wz = getWorldTranslation( node )
	return worldToLocal( refNode, wx, wy, wz )
end

function vehicleControlAddon.getDistance( refNode, leftMarker, rightMarker, iMinX, iMaxX )
	local lx, ly, lz = vehicleControlAddon.getRelativeTranslation( refNode, leftMarker )
	local rx, ry, rz = vehicleControlAddon.getRelativeTranslation( refNode, rightMarker )
	vcaDebugPrint(string.format( "(%5.2f, %5.2f, %5.2f) / (%5.2f, %5.2f, %5.2f)", lx, ly, lz, rx, ry, rz ))
	
	if iMinX ~= nil and iMaxX ~= nil then 
		return math.min( lx, rx, iMinX ), math.max( lx, rx, iMaxX )
	end 
	
	return math.min( lx, rx ), math.max( lx, rx )
end

function vehicleControlAddon:vcaGetSnapWillInvert()
	if     SpecializationUtil.hasSpecialization(AIVehicle, self.specializations) then
		for _, implement in ipairs(self:getAttachedAIImplements()) do
			if      SpecializationUtil.hasSpecialization( Plow, implement.object.specializations) 
					and implement.object.spec_plow.rotationPart.turnAnimation ~= nil then
				return true 
			end 
		end 
	end 
	return false
end 

function vehicleControlAddon:vcaGetSnapIsInverted()
	if SpecializationUtil.hasSpecialization(AIVehicle, self.specializations) then
		for _, implement in ipairs(self:getAttachedAIImplements()) do
			if implement.object:getAIInvertMarkersOnTurn( true ) then
				return true 
			end 
		end 
	end 
	return false
end 

function vehicleControlAddon:vcaGetSnapDistance()
	local minX, maxX
	
	if     SpecializationUtil.hasSpecialization(AIVehicle, self.specializations) then
		for _, implement in ipairs(self:getAttachedAIImplements()) do
			local leftMarker, rightMarker, backMarker, _ = implement.object:getAIMarkers()
			if implement.object.steeringAxleNode ~= nil and leftMarker ~= nil and rightMarker  ~= nil then 
				minX, maxX = vehicleControlAddon.getDistance( implement.object.steeringAxleNode, leftMarker, rightMarker, minX, maxX )
				local object = implement.object
				
				for i=1,10 do
					if object == nil or type( object.getAttacherVehicle ) ~= "function" then 
						break 
					end 
					local parent = object:getAttacherVehicle()
					if parent == nil or type( parent.getAttacherJointDescFromObject ) ~= "function" then 
						break 
					end 
					local jointDesc = parent:getAttacherJointDescFromObject( implement.object )
					if jointDesc == nil then  
						break 
					end 
					
					local vx, vy, vz = vehicleControlAddon.getRelativeTranslation( self:vcaGetSteeringNode(), jointDesc.jointTransform )
					local ix, iy, iz = vehicleControlAddon.getRelativeTranslation( implement.object.steeringAxleNode, jointDesc.jointTransform )
				--vcaDebugPrint( string.format("SnapDistance: (%5.2f, %5.2f); %5.2f; %5.2f", minX, maxX, vx, ix))
					minX = minX + vx - ix 
					maxX = maxX + vx - ix 

				--local m1, m2 = vehicleControlAddon.getDistance( self:vcaGetSteeringNode(), leftMarker, rightMarker, minX, maxX )
				--vcaDebugPrint( string.format("SnapDistance: (%5.2f, %5.2f); (%5.2f, %5.2f)", minX, maxX, m1, m2))
					
					if self.id == parent.id then 
						break 
					end 
					
					object = parent 
				end 
			end
		end
	end 
	
	if SpecializationUtil.hasSpecialization(AIImplement, self.specializations) then
		local leftMarker, rightMarker, backMarker, _ = self:getAIMarkers()
		if self:vcaGetSteeringNode() ~= nil and leftMarker ~= nil and rightMarker  ~= nil then 
			minX, maxX = vehicleControlAddon.getDistance( self:vcaGetSteeringNode(), leftMarker, rightMarker, minX, maxX )
		end
	end
	
	if minX ~= nil and maxX ~= nil then 
		local d = 0.1 * math.floor( 10 * ( maxX - minX ) + 0.5 )
		local o = 0.1 * math.floor(  5 * ( maxX + minX ) + 0.5 )
		local p = self:vcaGetSnapWillInvert()
		if p and self:vcaGetSnapIsInverted() then 
			o = -o 
		end 
		return d, -o, p
	end 
	
	return 0, 0, false 
end

function vehicleControlAddon:vcaSetCruiseSpeed( speed )
	local spec = self.spec_drivable 
	spec.cruiseControl.speed = speed
	spec.lastInputValues.cruiseControlValue = 0
	if spec.cruiseControl.speed ~= spec.cruiseControl.speedSent then
		if     not ( self.spec_vca.isInitialized )
			--or not ( g_vehicleControlAddon.isMP ) 
				then 
		elseif g_server ~= nil then
			g_server:broadcastEvent(SetCruiseControlSpeedEvent.new(self, spec.cruiseControl.speed, spec.cruiseControl.speedReverse), nil, nil, self)
		else
			g_client:getServerConnection():sendEvent(SetCruiseControlSpeedEvent.new(self, spec.cruiseControl.speed, spec.cruiseControl.speedReverse))
		end
		spec.cruiseControl.speedSent = spec.cruiseControl.speed
	end
end 

function vehicleControlAddon:vcaGetDefRevI()
	if not ( VCAGlobals.camReverseRotation ) then 
		return false 
	end 
	
	if self.attacherJoints ~= nil then
		for _,a in pairs( self.attacherJoints ) do
			if a.jointType == JOINTTYPE_SEMITRAILER then
				return false
			end
		end
	end

	if SpecializationUtil.hasSpecialization(Combine, self.specializations) then
		return false
	end 
	
	return true 
end

--******************************************************************************************************************************************
function vehicleControlAddon:vcaUpdateVehiclePhysics( superFunc, axisForward, axisSide, doHandbrake, dt )
	if self.spec_vca == nil then return superFunc( self, axisForward, axisSide, doHandbrake, dt ) end 
	
	--*******************************************************************
	-- Snap Angle
	local axisSideLast       = self.spec_vca.axisSideLast
	local lastSnapAngleTimer = self.spec_vca.snapAngleTimer
	self.spec_vca.axisSideLast     = nil
	self.spec_vca.snapAngleTimer   = nil
	self.spec_vca.lastSnapIsOn     = self.spec_vca.snapIsOn 
	
	local lastPathIndex = self.spec_vca.lastPathIndex 
	local lastPathAngle = self.spec_vca.lastPathAngle
	self.spec_vca.lastPathIndex = nil
	self.spec_vca.lastPathAngle = nil
	
	local snapIsOn = self.spec_vca.snapIsOn 
	if self.spec_vca.snapIsOn then 
		if not ( self.spec_vca.isEnteredMP ) then
			snapIsOn = false
		elseif self:getIsAIActive() then 
			snapIsOn = false
		end 
	end 
	
	if snapIsOn then 
		local lx,_,lz = localDirectionToWorld( self:vcaGetSteeringNode(), 0, 0, 1 )		
		local wx,_,wz = getWorldTranslation( self:vcaGetSteeringNode() )
		
		if lx*lx+lz*lz > 1e-6 then 
			local rot    = math.atan2( lx, lz )
			local d      = vehicleControlAddon.snapAngles[self.spec_vca.snapAngle]
			
			if not ( -4 <= self.spec_vca.lastSnapAngle and self.spec_vca.lastSnapAngle <= 4 ) then 
				if self:getIsVehicleControlledByPlayer() then 
					self.spec_vca.snapPosTimer = 20000
				end 

				self:vcaSetState( "lastSnapPosX", wx )
				self:vcaSetState( "lastSnapPosZ", wz )
				local target = 0
				local diff   = math.pi+math.pi
				if d == nil then 
					if self.spec_vca.snapAngle < 1 then 
						d = vehicleControlAddon.snapAngles[1] 
					else 
						d = 90 
					end 
				end 
				for i=0,360,d do 
					local a = math.rad( i )
					local b = math.abs( vehicleControlAddon.normalizeAngle( a - rot ) )
					if b < diff then 
						target = a 
						diff   = b
					end 
				end 
				
				self:vcaSetState( "lastSnapAngle", vehicleControlAddon.normalizeAngle( target ) )
				self:vcaSetState( "snapFactor", 0 )
			end 
			
			local tx = self.spec_vca.lastSnapPosX
			local tz = self.spec_vca.lastSnapPosZ
			local rot2 = rot 
			local setDir = false 
			if     self.spec_vca.snapDirection == 1 then 
				rot2 = self.spec_vca.lastSnapAngle 
			elseif self.spec_vca.snapDirection == 2 then 
				rot2 = self.spec_vca.lastSnapAngle + 0.5 * math.pi 
			elseif self.spec_vca.snapDirection == 3 then 
				rot2 = self.spec_vca.lastSnapAngle + math.pi 
			elseif self.spec_vca.snapDirection == 4 then 
				rot2 = self.spec_vca.lastSnapAngle - 0.5 * math.pi 
			else 
				setDir = true 
			end 
			
			local curSnapAngle, _, curSnapOffset = self:vcaGetCurrentSnapAngle( rot2 )
		
			if setDir then 
				delta = vehicleControlAddon.normalizeAngle( curSnapAngle - self.spec_vca.lastSnapAngle ) / math.pi 
				if     -0.25 <= delta and delta <=  0.25 then 
					self:vcaSetState( "snapDirection", 1 )
				elseif  0.25 <= delta and delta <=  0.75 then 
					self:vcaSetState( "snapDirection", 2 )
				elseif -0.75 <= delta and delta <= -0.25 then 
					self:vcaSetState( "snapDirection", 4 )
				else 
					self:vcaSetState( "snapDirection", 3 )
				end 
				print("VCA snap direction: "..tostring(delta).." => "..tostring(self.spec_vca.snapDirection))
			end 
			
			if vehicleControlAddon.snapCurve == nil then 
				vehicleControlAddon.snapCurve = AnimCurve.new(linearInterpolator1)

				local kf = {{x=0.01, y=0.01},
										{x=0.02, y=0.02},
										{x=0.05, y=0.08},
										{x=0.10, y=0.25},
										{x=0.12, y=0.42},
										{x=0.15, y=0.52},
										{x=0.20, y=0.66},
										{x=0.30, y=0.82},
										{x=0.40, y=0.95},
										{x=0.50, y=1.07},
										{x=0.60, y=1.18},
										{x=0.80, y=1.40},
										{x=0.90, y=1.50}}

				local iMax = table.getn( kf )

				vehicleControlAddon.snapCurve:addKeyframe({-math.pi*0.5,time=-1 }) 
				for i=iMax,1,-1 do 
					vehicleControlAddon.snapCurve:addKeyframe({-kf[i].y, time=-kf[i].x})
				end 
				vehicleControlAddon.snapCurve:addKeyframe({0 ,time=0})
				for i=1,iMax,1 do 
					vehicleControlAddon.snapCurve:addKeyframe({kf[i].y, time=kf[i].x})
				end 
				vehicleControlAddon.snapCurve:addKeyframe({ math.pi*0.5,time=1 })   
			end 
			
			local diffR = 0

			if type( self.spec_vca.currentPath ) == "table" and table.getn( self.spec_vca.currentPath ) > 1 then 
				
				local minP = nil 
				local minT = nil
				local minS = nil
				local minI = nil 
				local minA = nil 
				if lastPathIndex == nil then 
					lastPathIndex = 1 
				end 
				
				local debugOut = ""
				
				local i = table.getn( self.spec_vca.currentPath )
				local pdx = self.spec_vca.currentPath[i][1] - self.spec_vca.currentPath[1][1]
				local pdz = self.spec_vca.currentPath[i][2] - self.spec_vca.currentPath[1][2]
				local pdl = pdx*pdx + pdz*pdz 
				if pdl > 1 then 
					pdl = math.sqrt( pdl )
					pdx = pdx / pdl 
					pdz = pdz / pdl 
				else 
					pdx = math.sin( self.spec_vca.currentPath[1][3] )
					pdz = math.cos( self.spec_vca.currentPath[1][3] )
				end 
					 
				for indx=1,table.getn( self.spec_vca.currentPath ) do 
					local point = self.spec_vca.currentPath[indx]
					local sx = point[1]
					local sz = point[2]
					local a  = point[3]
					local l  = point[4]
					local dx = math.sin(a)
					local dz = math.cos(a)
					
					sx = sx - ( curSnapOffset + self.spec_vca.snapFactor * self.spec_vca.snapDistance ) * pdz 
					sz = sz + ( curSnapOffset + self.spec_vca.snapFactor * self.spec_vca.snapDistance ) * pdx 
					
					local da = vehicleControlAddon.mbClamp( vehicleControlAddon.normalizeAngle( a - rot ), -0.5*math.pi, 0.5 * math.pi)
					
					local vx = vehicleControlAddon.snapRadius * (1-math.cos( da )) * lx + math.sin( da ) * lz
					local vz = vehicleControlAddon.snapRadius * (1-math.cos( da )) * lz - math.sin( da ) * lx
					
					local t  = ( wx - sx + vx ) * dx + ( wz - sz + vz ) * dz 
					local s2 = ( wx - sx + vx ) * dz - ( wz - sz + vz ) * dx 
					local s  = ( wx - sx ) * dz - ( wz - sz ) * dx 
					
			--	t = t - vehicleControlAddon.snapRadius
					
					local t2 = 0
					if indx > 1 and t < 0 then 
						t2 = t 
					elseif indx < table.getn( self.spec_vca.currentPath ) and t > l then  
						t2 = t - l 
					end 
					local p2 = s2^2 + t2^2			
					
					
				--debugOut = debugOut .. tostring(indx)..", "..tostring(t2)..", "..tostring(s)..", "..tostring(p2)..";  "
					
					if minP == nil or ( p2 <= minP and t2 <= 0 ) then  
						minP = p2 
						minS = s 
						minT = t 
						minA = a
						minI = indx 
					end 
				end 
				
			--print(tostring(minI).." ("..tostring(minT)..", "..tostring(minS)..", "..tostring(minP)..")")
			--print(debugOut)
				
				self.spec_vca.lastPathIndex = minI  
				
				if minA ~= nil and minS ~= nil then 
					if lastPathAngle == nil then 
						self.spec_vca.lastPathAngle = minA 
					else 
						self.spec_vca.lastPathAngle = lastPathAngle + 0.08 * ( minA - lastPathAngle )
					end 
					diffR = rot - self.spec_vca.lastPathAngle
					
					if not self.spec_vca.isForward then
						diffR	= -diffR
					end 
					local dist  = minS
					local d2    = vehicleControlAddon.mbClamp( dist / vehicleControlAddon.snapRadius, -1, 1 )
					local alpha = vehicleControlAddon.snapCurve:get( d2 )
					diffR = diffR + alpha				
				end 
			
			else 
		
				diffR = rot - curSnapAngle
				if not self.spec_vca.isForward then
					diffR	= -diffR
				end 

				do
					local dx    = math.sin( curSnapAngle )
					local dz    = math.cos( curSnapAngle )			
					local distX = wx - tx
					local distZ = wz - tz 			
					local dist  = curSnapOffset + distX * dz - distZ * dx + self.spec_vca.snapFactor * self.spec_vca.snapDistance
					local d2    = vehicleControlAddon.mbClamp( dist / vehicleControlAddon.snapRadius, -1, 1 )
					local alpha = vehicleControlAddon.snapCurve:get( d2 )
					diffR = diffR + alpha				
				end 
			end 
			
			local a = vehicleControlAddon.mbClamp( vehicleControlAddon.normalizeAngle( diffR ) * 6, -1, 1 )

			if self.spec_reverseDriving  ~= nil and self.spec_reverseDriving.isReverseDriving then
				a = -a 
			end

			local d
			d = 0.0002 * dt * ( 2 + math.min( 18, self.lastSpeed * 3600 ) )
			
			if axisSideLast == nil then 
				axisSideLast = axisSide
			end 
			
			axisSide = axisSideLast + vehicleControlAddon.mbClamp( a - axisSideLast, -d, d )
		end 
		
		self.spec_vca.snapAngleTimer = 400 
		self.spec_vca.axisSideLast   = axisSide
	elseif axisSideLast ~= nil and lastSnapAngleTimer ~= nil and lastSnapAngleTimer > dt then  
		f = 0.0025 * lastSnapAngleTimer
		axisSide = f * axisSideLast + ( 1 - f ) * axisSide 
			
		self.spec_vca.snapAngleTimer = lastSnapAngleTimer - dt 
		self.spec_vca.axisSideLast   = axisSideLast
	end 				
	
-- remember and restore cruise control state when accelerating
	local ccState = nil
	local limit,_ = self:getSpeedLimit(true)
	local speed   = math.min( limit, self.lastSpeed * 3600 )
	local spec    = self.spec_drivable
	if      self.spec_vca ~= nil
			and self:vcaIsVehicleControlledByPlayer()
			and spec.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF
			and ( self:vcaGetShuttleCtrl() or self.movingDirection > 0 )
			and axisForward > 0 then 
		ccState = spec.cruiseControl.state
		spec.cruiseControl.state = Drivable.CRUISECONTROL_STATE_OFF
		self:getMotor():setSpeedLimit( limit )
	end 
	
	local lastBrakeCruiseSpeed = self.spec_vca.brakeCruiseSpeed
	self.spec_vca.brakeCruiseSpeed = nil 
	
	if ccState ~= nil and spec.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF then
		if speed > 3 then 
			self.spec_vca.brakeCruiseSpeed       = speed
			spec.cruiseControl.speedInterpolated = speed
		end 
	elseif speed > 3 and self.spec_vca.idleThrottle and spec.cruiseControl.speedInterpolated ~= nil then  
		if lastBrakeCruiseSpeed == nil then   
			self.spec_vca.brakeCruiseSpeed = speed
		else 
			self.spec_vca.brakeCruiseSpeed = math.min( limit, speed + 1, math.max( spec.cruiseControl.speedInterpolated, lastBrakeCruiseSpeed - 0.003 * dt ) )
		end 
		spec.cruiseControl.speedInterpolated = self.spec_vca.brakeCruiseSpeed
	end 
	
	local res = { superFunc( self, axisForward, axisSide, doHandbrake, dt ) }
	
	if ccState ~= nil and spec.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF then 
		self:setCruiseControlState(ccState)
	end 
	
	return unpack( res )

end 

Drivable.updateVehiclePhysics = Utils.overwrittenFunction( Drivable.updateVehiclePhysics, vehicleControlAddon.vcaUpdateVehiclePhysics )
--******************************************************************************************************************************************
-- shuttle control and inching
function vehicleControlAddon:vcaUpdateWheelsPhysics( superFunc, dt, currentSpeed, acceleration, doHandbrake, stopAndGoBraking )

	if not ( self.spec_vca ~= nil and self.spec_vca.isInitialized ) then  
		return superFunc( self, dt, currentSpeed, acceleration, doHandbrake, stopAndGoBraking )
	end 
	
	local lastWheelSlip        = self.spec_vca.wheelSlip
	self.spec_vca.wheelSlip    = nil

	self.spec_vca.oldAcc       = acceleration
	self.spec_vca.oldHandbrake = doHandbrake
	
	local lastKSBrakeTime = self.spec_vca.ksBrakeTime
	self.spec_vca.ksBrakeTime = nil 
	local lastIdleThrottleTime = self.spec_vca.idleThrottleTime
	self.spec_vca.idleThrottleTime = nil 
	local lastAccAntiSlip = self.spec_vca.maxAccAntiSlip
	self.spec_vca.maxAccAntiSlip = nil 
	
	if self:vcaIsVehicleControlledByPlayer() then
		if self.spec_vca.ksIsActive then 
			if self:vcaGetShuttleCtrl() or self.spec_vca.keepSpeed > 0 then 
				if acceleration < -0.1 then 
					if lastKSBrakeTime == nil then 
						self.spec_vca.ksBrakeTime = g_currentMission.time 
					else 
						self.spec_vca.ksBrakeTime = lastKSBrakeTime
					end 
				end 
			elseif self.spec_vca.keepSpeed < 0 then 
				if acceleration > 0.1 then 
					if lastKSBrakeTime == nil then 
						self.spec_vca.ksBrakeTime = g_currentMission.time 
					else 
						self.spec_vca.ksBrakeTime = lastKSBrakeTime
					end 
				end 
			end 
		end 

		local motor = self.spec_motorized.motor 
		
		
		local wheelSlip  = 1
		local speedMS    = self.lastSpeedReal * 1000
		local wheelSpeed = motor.differentialRotSpeed
		if motor.maxGearRatio < 0 then 
			wheelSpeed = -wheelSpeed
		end 
		if wheelSpeed > speedMS and speedMS > 0.3 then 
			wheelSlip = wheelSpeed / speedMS 
		end 
		if lastWheelSlip == nil then 
			lastWheelSlip = 1
		end 
		self.spec_vca.wheelSlip = lastWheelSlip + 0.03 * ( wheelSlip - lastWheelSlip )
	
		if not self:getIsMotorStarted() then 
			acceleration = 0
			doHandbrake = true 
		elseif self.spec_vca.handbrake then  
			acceleration = 0
			doHandbrake = true 		
		elseif self.spec_drivable.cruiseControl.state > 0 then 

		elseif self.spec_vca.ksIsOn and math.abs( self.spec_vca.keepSpeed ) < 0.5 then 
			acceleration = 0
			doHandbrake  = true 
		elseif self.spec_vca.ksIsActive and ( self.spec_vca.ksBrakeTime == nil or g_currentMission.time < self.spec_vca.ksBrakeTime + 1000 ) then 
			self.spec_motorized.motor:setSpeedLimit( math.min( self:getSpeedLimit(true), math.abs(self.spec_vca.keepSpeed) ) )
			if self:vcaGetShuttleCtrl() then 
				acceleration = 1
			elseif self.spec_vca.keepSpeed > 0 then 
				acceleration = self.spec_drivable.reverserDirection
				self.nextMovingDirection = 1
			else
				acceleration = -self.spec_drivable.reverserDirection
				self.nextMovingDirection = -1
			end 
			self.spec_vca.oldAcc = acceleration
		elseif self.spec_vca.isBlocked and self.spec_vca.isEnteredMP then
			acceleration = 0
			doHandbrake  = true 
		end 
			
		if self.spec_drivable.cruiseControl.state == 0 and self.spec_vca.limitThrottle ~= nil and self.spec_vca.inchingIsOn ~= nil and math.abs( acceleration ) > 0.01 then 
			local limitThrottleRatio     = 0.75
			local limitThrottleIfPressed = true
			if self.spec_vca.limitThrottle < 11 then
				limitThrottleIfPressed = false
				limitThrottleRatio     = 0.45 + 0.05 * self.spec_vca.limitThrottle
			else
				limitThrottleIfPressed = true
				limitThrottleRatio     = 1.5 - 0.05 * self.spec_vca.limitThrottle
			end
				
			if self.spec_vca.inchingIsOn == limitThrottleIfPressed then
				acceleration   = acceleration   * limitThrottleRatio
				self.spec_vca.oldAcc = self.spec_vca.oldAcc * limitThrottleRatio
			end
		end
		
		self.spec_vca.useIdleThrottle = false 
		
		if self.spec_vca.clutchMode > 0 and doHandbrake then 
			motor.stallTimer = 0
		end
		
		if      self.spec_vca.idleThrottle 
				and not doHandbrake
				and self.spec_motorized       ~= nil 
				and self.spec_motorized.motor ~= nil 	
				and self.spec_motorized.motorizedNode ~= nil
				and next(self.spec_motorized.differentials) ~= nil
				then 
			local motor = self.spec_motorized.motor 
			local m = motor.currentDirection
			if self:vcaGetShuttleCtrl() then 
				m = 1 
			end 
			
			if     self.spec_vca.clutchMode >= vehicleControlAddon.ANTI_STALL then
				motor.stallTimer = 0
			elseif self.spec_vca.clutchMode >= vehicleControlAddon.SHUTTLE_CLUTCH and self.movingDirection * motor.currentDirection < 0 then 
				motor.stallTimer = 0
			end
			
			local noAutoBrake = false 
			if      motor.gearShiftMode           == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH
					and motor.manualClutchValue       <  0.9 then 
				noAutoBrake     = true 
			elseif motor.stallTimer               >  0 then 
				noAutoBrake     = true 
			elseif math.abs( self.lastSpeedReal ) >= motor.lowBrakeForceSpeedLimit
					or math.abs( acceleration )       >= 0.001 then 
				noAutoBrake     = true 
			elseif self:vcaGetShuttleCtrl() and lastIdleThrottleTime ~= nil and self.spec_vca.isEnteredMP and not self.spec_vca.isBlocked then 
				self.spec_vca.idleThrottleTime = lastIdleThrottleTime
				if g_currentMission.time < lastIdleThrottleTime then 
					noAutoBrake   = true 
				end 
			end 

			local hasSlippingWheel = false 
			do --if self.lastSpeed > 0.0005 then 
				local f = 1
				if     self.rotatedTime == nil
						or self.minRotTime  == nil
						or self.maxRotTime  == nil then 
				elseif self.rotatedTime < 0 and self.minRotTime < 0 then 
					f = f + 4 * self.rotatedTime / self.minRotTime
				elseif self.rotatedTime > 0 and self.maxRotTime > 0 then 
					f = f + 4 * self.rotatedTime / self.maxRotTime
				end 
				local vehicleSpeed = self.lastSpeed * 1000 + math.max( f * self.lastSpeed * 100, 1 )
				for _,w in pairs( self.spec_wheels.wheels ) do 
					if      w.vcaSpeed   ~= nil 
							and w.vcaNoPower ~= nil 
							and w.vcaNoPower ~= true then 
						local ws = w.vcaSpeed
						if self:vcaGetShuttleCtrl() then 
							if self.spec_motorized.motor.currentDirection < 0 then 
								ws = -ws 
							end 
						else 
							if self.movingDirection < 0 then 
								ws = -ws 
							end 
						end 
						if ws > vehicleSpeed then 
							hasSlippingWheel = true 
						end 
					end 
				end 
			end 
			
			if      self:getIsMotorStarted()
					and ( ( m > 0 and acceleration > -0.01 ) or ( m < 0 and acceleration < 0.01 ) )
					and (motor.backwardGears or motor.forwardGears) 
					and motor.gearRatio ~= 0 
					and self.spec_vca.gearRatio ~= 0 
					and noAutoBrake then

				self.spec_vca.useIdleThrottle  = true 
				self.spec_vca.idleThrottleTime = g_currentMission.time + 2000

				local rpmRange  = motor.maxRpm - motor.minRpm
				local minRpm    = vehicleControlAddon.mbClamp( vehicleControlAddon.origGetMaxPtoRpm( self ) * motor.ptoMotorRpmRatio, motor.minRpm, motor.maxRpm )
				if self.spec_vca.handThrottle > 0 then 
				-- hand throttle overrides PTO RPM
					minRpm        = motor.minRpm + vehicleControlAddon.mbClamp( self.spec_vca.handThrottle, 0, 1 ) * rpmRange
				end 													
				local diffRpm   = motor.differentialRotSpeed * 30 / math.pi
				local motorRpm  = motor.motorRotSpeed * 30 / math.pi --math.abs( diffRpm * motor.gearRatio )
				local clutchRpm = math.abs( diffRpm * self.spec_vca.gearRatio )
				local delta     = vehicleControlAddon.mbClamp( 1 - self.spec_vca.handThrottle, 0, 0.1 )
				
			-- accelerate
				local function getClutchMinAcc()
					if     clutchRpm >= minRpm then 
						return 0 
					elseif delta < 1e-3 then 
						return 1
					else
						return math.min( 1, ( 1 - clutchRpm / minRpm ) / delta )
					end 
				end 
				local function getMotorMaxAcc()
					local maxRpm = 0.96 * minRpm + 0.04 * motor.maxRpm
					if     motorRpm >= maxRpm then 
						return 0 
					elseif motorRpm <= minRpm then 
						return 1 
					else 
						return ( maxRpm - motorRpm ) / ( maxRpm - minRpm )
					end 
				end 
				
				local change = false 
				local acc    = math.abs( acceleration )
				local maxRpm = math.max( minRpm, motor.minRpm + vehicleControlAddon.mbClamp( acc - 0.02, 0.2, 1 ) * rpmRange )
				if 0 < acc and acc < 1 and motorRpm > maxRpm then 
					if motorRpm >= maxRpm + 0.04 * rpmRange then 
					  acc      = 0
					else 
					  acc      = math.min( acc, 1 - 25 * ( motorRpm - maxRpm ) / rpmRange )
					end 
					change     = true 
				end 
				local minAcc = math.min( getClutchMinAcc(), getMotorMaxAcc() )
				if math.abs( self.lastSpeedReal ) < motor.lowBrakeForceSpeedLimit then 
					minAcc     = math.max( 0.0015, minAcc )
				end 
				if acc < minAcc then 
					acc        = minAcc
					change     = true 
				end 

				if change and self.spec_wheels ~= nil and self.spec_wheels.wheels ~= nil and hasSlippingWheel then 
					acc = math.min( acc, minAcc )
				end 
				
				local speed = math.abs( self.lastSpeedReal )
				if 0.01 < speed and speed <= motor.lowBrakeForceSpeedLimit and acc <= 0.0015 then 
					acc    = - math.max( 0.0015, motor.lowBrakeForceScale ) 
					change = true 
				end 
				
				if change then 
					acceleration = m * acc
				end 
			elseif  self:getIsMotorStarted()
					and self:vcaGetShuttleCtrl()
					and acceleration > -0.01 
					and motor.backwardGears == nil and motor.forwardGears == nil  
					and noAutoBrake then
			-- turn off automatic brake in WheelsUtil:updateWheelsPhysics 
			-- you have to push the brake for at least one sceond below lowBrakeForceSpeedLimit, i.e. 3.6 km/h
				self.spec_vca.idleThrottleTime = g_currentMission.time + 1000
				local speed = math.abs( self.lastSpeedReal )
				if 0.01 < speed and speed <= motor.lowBrakeForceSpeedLimit and math.abs( acceleration ) <= 0.0015 then 
					acceleration = - math.max( 0.0015, motor.lowBrakeForceScale )
				end 
			end 
			
			if      self:getIsMotorStarted()
					and ( ( m > 0 and acceleration > -0.01 ) or ( m < 0 and acceleration < 0.01 ) )
					and self.spec_vca.antiSlip
					and noAutoBrake then
				if lastAccAntiSlip == nil then 
					lastAccAntiSlip = 1
				end 
				--local lastAccAntiSlip = self.spec_vca.maxAccAntiSlip
				if hasSlippingWheel then 
					self.spec_vca.maxAccAntiSlip = math.max( 0.1, lastAccAntiSlip - 0.001 * dt )
				else 
					self.spec_vca.maxAccAntiSlip = math.min( 1.0, lastAccAntiSlip + 0.001 * dt )
				end 
				if m > 0 then 
					acceleration = math.min( acceleration, self.spec_vca.maxAccAntiSlip )
				else 
					acceleration = math.min( acceleration, -self.spec_vca.maxAccAntiSlip )
				end 
			end 
		end
	end 
	
	self.spec_vca.newAcc       = acceleration
	self.spec_vca.newHandbrake = doHandbrake
	
	local state, result = pcall( superFunc, self, dt, currentSpeed, acceleration, doHandbrake, stopAndGoBraking ) 
	if not ( state ) then
		print("Error in updateWheelsPhysics :"..tostring(result))
		return 
	end
	
	return result 
end 
WheelsUtil.updateWheelsPhysics = Utils.overwrittenFunction( WheelsUtil.updateWheelsPhysics, vehicleControlAddon.vcaUpdateWheelsPhysics )
--******************************************************************************************************************************************
function vehicleControlAddon:vcaGetSmoothedAccBrake( superFunc, acceleratorPedal, brakePedal, dt )
	if      self          ~= nil 
			and self.spec_vca ~= nil 
			and self.spec_vca.isInitialized
			and self:vcaIsVehicleControlledByPlayer()
			and next(self.spec_motorized.differentials) ~= nil
			and ( self.spec_vca.handbrake 
				or  self.spec_vca.isBlocked 
				or  not self:getIsMotorStarted()
				or  ( self.spec_vca.ksIsOn and math.abs( self.spec_vca.keepSpeed ) < 0.5 ) )
			then 
		return superFunc( self, 0, 1, dt )
	end 
	return superFunc( self, acceleratorPedal, brakePedal, dt )
end 
WheelsUtil.getSmoothedAcceleratorAndBrakePedals = Utils.overwrittenFunction( WheelsUtil.getSmoothedAcceleratorAndBrakePedals, vehicleControlAddon.vcaGetSmoothedAccBrake )

--******************************************************************************************************************************************
function vehicleControlAddon.vcaMotorAfterUpdateGear( motor, superFunc, acceleratorPedal, brakePedal, dt, ... )
	local self  = motor.vehicle 
	
	local adjAcceleratorPedal, brakePedal = superFunc( motor, acceleratorPedal, brakePedal, dt, ... )
	
	if 			self.spec_vca.clutchMode == vehicleControlAddon.TURBO_CLUTCH
			and (motor.backwardGears or motor.forwardGears)
			and motor.maxGearRatio ~= nil
			and motor.maxGearRatio ~= 0
			then 
		local diffRpm   = motor.differentialRotSpeed * 30 / math.pi
		local motorRpm  = motor.motorRotSpeed * 30 / math.pi --math.abs( diffRpm * motor.gearRatio )
		local clutchRpm = math.abs( diffRpm * self.spec_vca.gearRatio )
	
	  local openRpm  = 0.95 * motor.minRpm + 0.05 * motor.maxRpm
		local closeRpm = 0.75 * motor.minRpm + 0.25  * motor.maxRpm
	
		if self.spec_vca.tcOpen == nil then self.spec_vca.tcOpen = true end 
		
		if self.spec_vca.tcOpen then 
			if clutchRpm > closeRpm then 
				self.spec_vca.tcOpen = false 
			end 
		else 
			if clutchRpm < openRpm then 
				self.spec_vca.tcOpen = true  
			end 
		end 
		
		if self.spec_vca.tcOpen then 
			local factor = 1.25
			if motorRpm  <= openRpm then 
				factor = factor + 2
			elseif motorRpm < closeRpm then 
				factor = factor + 2 * ( closeRpm - motorRpm ) / ( closeRpm - openRpm )
			end 
			motor.maxGearRatio = motor.maxGearRatio * factor
		end 
	end 
	
	return adjAcceleratorPedal, brakePedal
end 
VehicleMotor.updateGear = Utils.overwrittenFunction( VehicleMotor.updateGear, vehicleControlAddon.vcaMotorAfterUpdateGear )
	
--******************************************************************************************************************************************
function vehicleControlAddon.vcaMotorGetSpeedLimit( motor, superFunc, ... )
	local self  = motor.vehicle 
	local limit = superFunc( motor, ... )
	
	
	if self ~= nil and self.spec_vca ~= nil then 
		if      self.spec_vca.speedLimiter 
				and self:vcaIsVehicleControlledByPlayer()
				and self.spec_drivable.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF
				and not self.spec_vca.ksIsActive then 
			limit = math.min( limit, self.spec_drivable.cruiseControl.speed )
		end
		if self.spec_vca.wheelSlip ~= nil then 
			limit = self.spec_vca.wheelSlip * limit
		end 
	end 
	return limit
end 
VehicleMotor.getSpeedLimit = Utils.overwrittenFunction( VehicleMotor.getSpeedLimit, vehicleControlAddon.vcaMotorGetSpeedLimit )

--******************************************************************************************************************************************
function vehicleControlAddon.vcaGetManualClutchPedal( motor, superFunc, ... )
	local self = motor.vehicle 
	if      self          ~= nil 
			and self.spec_vca ~= nil 
			and self.spec_vca.isInitialized
			and self:vcaIsVehicleControlledByPlayer()
			and next(self.spec_motorized.differentials) ~= nil
			and ( self.spec_vca.handbrake 
				or  self.spec_vca.isBlocked 
				or  ( self.spec_vca.ksIsOn and math.abs( self.spec_vca.keepSpeed ) < 0.5 ) )
			then 
		return 1
	end 
	return superFunc( motor, ... )
end 
VehicleMotor.getManualClutchPedal = Utils.overwrittenFunction( VehicleMotor.getManualClutchPedal, vehicleControlAddon.vcaGetManualClutchPedal )

--******************************************************************************************************************************************
--******************************************************************************************************************************************
function vehicleControlAddon:getCruiseControlState( superFunc )
	if self.spec_vca ~= nil and self.spec_vca.ksIsOn and math.abs( self.spec_vca.keepSpeed ) >= 0.5 then  
		return Drivable.CRUISECONTROL_STATE_ACTIVE 
	end 
	return superFunc( self )
end 
Drivable.getCruiseControlState = Utils.overwrittenFunction( Drivable.getCruiseControlState, vehicleControlAddon.getCruiseControlState )


function vehicleControlAddon:newActionEventCruiseControlState( superFunc, actionName, inputValue, callbackState, isAnalog )
	if self.spec_vca ~= nil and self.spec_vca.isEntered then 
		self.spec_vca.cruiseControlStateIsPressed = true
		self.spec_drivable.lastInputValues.cruiseControlState = 0
		return 
	end 
	
	superFunc( self, actionName, inputValue, callbackState, isAnalog )
end 
Drivable.actionEventCruiseControlState = Utils.overwrittenFunction( Drivable.actionEventCruiseControlState, vehicleControlAddon.newActionEventCruiseControlState )

function vehicleControlAddon:getCruiseControlSpeed( superFunc )
	if      self.spec_drivable.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF
			and self.spec_vca ~= nil 
			and self.spec_vca.ksIsOn 
			and math.abs( self.spec_vca.keepSpeed ) >= 0.5 then  
		return self.spec_vca.keepSpeed
	end 
	return superFunc( self )
end 
Drivable.getCruiseControlSpeed = Utils.overwrittenFunction( Drivable.getCruiseControlSpeed, vehicleControlAddon.getCruiseControlSpeed ) 

function vehicleControlAddon:getAccelerationAxis( superFunc )
	if self.spec_vca ~= nil and self.spec_vca.handThrottle > 0 then 
		return math.max( superFunc( self ), self.spec_vca.handThrottle )
	end 
	return superFunc( self )
end 
Drivable.getAccelerationAxis   = Utils.overwrittenFunction( Drivable.getAccelerationAxis,   vehicleControlAddon.getAccelerationAxis )

function vehicleControlAddon:getCruiseControlAxis( superFunc )
	if      self.spec_drivable.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF
			and self.spec_vca ~= nil 
			and self.spec_vca.ksIsOn 
			and math.abs( self.spec_vca.keepSpeed ) >= 0.5 then  
		return 1
	end 
	return superFunc( self )
end 
Drivable.getCruiseControlAxis  = Utils.overwrittenFunction( Drivable.getCruiseControlAxis,  vehicleControlAddon.getCruiseControlAxis )

--******************************************************************************************************************************************
--******************************************************************************************************************************************


function vehicleControlAddon:vcaIsHydraulicSamplePlaying()
	if self == nil then 
		return false 
	end 
	
	if self.spec_cylindered ~= nil then 
		local spec = self.spec_cylindered
		if spec.movingToolNeedsSound or spec.movingPartNeedsSound then 
			return true 
		end 
	end 
	
	if self.spec_foldable ~= nil and self.spec_foldable.foldAnimTime ~= nil then 
		local spec = self.spec_foldable
		if 0.01 < spec.foldAnimTime and spec.foldAnimTime < 0.99 and ( spec.foldMiddleAnimTime == nil or math.abs( spec.foldAnimTime - spec.foldMiddleAnimTime ) > 0.01 ) then 
			return true 
		end 
	end 
	
	if self.spec_dischargeable ~= nil then
		local spec = self.spec_dischargeable
		if     spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT
				or spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then 
			return true
		end 
	end 
	
	if self.spec_trailer ~= nil then
		local spec = self.spec_trailer
		if self.spec_trailer.tipState == Trailer.TIPSTATE_OPENING then
			return true
		end 	
	end 	
	
	if self.spec_attacherJoints ~= nil and type( self.spec_attacherJoints.attachedImplements ) == "table" then  
		local spec = self.spec_attacherJoints
		for _,implement in pairs( self.spec_attacherJoints.attachedImplements ) do 
			local jointDesc = spec.attacherJoints[implement.jointDescIndex]
			
			if jointDesc == nil or implement.object == nil then 
				return false 
			end 
			
			if      not implement.object.spec_attachable.isHardAttached
					and not implement.attachingIsInProgress 
					and jointDesc ~= nil 
					and jointDesc.allowsLowering and self:getIsActive()
					and not jointDesc.moveDown 
					and math.abs(jointDesc.moveAlpha - jointDesc.upperAlpha) > 0.05 then 
				return true 
			elseif vehicleControlAddon.vcaIsHydraulicSamplePlaying( implement.object ) then 
				return true 
			end 
		end 
	end 
	
	return false 
end 

--******************************************************************************************************************************************
-- hand RPM via PowerConsumer.getMaxPtoRpm (only in neutral)
vehicleControlAddon.origGetMaxPtoRpm = PowerConsumer.getMaxPtoRpm
PowerConsumer.getMaxPtoRpm = function ( self, ... )
	if     self.spec_vca              == nil
			or self.spec_motorized        == nil 
			or self.spec_motorized.motor  == nil 
			or not self.spec_vca.isInitialized 
			or self.spec_motorized.motor.ptoMotorRpmRatio == nil
			or self.spec_motorized.motor.ptoMotorRpmRatio <= 0
			then 
		return vehicleControlAddon.origGetMaxPtoRpm( self, ... )
	end 
	
	if self.spec_vca.useIdleThrottle then 
	-- do not increase RPM while driving
		return 0 
	end 
	
	local motor = self.spec_motorized.motor 
	
	local r0 = vehicleControlAddon.origGetMaxPtoRpm( self, ... )
	
	if self.spec_vca.handbrake and self.spec_vca.oldAcc ~= nil and self.spec_vca.oldAcc > 0 then 
		r0 = math.max( r0, ( motor.minRpm + self.spec_vca.oldAcc * ( motor.maxRpm - motor.minRpm ) ) / motor.ptoMotorRpmRatio )
	end 

	local r1 = r0
	
	if self.spec_vca.handThrottle > 0 then
	-- hand throttle overrides PTO RPM
		r1 = ( motor.minRpm + vehicleControlAddon.mbClamp( self.spec_vca.handThrottle, 0, 1 ) * ( motor.maxRpm - motor.minRpm ) ) / motor.ptoMotorRpmRatio
	elseif self.spec_vca.idleThrottle and vehicleControlAddon.vcaIsHydraulicSamplePlaying( self ) then 
	-- extra RPM for hydraulics
		r1 = math.max( r0, ( motor.minRpm + 0.2 * ( motor.maxRpm - motor.minRpm ) ) / motor.ptoMotorRpmRatio )
	end 
	
	return r1
end 

--******************************************************************************************************************************************
-- limit maxRpm in case of inching 
function vehicleControlAddon:vcaGetRequiredMotorRpmRange( superFunc, ... )
	local lastIncreaseRpm = -1 
	if self.vcaIncreaseRpm ~= nil then 
		lastIncreaseRpm = self.vcaIncreaseRpm
	end 
	local lastMinRpm = self.minRpm 
	if self.vcaMinRpm ~= nil then 
		lastMinRpm = self.vcaMinRpm
	end 
	local lastMaxRpm = self.maxRpm
	if self.vcaMaxRpm ~= nil then 
		lastMaxRpm = self.vcaMaxRpm
	end 
	local lastSpeedFactor = self.vcaSpeedFactor
	
	self.vcaIncreaseRpm = nil 
	self.vcaMinRpm      = nil 
  self.vcaMaxRpm      = nil 
	self.vcaSpeedFactor = nil 


	if not ( self.vehicle ~= nil 
			 and self.vehicle.spec_vca ~= nil 
			 and self.vehicle.spec_vca.idleThrottle
			 and self.vehicle.spec_vca.oldAcc ~= nil
			 and self.vehicle.spec_vca.tickDt ~= nil
			 and self.currentDirection ~= nil 
			 and self.manualClutchValue < 0.1
			 and next(self.vehicle.spec_motorized.differentials) ~= nil
			) then 
		return superFunc( self, ... )
	end 
	
	local m = 1
	if not self.vehicle:vcaGetShuttleCtrl() and self.currentDirection < 0 then 
		m = -1 
	end 
	local curAcc = math.max( 0, m * self.vehicle.spec_vca.oldAcc )
	local curBrake = math.max( 0, -m * self.vehicle.spec_vca.oldAcc )
	
	local rpmRange = self.maxRpm - self.minRpm
	local minRpm, maxRpm = self.minRpm, self.maxRpm
	local motorPtoRpm = math.min(PowerConsumer.getMaxPtoRpm(self.vehicle) * self.ptoMotorRpmRatio, self.maxRpm)
	
	local speed = self.vehicle:getLastSpeed()

	lastMinRpm = vehicleControlAddon.mbClamp( lastMinRpm, minRpm, maxRpm )
	lastMaxRpm = vehicleControlAddon.mbClamp( lastMaxRpm, minRpm, maxRpm )
	
	if self.backwardGears or self.forwardGears or self.vehicle.spec_vca.useIdleThrottle then  
		-- standard transmission 
		maxRpm = self.maxRpm 
		if motorPtoRpm > self.minRpm and not self.vehicle.spec_vca.useIdleThrottle then 
			-- increase RPM in neutral
			minRpm = motorPtoRpm
		end 
	else
		-- variable transmission
		
		local function getRequiredRpmAtSpeedLimit(ratio)
			local speedLimit = math.min(self.vehicle:getSpeedLimit(true), self.vehicle.lastSpeedReal * 3600 + 1 )

			if self.vehicle:getCruiseControlState() == Drivable.CRUISECONTROL_STATE_ACTIVE then
				speedLimit = math.min(speedLimit, self.vehicle:getCruiseControlSpeed())
			end

			speedLimit = ratio > 0 and math.min(speedLimit, self.maxForwardSpeed * 3.6) or math.min(speedLimit, self.maxBackwardSpeed * 3.6)

			return speedLimit / 3.6 * 30 / math.pi * math.abs(ratio)
		end			
					
		local speedMinRpm  = getRequiredRpmAtSpeedLimit( self.minGearRatio )
		local peakPowerRpm = self.peakMotorPowerRotSpeed * 30 / math.pi

		if motorPtoRpm > self.minRpm then 
			-- variable transmission and fixed RPM 
			if self.smoothedLoadPercentage > 0.9 then 	
				-- reduce maxRpm under full load
				local f = 10 * self.smoothedLoadPercentage - 9
				motorPtoRpm = motorPtoRpm - 0.07 * f * rpmRange 
				maxRpm = vehicleControlAddon.mbClamp( math.max( motorPtoRpm + 0.07 * rpmRange, speedMinRpm ), self.minRpm, self.maxRpm )
			end 
			minRpm = vehicleControlAddon.mbClamp( motorPtoRpm, self.minRpm, self.maxRpm )
		else 
			-- variable transmission and variable RPM; e.g. Fendt TMS
	
			local idleRpm = math.min( math.max( minRpm, self.minRpm + 0.1 * rpmRange ), maxRpm )

			if self.vehicle.spec_combine ~= nil then 
				-- increase RPM for combine
				if     speed  > 2   then 
					-- not stopped => increase RPM
					self.vcaIncreaseRpm = math.max( lastIncreaseRpm, g_currentMission.time + 5000 )
				elseif curAcc > 0.1 then 
					-- accelerating => increase RPM for a short time 
					self.vcaIncreaseRpm = math.max( lastIncreaseRpm, g_currentMission.time + 2000 )
				elseif lastIncreaseRpm > g_currentMission.time then 
					self.vcaIncreaseRpm = lastIncreaseRpm
				end 

				idleRpm = math.min( math.max( minRpm, self.minRpm + 0.6 * rpmRange ), maxRpm )
			else
				if speed  > 2   then 
					-- not stopped => increase RPM
					self.vcaIncreaseRpm = math.max( lastIncreaseRpm, g_currentMission.time + 2000 )
				elseif curAcc > 0.1 then 
					-- accelerating => increase RPM 
					self.vcaIncreaseRpm = math.max( lastIncreaseRpm, g_currentMission.time + 1000 )
				elseif lastIncreaseRpm > g_currentMission.time then 
					self.vcaIncreaseRpm = lastIncreaseRpm
				end 
			end 
			
			local function vehicleHasActiveWorkArea( self )
				if self.spec_turnOnVehicle ~= nil and self.spec_turnOnVehicle.isTurnedOn then 
					return true 
				end
				if self.spec_workArea ~= nil and type( self.spec_workArea.workAreas ) == "table" then 
					for _,w in pairs( self.spec_workArea.workAreas ) do 
						if self:getIsWorkAreaActive(w) then 	
							return true 
						end 
					end 
				end 
				if self.spec_attacherJoints ~= nil and type( self.spec_attacherJoints.attachedImplements ) == "table" then 
					for _,implement in pairs( self.spec_attacherJoints.attachedImplements ) do 
						if implement.object ~= nil and vehicleHasActiveWorkArea( implement.object ) then 
							return true 
						end 
					end 
				end 
				return false 
			end 
			
			local hasActiveWorkArea = false 
			if vehicleHasActiveWorkArea( self.vehicle ) then 
				hasActiveWorkArea     = true 
				self.vcaActiveWATimer = g_currentMission.time + 1000
			elseif self.vcaActiveWATimer == nil then 
			elseif g_currentMission.time < self.vcaActiveWATimer then 
				hasActiveWorkArea     = true 
			else 
				self.vcaActiveWATimer = nil 
			end 	
		
			if hasActiveWorkArea then 
				idleRpm = math.min( math.max( minRpm, self.minRpm + 0.4 * rpmRange ), maxRpm )
			end 		
			if self.smoothedLoadPercentage > 0.8 then 	
				idleRpm = math.min( maxRpm,math.max( minRpm, idleRpm, peakPowerRpm - 0.1 * rpmRange ) )
			end 
			
			if self.smoothedLoadPercentage > 0.9 then 	
			-- reduce maxRpm under full load
				local f = 10 * self.smoothedLoadPercentage - 9
				maxRpm  = math.min( maxRpm,math.max( minRpm, idleRpm, peakPowerRpm - 0.1 * f * rpmRange, speedMinRpm ) )
			else
				local r = self.maxRpm 
				if self.smoothedLoadPercentage < 0.7 then 
				-- reduce maxRpm for best efficiency
					r = speedMinRpm
				end 
				maxRpm = math.min( maxRpm, math.max( minRpm, idleRpm + 0.1 * rpmRange, r ) )
			end 
			
			if self.vcaIncreaseRpm ~= nil and g_currentMission.time < self.vcaIncreaseRpm then 
				minRpm = math.max( minRpm, math.min( idleRpm, maxRpm ) )
			end		
		end 
	end 
	
	local r = nil 
	if self.vehicle.lastSpeedReal > 0.001 then 
		local sf = self:getNonClampedMotorRpm() / self.vehicle.lastSpeedReal
		if lastSpeedFactor == nil then 
			self.vcaSpeedFactor = sf 
		else 
			self.vcaSpeedFactor = lastSpeedFactor + 0.01 * ( sf - lastSpeedFactor )
			r = self.vcaSpeedFactor * self.vehicle.lastSpeedReal
		end 
	end 
	
	local smooth = self.vehicle.spec_vca.tickDt * rpmRange
	if self.backwardGears or self.forwardGears or self.vehicle.spec_vca.useIdleThrottle then  
		smooth = smooth * 0.0005
	else 
		smooth = smooth * 0.0001
		if r ~= nil then 
			local m1, m2 = minRpm, maxRpm
			minRpm = math.max( m1, math.min( self:getNonClampedMotorRpm() - self.vehicle.spec_vca.tickDt * 0.00025 * rpmRange, r, m2 ) )
			maxRpm = math.min( m2, math.max( self:getNonClampedMotorRpm() + self.vehicle.spec_vca.tickDt * 0.00025 * rpmRange, r, m1 ) )
		end 
	end 
	
	if r == nil then 
		minRpm = vehicleControlAddon.mbClamp( minRpm, lastMinRpm - smooth, lastMinRpm + smooth )
		maxRpm = vehicleControlAddon.mbClamp( maxRpm, lastMaxRpm - smooth, lastMaxRpm + smooth )
	else 
		minRpm = vehicleControlAddon.mbClamp( minRpm, math.min( lastMinRpm - smooth, r ), lastMinRpm + smooth )
		maxRpm = vehicleControlAddon.mbClamp( maxRpm, lastMaxRpm - smooth, math.max( lastMaxRpm + smooth, r ) )
	end 
	
	self.vcaMinRpm = minRpm 
	self.vcaMaxRpm = maxRpm 
	
	return minRpm, maxRpm
end

VehicleMotor.getRequiredMotorRpmRange = Utils.overwrittenFunction( VehicleMotor.getRequiredMotorRpmRange, vehicleControlAddon.vcaGetRequiredMotorRpmRange )

--******************************************************************************************************************************************

function vehicleControlAddon:vcaGetGearToDisplay( superFunc )
	if      self.vehicle          ~= nil        
			and self.vehicle.spec_vca ~= nil 
			and self.vehicle.spec_vca.handbrake 
			then 
		if self.backwardGears or self.forwardGears then
			return "P", false, false, nil, nil, nil, nil, false 
		else 
			return "P", false, false, "R", "D", nil, nil, false 
		end 
	end
	return superFunc( self )
end 

VehicleMotor.getGearToDisplay = Utils.overwrittenFunction( VehicleMotor.getGearToDisplay, vehicleControlAddon.vcaGetGearToDisplay )

--******************************************************************************************************************************************
--
function vehicleControlAddon:vcaGetLastModulatedMotorRpm( superFunc, ... )
	if not ( self.vehicle ~= nil 
			 and self.vehicle.spec_vca ~= nil 
			 and self.vehicle.spec_vca.idleThrottle
			 and self.lastRealMotorRpm ~= nil 
			 and (self.backwardGears or self.forwardGears)
			 and next(self.vehicle.spec_motorized.differentials) ~= nil
			) then 
		return superFunc( self, ... )
	end 
	return self.lastRealMotorRpm
end 

VehicleMotor.getLastModulatedMotorRpm = Utils.overwrittenFunction( VehicleMotor.getLastModulatedMotorRpm, vehicleControlAddon.vcaGetLastModulatedMotorRpm )

--******************************************************************************************************************************************
-- overwrite automatic shifting to make it local per vehicle
vehicleControlAddon.vcaVehicleMotorGetUseAutomaticGearShifting = VehicleMotor.getUseAutomaticGearShifting
VehicleMotor.getUseAutomaticGearShifting = function( self, ... )
	if vehicleControlAddon.vcaVehicleMotorGetUseAutomaticGearShifting( self, ... ) then 
		return true 
	end 
	
	if      self.vehicle          ~= nil	
			and self.vehicle.spec_vca ~= nil 
			and self.vehicle.spec_vca.isInitialized
			and self.vehicle.spec_vca.autoShift then 
		return true 
	end 
	return vehicleControlAddon.vcaVehicleMotorGetUseAutomaticGearShifting( self, ... )
end 
--******************************************************************************************************************************************

vehicleControlAddon.vcaVehicleMotorGetUseAutomaticGroupShifting = VehicleMotor.getUseAutomaticGroupShifting
VehicleMotor.getUseAutomaticGroupShifting = function ( self, ... )
	if vehicleControlAddon.vcaVehicleMotorGetUseAutomaticGroupShifting( self, ... ) then 
		return true 
	end 
	
	if      self.vehicle          ~= nil	
			and self.vehicle.spec_vca ~= nil 
			and self.vehicle.spec_vca.isInitialized
			and self.vehicle.spec_vca.autoShift then 
		return true 
	end 
	return vehicleControlAddon.vcaVehicleMotorGetUseAutomaticGroupShifting( self, ... )
end 
--******************************************************************************************************************************************
function vehicleControlAddon:vcaGetGearRatioMultiplier( superFunc, ... )
	if      self.vehicle          ~= nil	
			and self.vehicle.spec_vca ~= nil 
			and self.vehicle.spec_vca.isInitialized
			and self.vehicle.spec_vca.splitGears > 0 
			and self.vehicle.spec_vca.gearSplitGear <= self.vehicle.spec_vca.splitGears
			then 
		local r = superFunc( self, ... )
		local g = self.vehicle.spec_vca.gearSplitGear
		local q = vehicleControlAddon.splitGearRatios[self.vehicle.spec_vca.splitGearRatio]
		if q == nil then q = 0.8 end 
		while g <= self.vehicle.spec_vca.splitGears do 
			r = r / q
			g = g + 1 
		end 
		return r
	end 
	return superFunc( self, ... )
end 

VehicleMotor.getGearRatioMultiplier = Utils.overwrittenFunction( VehicleMotor.getGearRatioMultiplier, vehicleControlAddon.vcaGetGearRatioMultiplier )
--******************************************************************************************************************************************

function vehicleControlAddon:vcaOnSetSnapAngle( old, new, noEventSend )
	if new < 1 then 
		self.spec_vca.snapAngle = 1 
	elseif new > table.getn( vehicleControlAddon.snapAngles ) then 
		self.spec_vca.snapAngle = table.getn( vehicleControlAddon.snapAngles ) 
	else 
		self.spec_vca.snapAngle = new 
	end 
end 

function vehicleControlAddon:vcaOnSetSnapIsOn( old, new, noEventSend )
	self.spec_vca.snapIsOn = new 
	
  if      ( old == nil or new ~= old ) then 
		if self.isClient and self:vcaIsActive() then
			if new and vehicleControlAddon.snapOnSample ~= nil then
				playSample(vehicleControlAddon.snapOnSample, 1, 0.2, 0, 0, 0)
			elseif not new and vehicleControlAddon.snapOffSample ~= nil then
				playSample(vehicleControlAddon.snapOffSample, 1, 0.2, 0, 0, 0)
			end 
		end 
		
		if self.isServer and new and self.spec_vca.snapDistance < 0.1 then
			local d, o, p = self:vcaGetSnapDistance()
			if d < 0.1 then 
				d = 1
				o = 0
				p = false 
			end 
			self:vcaSetState( "snapDistance", d, noEventSend )
			self:vcaSetState( "snapOffset", o, noEventSend )
			self:vcaSetState( "snapInvert", p, noEventSend )
		end
	elseif new and self.isClient and self:vcaIsActive() and vehicleControlAddon.snapOnSample ~= nil then
		playSample(vehicleControlAddon.snapOnSample, 1, 0.2, 0, 0, 0)
	end 
end 

function vehicleControlAddon:vcaOnSetKSIsOn( old, new, noEventSend )
	self.spec_vca.ksIsOn = new 
end 

function vehicleControlAddon:vcaOnSetLastSnapAngle( old, new, noEventSend )
	self.spec_vca.lastSnapAngle = new 
end 


function vehicleControlAddon:vcaOnSetWarningText( old, new, noEventSend )
	self.spec_vca.warningText  = new
  self.spec_vca.warningTimer = 4000
end

function vehicleControlAddon:vcaOnSetSplitGears( old, new, noEventSend )
	if type( new ) == "number" and 0 <= new and new <= 3 then  
		self.spec_vca.splitGears = new 
	else 
		self.spec_vca.splitGears = 0 
	end 
	
	self:vcaSetState( "gearSplitGear", self.spec_vca.splitGears + 1, noEventSend )
end 

function vehicleControlAddon:vcaOnSetSplitGearRatio( old, new, noEventSend )
	if type( new ) == "number" and vehicleControlAddon.splitGearRatios[new] ~= nil then 
		self.spec_vca.splitGearRatio = new 
	else 
		self.spec_vca.splitGearRatio = 1 
	end 

	if      self.spec_vca.isInitialized 
			and self.spec_vca.splitGears > 0
			and self.spec_motorized       ~= nil 
			and self.spec_motorized.motor ~= nil
			and self.spec_motorized.motor.currentGears ~= nil then 
		self.spec_motorized.motor:applyTargetGear()
	end 
end 

function vehicleControlAddon:vcaOnSetGearSplitGear( old, new, noEventSend )
	g = math.max( 1, Utils.getNoNil( self.spec_vca.splitGears, 0 ) + 1 )
	if type( new ) ~= "number" or new < 1 then  
		self.spec_vca.gearSplitGear = 1
	elseif new > g then 
		self.spec_vca.gearSplitGear = g
	else 
		self.spec_vca.gearSplitGear = new 
	end 
	
	if      old ~= nil
			and new ~= nil 
			and old ~= new 
			and self.spec_vca.isInitialized 
			and self.spec_vca.splitGears > 0
			and self.spec_motorized       ~= nil 
			and self.spec_motorized.motor ~= nil
			and self.spec_motorized.motor.currentGears ~= nil then 
			
		self.spec_motorized.motor:applyTargetGear()
		
		if new > old then 
			self.spec_motorized.motor.loadPercentageChangeCharge = 1
		end 
		
		if self.isClient and not g_soundManager:getIsSamplePlaying(self.spec_motorized.samples.gearGroupChange) then
			g_soundManager:playSample(self.spec_motorized.samples.gearGroupChange)
		end
	end 
end 

function vehicleControlAddon:vcaGetAbsolutRotY( camIndex )
	if     self.spec_enterable.cameras == nil
			or self.spec_enterable.cameras[camIndex] == nil then
		return 0
	end
  return vehicleControlAddon.vcaGetRelativeYRotation( self.spec_enterable.cameras[camIndex].cameraNode, self:vcaGetSteeringNode() )
end

function vehicleControlAddon.vcaGetRelativeYRotation(root,node)
	if root == nil or node == nil then
		return 0
	end
	local x, y, z = worldDirectionToLocal(node, localDirectionToWorld(root, 0, 0, 1))
	local dot = z
	local len = 0
	if math.abs( z ) < 1e-6 then 
		len = math.abs( x )
	elseif math.abs( x ) < 1e-6 then 
		len = math.abs( z ) 
	else 
		len = math.sqrt( x*x + z*z )
	end 
	dot = dot / len
	local angle = math.acos(dot)
	if x < 0 then
		angle = -angle
	end
	return angle
end

function vehicleControlAddon.vcaSpeedInt2Ext( speed )
	if not ( type( speed ) == "number" ) then 
		return -1
	end 
	local s = speed * 3.6 
	if g_gameSettings.useMiles then 
		s = s * 0.621371
	end 
	return s --0.1 * math.floor( 10 * s + 0.5 )
end 
function vehicleControlAddon.vcaSpeedExt2Int( speed )
	if not ( type( speed ) == "number" ) then 
		return -1
	end 
	local s = speed / 3.6 
	if g_gameSettings.useMiles then 
		s = s / 0.621371
	end 
	return s
end 
function vehicleControlAddon:vcaSpeedToString( speed, numberFormat, noUnit )
	if speed == nil then	
		return "nil" 
	end 
	local s = vehicleControlAddon.vcaSpeedInt2Ext( speed )
	local f = numberFormat
	if f == nil then 
		if     math.abs( s ) < 0.995 then 
			f = "%4.2f"
		elseif math.abs( s ) < 9.95 then 
			f = "%3.1f" 
		else 
			f = "%3.0f"
		end 
	end 
	if noUnit then 
		return string.format( f, s )
	end 
	local u = "km/h" 
	if g_gameSettings.useMiles then 
		u = "mph"
	end 
	return string.format( f, s ).." "..u 	
end 






function vehicleControlAddon:vcaUIShowautoShift()
	if not self.isServer then 
		return self.spec_vca.hasGearsAuto
	end 
	if      self.spec_motorized       ~= nil 
			and self.spec_motorized.motor ~= nil 	
			and self.spec_motorized.motorizedNode ~= nil
			and next(self.spec_motorized.differentials) ~= nil
			then 
		local motor = self.spec_motorized.motor 
		if not (motor.backwardGears or motor.forwardGears) then 
			return false 
		elseif vehicleControlAddon.vcaVehicleMotorGetUseAutomaticGearShifting( motor ) and vehicleControlAddon.vcaVehicleMotorGetUseAutomaticGroupShifting( motor ) then 
			return false 
		end 
		return true
	end 
	return false
end 



function vehicleControlAddon:vcaUIShowidleThrottle()
	if not self.isServer then 
		return self.spec_vca.hasGearsIdle
	end 
	if      self.spec_motorized       ~= nil 
			and self.spec_motorized.motor ~= nil 	
			and self.spec_motorized.motorizedNode ~= nil
			and next(self.spec_motorized.differentials) ~= nil
			then 
		local motor = self.spec_motorized.motor 
	--if motor.gearShiftMode ~= VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH and (motor.backwardGears or motor.forwardGears) then
	--if (motor.backwardGears or motor.forwardGears) then
			return true 
	--end 
	end 
	return false
end 


function vehicleControlAddon:beforeSpeedMeterDisplayDraw( )
	if self.isVehicleDrawSafe and self:getVisible() 
			and self.vehicle ~= nil 
			and self.vehicle.spec_vca ~= nil 
			and self.vehicle.spec_vca.splitGears ~= nil 
			and self.vehicle.spec_vca.splitGears > 0 then 
		self.vcaHideIcon = true 
		self.gearIcon:setVisible(false) 
		
		setTextColor(unpack(vehicleControlAddon.colorActive))
		setTextAlignment( RenderText.ALIGN_CENTER ) 
		setTextVerticalAlignment( RenderText.VERTICAL_ALIGN_MIDDLE )
		setTextBold(true)					
		
		local overlay = self.gearIcon.overlay 
		local x = overlay.x + overlay.offsetX + 0.5 * overlay.width
		local y = overlay.y + overlay.offsetY + 0.5 * overlay.height
		
		local vca = self.vehicle.spec_vca 
		local suffix = ""
		if     vca.splitGears < 1 then 
		elseif vca.splitGears < 3 then 
		-- 2 or 3 powershift gears 
			if vca.gearSplitGear <= 1 then 
				suffix = " Lo"
			elseif vca.gearSplitGear > vca.splitGears then 
				suffix = " Hi"
			else 
				suffix = " Me"
			end 
		elseif vca.splitGears < 4 then 
			if     vca.gearSplitGear <= 1 then 
				suffix = " I"
			elseif vca.gearSplitGear <= 2 then 
				suffix = " II"
			elseif vca.gearSplitGear <= 3 then 
				suffix = " III"
			else 
				suffix = " IV"
			end 
		else 
			suffix = string.format( " (%d)", vca.gearSplitGear )
		end 
		
		renderText( x, y, self.gearTextSize, suffix)
		
		setTextAlignment( RenderText.ALIGN_LEFT ) 
		setTextVerticalAlignment( RenderText.VERTICAL_ALIGN_BASELINE )
		setTextColor(1, 1, 1, 1) 
		setTextBold(false)					
		
	elseif self.vcaHideIcon then 
		self.gearIcon:setVisible(true)
		self.vcaHideIcon = nil 
	end 
end 






SpeedMeterDisplay.draw = Utils.prependedFunction( SpeedMeterDisplay.draw, vehicleControlAddon.beforeSpeedMeterDisplayDraw )
 
vehicleControlAddon.createStates()