--
-- Ropa euro-Tiger V8-3 XL
--
-- @author: 	Burner
-- @date: 		29.04.2013
--

RopaEuroTiger_XL = {};


RopaEuroTiger_XL.SynchronizedSteering = 1;
RopaEuroTiger_XL.OffsetSteerLeft = 2;
RopaEuroTiger_XL.OffsetSteerRight = 3;
RopaEuroTiger_XL.TotalSteer = 4;
RopaEuroTiger_XL.AllWheelSteer = 5;

function RopaEuroTiger_XL.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Combine, specializations) and SpecializationUtil.hasSpecialization(AICombine, specializations);
end;

function RopaEuroTiger_XL:load(xmlFile)
	
	self.getIsStartThreshingAllowed = Utils.overwrittenFunction(self.getIsStartThreshingAllowed, RopaEuroTiger_XL.getIsStartThreshingAllowed);
	self.getIsPipeUnloadingAllowed = Utils.overwrittenFunction(self.getIsPipeUnloadingAllowed, RopaEuroTiger_XL.getIsPipeUnloadingAllowed);
	self.getIsPipeStateChangeAllowed = Utils.overwrittenFunction(self.getIsPipeStateChangeAllowed, RopaEuroTiger_XL.getIsPipeStateChangeAllowed);
	self.getIsFoldAllowed = Utils.overwrittenFunction(self.getIsFoldAllowed, RopaEuroTiger_XL.getIsFoldAllowed);
	self.handleAttachAttachableEvent = Utils.overwrittenFunction(self.handleAttachAttachableEvent, RopaEuroTiger_XL.handleAttachAttachableEvent);
	self.setPipeState = Utils.appendedFunction(self.setPipeState, RopaEuroTiger_XL.setPipeState);
	self.setVehicleRpmUp = SpecializationUtil.callSpecializationsFunction("setVehicleRpmUp");
	self.changePlane = SpecializationUtil.callSpecializationsFunction("changePlane");
	--self.toggleWindowState = SpecializationUtil.callSpecializationsFunction("toggleWindowState");
	--self.toggleDoorState = SpecializationUtil.callSpecializationsFunction("toggleDoorState");
	self.setSteeringMode = SpecializationUtil.callSpecializationsFunction("setSteeringMode");
	self.steeringMode = RopaEuroTiger_XL.SynchronizedSteering;
	
	self.driveShafts = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.driveShafts.driveShaft(%d)", i);
		if not hasXMLProperty(xmlFile, key) then
			break;
		end;
		local driveShaft = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
		local speed = Utils.getNoNil(getXMLFloat(xmlFile, key.."#speed"), 1);
		if driveShaft ~= nil then
			table.insert(self.driveShafts, {node=driveShaft, speed=speed});
		end;
		i = i + 1;
	end;
	
	self.cutterAttachedItems = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.cutterAttachedItems.cutterAttachedItem(%d)", i);
		if not hasXMLProperty(xmlFile, key) then
			break;
		end;
		local cutterAttachedItem = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
		local visibility = Utils.getNoNil(getXMLBool(xmlFile, key.."#visibility"), true);
		if cutterAttachedItem ~= nil then
			table.insert(self.cutterAttachedItems, {node=cutterAttachedItem, visibility=visibility});
		end;
		i = i + 1;
	end;
	
	for _, cutterAttachedItem in pairs(self.cutterAttachedItems) do
		if self.numAttachedCutters > 0 then
			setVisibility(cutterAttachedItem.node, cutterAttachedItem.visibility);
		else
			setVisibility(cutterAttachedItem.node, not cutterAttachedItem.visibility);
		end;
	end;
	
	local gaspedalNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.gaspedal#index"));
    if gaspedalNode ~= nil then
        self.gaspedal = {};
        self.gaspedal.node = gaspedalNode;    
		self.gaspedal.maxSpeed = getXMLInt(xmlFile, "vehicle.gaspedal#maxSpeed");
		local x1,y1,z1 = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.gaspedal#minRot"));
		local x2,y2,z2 = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.gaspedal#maxRot"));		
		self.gaspedal.rotMin = {math.rad(Utils.getNoNil(x1,0)),math.rad(Utils.getNoNil(y1,0)),math.rad(Utils.getNoNil(z1,0))};
		self.gaspedal.rotMax = {math.rad(Utils.getNoNil(x2,0)),math.rad(Utils.getNoNil(y2,0)),math.rad(Utils.getNoNil(z2,0))};
    end;
	
    local brakepedalNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.brakepedal#index"));
    if brakepedalNode ~= nil then
        self.brakepedal = {};
        self.brakepedal.node = brakepedalNode;
		local x1,y1,z1 = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.gaspedal#minRot"));
		local x2,y2,z2 = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.gaspedal#maxRot"));
		self.brakepedal.rotMin = {math.rad(Utils.getNoNil(x1,0)),math.rad(Utils.getNoNil(y1,0)),math.rad(Utils.getNoNil(z1,0))};			
		self.brakepedal.rotMax = {math.rad(Utils.getNoNil(x2,0)),math.rad(Utils.getNoNil(y2,0)),math.rad(Utils.getNoNil(z2,0))};
        self.brakepedal.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.brakepedal#rotTime"), 1)*1000;
        self.brakepedalIsActive = false;
    end;
	
	self.transportPlate = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.transportPlate#index"));
	
	local index = getXMLInt(xmlFile, "vehicle.articulatedAxis#componentJointIndex");
    if index ~= nil then
        local componentJoint = self.componentJoints[index+1];
        local rotSpeed = getXMLFloat(xmlFile, "vehicle.articulatedAxis#rotSpeed");
        local rotMax = getXMLFloat(xmlFile, "vehicle.articulatedAxis#rotMax");
        local rotMin = getXMLFloat(xmlFile, "vehicle.articulatedAxis#rotMin");
        if componentJoint ~= nil and rotSpeed ~= nil and rotMax ~= nil and rotMin ~= nil then
            rotSpeed = math.rad(rotSpeed);
            rotMax = math.rad(rotMax);
            rotMin = math.rad(rotMin);
            local entry = {};

            entry.componentJoint = componentJoint;
            entry.anchorActor = Utils.getNoNil(getXMLInt(xmlFile,  "vehicle.articulatedAxis#anchorActor"), 0);

            entry.rotMax = rotMax;
            entry.rotMin = rotMin;
            entry.rotSpeed = rotSpeed;


            local maxRotTime = rotMax/rotSpeed;
            local minRotTime = rotMin/rotSpeed;
            if minRotTime > maxRotTime then
                local temp = minRotTime;
                minRotTime = maxRotTime;
                maxRotTime = temp;
            end;
            if maxRotTime > self.maxRotTime then
                self.maxRotTime = maxRotTime;
            end;
            if minRotTime < self.minRotTime then
                self.minRotTime = minRotTime;
            end;

            entry.curRot = 0;

            self.articulatedAxis = entry;
        end;
    end;
	self.lastRotatedTime = 0;
	
	self.rollNodes = {};
    local i = 0;
    while true do
        local key = string.format("vehicle.rolls.roll(%d)", i);
        if not hasXMLProperty(xmlFile, key) then
            break;
        end;
        local rollNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
        local speed = {}
		local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#speed"));
		speed[1] = Utils.getNoNil(x, 0.003);
		speed[2] = Utils.getNoNil(y, 0.003);
		speed[3] = Utils.getNoNil(z, 0.003);
        if rollNode ~= nil then
            table.insert(self.rollNodes, {node=rollNode, speed=speed});
        end;
        i = i + 1;
    end;
	
	self.combineThreshingFillScrollers = {};

	local hasFruitTypes = true
	local i = 0;
    while true do
        local key = string.format("vehicle.combineThreshingFillScrollers.combineThreshingFillScroller".."(%d)", i);
        if not hasXMLProperty(xmlFile, key) then
			break;
        end
        local node = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
        local shaderParameterName = getXMLString(xmlFile, key.."#shaderParameterName");
        local fruitDesc = nil;
        if hasFruitTypes then
            local fruitType = getXMLString(xmlFile, key.."#type");
            if fruitType ~= nil then
                fruitDesc = FruitUtil.fruitTypes[fruitType];
            end
        end
        if node ~= nil and shaderParameterName ~= nil and (fruitDesc ~= nil or not hasFruitTypes) then
            local scrollSpeed = Utils.getNoNil(getXMLFloat(xmlFile, key.."#scrollSpeed"), 1)*0.001;
			newSpeed = Utils.getVectorNFromString(getXMLString(xmlFile, key.."#newSpeed"), 2);
			if newSpeed == nil then
				newSpeed = {}
				newSpeed[1] = 0;
				newSpeed[2] = 0;
			else
				newSpeed[1] = newSpeed[1]*0.001;
				newSpeed[2] = newSpeed[2]*0.001;
			end;
            local shaderParameterComponent = Utils.getNoNil(getXMLInt(xmlFile, key.."#shaderParameterComponent"), 1);
            local scrollLength = Utils.getNoNil(getXMLFloat(xmlFile, key.."#scrollLength"), 1);
            local toggleVisibility = Utils.getNoNil(getXMLBool(xmlFile, key.."#toggleVisibility"), false);
            if toggleVisibility then
                setVisibility(node, false);
            end
            local insertList = self.combineThreshingFillScrollers;
            if hasFruitTypes then
                insertList = self.combineThreshingFillScrollers[fruitDesc.index];
                if insertList == nil then
                    insertList = {};
                    self.combineThreshingFillScrollers[fruitDesc.index] = insertList;
                end
            end
            table.insert(insertList, {node=node, scrollSpeed=scrollSpeed, scrollPosition=0, scrollPosition1=0, scrollPosition2=0, scrollLength=scrollLength, shaderParameterName=shaderParameterName, shaderParameterComponent=shaderParameterComponent, toggleVisibility=toggleVisibility, newSpeed=newSpeed});
        end
        i = i + 1;
    end

	self.maxNumRealLights = 3;
	
	self.screwConveyor = {};
	self.screwConveyor.node = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.screwConveyor#index"));
	self.screwConveyor.speed = Utils.getNoNil(getXMLFloat(xmlFile,  "vehicle.screwConveyor#speed"), 0.006);
	self.screwConveyor.currentSpeed = self.screwConveyor.speed;
	self.screwConveyor.invertedSpeed = -self.screwConveyor.speed;
	
	self.saveMinRpm = self.motor.minRpm;
	self.motorSoundRunVolumeBackup = self.motorSoundRunVolume;
	self.motorSoundRunVolume = 0;

	local camNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.cameraBend#node"));
    if camNode ~= nil then
        self.cameraBend = {};
		self.cameraBend.camIndex = getXMLInt(xmlFile, "vehicle.cameraBend#camIndex");
        self.cameraBend.node = camNode;
		local x1,y1,z1 = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.cameraBend#transMin"));
		local x2,y2,z2 = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.cameraBend#transMax"));
		self.cameraBend.transMin = {Utils.getNoNil(x1,0),Utils.getNoNil(y1,0),Utils.getNoNil(z1,0)};
		self.cameraBend.transMax = {Utils.getNoNil(x2,0),Utils.getNoNil(y2,0),Utils.getNoNil(z2,0)};
		self.cameraBend.moveTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.cameraBend#moveTime"), 1)*1000;
    end;
	self.cameraBendActive = false;
	
	self.cutterHydraulicHoses = {};
    local i = 0;
    while true do
        local key = string.format("vehicle.cutterHydraulicHoses.cutterHydraulicHose(%d)", i);
        if not hasXMLProperty(xmlFile, key) then
            break;
        end;
		hose = {}
        hose.node = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
        local x1,y1,z1 = Utils.getVectorFromString(getXMLString(xmlFile, key.."#minRot"));
		local x2,y2,z2 = Utils.getVectorFromString(getXMLString(xmlFile, key.."#maxRot"));
		hose.rotMin = {math.rad(Utils.getNoNil(x1,0)),math.rad(Utils.getNoNil(y1,0)),math.rad(Utils.getNoNil(z1,0))};			
		hose.rotMax = {math.rad(Utils.getNoNil(x2,0)),math.rad(Utils.getNoNil(y2,0)),math.rad(Utils.getNoNil(z2,0))};
        hose.rotTime = Utils.getNoNil(getXMLString(xmlFile, key.."#rotTime"), 1)*1000;
		local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#scaleMin"));
		hose.scaleMin = {Utils.getNoNil(x,1),Utils.getNoNil(y,1),Utils.getNoNil(z,1)};
		local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#scaleMax"));
		hose.scaleMax = {Utils.getNoNil(x,1),Utils.getNoNil(y,1),Utils.getNoNil(z,1)};
        if hose.node ~= nil then
            table.insert(self.cutterHydraulicHoses, hose);
        end;
        i = i + 1;
    end;
	
	self.loading = true;
	
	self.grainTankFillLevelBackup = self.grainTankFillLevel;
	
	self.loadingPlanes = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.loadingPlanes.loadingPlane(%d)", i);
		if not hasXMLProperty(xmlFile, key) then
			break;
		end;
		local planeNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
		local v = getXMLString(xmlFile, key .. "#type");
		local fillType = Fillable.fillTypeNameToInt[v];
		if planeNode ~= nil and fillType ~= nil then
			table.insert(self.loadingPlanes, {node=planeNode, fillType=fillType});
		end;
		i = i + 1;
	end;
	
	self.unloadingPlanes = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.unloadingPlanes.unloadingPlane(%d)", i);
		if not hasXMLProperty(xmlFile, key) then
			break;
		end;
		local planeNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
		local v = getXMLString(xmlFile, key .. "#type");
		local fillType = Fillable.fillTypeNameToInt[v];
		if planeNode ~= nil and fillType ~= nil then
			table.insert(self.unloadingPlanes, {node=planeNode, fillType=fillType});
		end;
		i = i + 1;
	end;
	
	if self.isClient then
		self.unloadingSoundEnabled = false;
		local unloadingSound = getXMLString(xmlFile, "vehicle.unloadingSound#file");
		if unloadingSound ~= nil and unloadingSound ~= "" then
			unloadingSound = Utils.getFilename(unloadingSound, self.baseDirectory);
			self.unloadingSound = createSample("unloadingSound");
			loadSample(self.unloadingSound, unloadingSound, false);
			self.unloadingSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.unloadingSound#pitchOffset"), 1);
			self.unloadingSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.unloadingSound#volume"), 1.0);
			setSamplePitch(self.unloadingSound, self.unloadingSoundPitchOffset);
		end;

		self.hydraulicSoundEnabled = false;
		local hydraulicSound = getXMLString(xmlFile, "vehicle.hydraulicSound#file");
		if hydraulicSound ~= nil and hydraulicSound ~= "" then
			hydraulicSound  = Utils.getFilename(hydraulicSound, self.baseDirectory);
			self.hydraulicSound = createSample("hydraulicSound");
			loadSample(self.hydraulicSound, hydraulicSound, false);
			self.hydraulicSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.hydraulicSound#pitchOffset"), 1.0);
			self.hydraulicSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.hydraulicSound#volume"), 1.0);
			setSamplePitch(self.hydraulicSound, self.hydraulicSoundPitchOffset);
		end;
	end;
	
	self.firstRun = true;
end;

function RopaEuroTiger_XL:delete()
	if self.unloadingSound ~= nil then
		delete(self.unloadingSound);
	end
	if self.hydraulicSound ~= nil then
		delete(self.hydraulicSound);
	end
end;

function RopaEuroTiger_XL:getSaveAttributesAndNodes(nodeIdent)
	local attributes = 'steeringMode="'.. tostring(self.steeringMode) ..'"';
	return attributes, nil;
end;

function RopaEuroTiger_XL:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	local steeringMode =  Utils.getNoNil(getXMLInt(xmlFile, key.."#steeringMode"), self.steeringMode);
	self.steeringMode = steeringMode;
	return BaseMission.VEHICLE_LOAD_OK;
end;

function RopaEuroTiger_XL:readStream(streamId, connection)  
	self:setSteeringMode(streamReadInt8(streamId), true);
end;

function RopaEuroTiger_XL:writeStream(streamId, connection)
	streamWriteInt8(streamId, self.steeringMode);
end;

function RopaEuroTiger_XL:mouseEvent(posX, posY, isDown, isUp, button)
end;

function RopaEuroTiger_XL:keyEvent(unicode, sym, modifier, isDown)
end;

function RopaEuroTiger_XL:update(dt)
	if self.firstRun and self.isServer then
		self:setSteeringMode(self.steeringMode);
		self.firstRun = false;
	end;
	if self.motorSoundEnabled and self:getIsActiveForSound() then
		local soundPercent = 1;
		--[[
		if self.camIndex == 2 and self.animationParts[9].clipStartTime and  self.animationParts[10].clipStartTime then
			soundPercent = 0.4;
		end;
		]]
		if self.motorSoundRunVolume <= self.motorSoundRunVolumeBackup then
			self.motorSoundRunVolume = self.motorSoundRunVolume+(dt/1000)
		end
		setSampleVolume(self.motorSoundRun, math.min(self.motorSoundRunVolume*soundPercent, self.motorSoundRunVolumeBackup));
	else
		self.motorSoundRunVolume = 0;
	end;
	
	if self:getIsActiveForInput(false) then
		--[[
		if InputBinding.hasEvent(InputBinding.RopaEuroTiger_WindowOpen) then
			self:toggleWindowState();
		end;
		
		if InputBinding.hasEvent(InputBinding.RopaEuroTiger_DoorOpen) then
			self:toggleDoorState();
		end;
		]]
		
		if self.isMotorStarted then
			if InputBinding.hasEvent(InputBinding.RopaEuroTiger_OffsetSteerLeft) then
				self:setSteeringMode(RopaEuroTiger_XL.OffsetSteerLeft);
			end;
			
			if InputBinding.hasEvent(InputBinding.RopaEuroTiger_OffsetSteerRight) then
				self:setSteeringMode(RopaEuroTiger_XL.OffsetSteerRight);
			end;
			
			if InputBinding.hasEvent(InputBinding.RopaEuroTiger_SynchronizedSteering) then
				self:setSteeringMode(RopaEuroTiger_XL.SynchronizedSteering);
			end;
			
			if InputBinding.hasEvent(InputBinding.RopaEuroTiger_AllWheelSteer) then
				self:setSteeringMode(RopaEuroTiger_XL.AllWheelSteer);
			end;
			
			if InputBinding.hasEvent(InputBinding.RopaEuroTiger_TotalSteer) then
				self:setSteeringMode(RopaEuroTiger_XL.TotalSteer);
			end;
			
			if self:getIsPipeStateChangeAllowed(2) then
				if InputBinding.isPressed(InputBinding.RopaEuroTiger_PipeUp) then
					self:setAnimationTime(7, self.animationParts[7].currentPosition-(self.animationParts[7].offSet+dt), false);
				elseif InputBinding.isPressed(InputBinding.RopaEuroTiger_PipeDown) then
					self:setAnimationTime(7, self.animationParts[7].currentPosition+(self.animationParts[7].offSet+dt), false);
				end;
			end;
		end
		
		if self.camIndex == self.cameraBend.camIndex then
			if InputBinding.isPressed(InputBinding.RopaEuroTiger_CameraBend) then
				self.cameraBendActive = true;
			else
				self.cameraBendActive = false;
			end;
		end;
	end;

	if self:getIsActive() then
		for _, driveShaft in pairs(self.driveShafts) do
			rotate(driveShaft.node, 0, 0, 2.5 * self.lastSpeedReal * self.movingDirection * dt * driveShaft.speed);
		end;
		
		if g_currentMission.environment.lastRainScale > 0.1 and g_currentMission.environment.timeSinceLastRain < 30 and self.isMotorStarted then
			self:setAnimationTime(8, 1);
		else
			self:setAnimationTime(8, 0);
		end;
	end;
	
	-- update front wheels angle
	local x, y, z = getRotation(self.animationParts[4].rootNode);
	for i=1, 2 do
		self.wheels[i].rotMin = y;
		self.wheels[i].rotMax = -y;
	end;
	
	-- update back wheels angle
	local x, y, z = getRotation(self.animationParts[2].rootNode);
	for i=3, 4 do
		self.wheels[i].rotMin = y;
		self.wheels[i].rotMax = -y;
	end;
	
	-- last wheels have bigger turning angle
	for i=5, 6 do
		self.wheels[i].rotMin = y * 1.36;
		self.wheels[i].rotMax = -y * 1.36;
	end;
		
	-- update articulated axis angle
	local x, y, z = getRotation(self.animationParts[3].rootNode);
	self.articulatedAxis.rotMax = -y;
	self.articulatedAxis.rotMin = y;
	
	-- articulated axis
	if self.isServer then
        if self:getIsActive() then
            if self.articulatedAxis ~= nil then
                --if self.lastRotatedTime ~= self.rotatedTime then
                    self.lastRotatedTime = self.rotatedTime;
                    local steeringAngle = self.rotatedTime * self.articulatedAxis.rotSpeed;
                    if steeringAngle > self.articulatedAxis.rotMax then
                        steeringAngle = self.articulatedAxis.rotMax;
                    elseif steeringAngle < self.articulatedAxis.rotMin then
                        steeringAngle = self.articulatedAxis.rotMin;
                    end;
                    if math.abs(steeringAngle - self.articulatedAxis.curRot) > 0.001 then
                        setRotation(self.articulatedAxis.componentJoint.jointNode, 0, steeringAngle, 0);
                        setJointFrame(self.articulatedAxis.componentJoint.jointIndex, self.articulatedAxis.anchorActor, self.articulatedAxis.componentJoint.jointNode);
                        self.articulatedAxis.curRot = steeringAngle;
                    end;
                --end;
            end;
        end;
    end;
	
	if self:getIsActive() and self.isThreshing and self.isClient then
		if self.combineIsFilling and self.currentCombineThreshingFillScrollers ~= nil then
            for _, scroller in pairs(self.currentCombineThreshingFillScrollers) do
                scroller.scrollPosition1 = (scroller.scrollPosition1 + dt*(scroller.newSpeed[1])) % scroller.scrollLength;
				scroller.scrollPosition2 = (scroller.scrollPosition2 + dt*(scroller.newSpeed[2])) % scroller.scrollLength;
                setShaderParameter(scroller.node, scroller.shaderParameterName, scroller.scrollPosition1,scroller.scrollPosition2,0,0, false);
            end
        end
	end
	
	if self:getIsActiveForInput(false) and self.cameraBend.node ~= nil then
		local curTrans = {getTranslation(self.cameraBend.node)};
		local newTrans = Utils.getMovedLimitedValues(curTrans, self.cameraBend.transMax, self.cameraBend.transMin, 3, self.cameraBend.moveTime, dt, not self.cameraBendActive);
		setTranslation(self.cameraBend.node, unpack(newTrans));
	end;
	
	if self.grainTankFillLevelBackup < self.grainTankFillLevel then
		self.grainTankFillLevelBackup = self.grainTankFillLevel;
		self:changePlane(true);
	end;
	
	if self.pipeIsUnloading then
		self:setVehicleRpmUp(dt, true, 300);
		self.grainTankFillLevelBackup = self.grainTankFillLevel;
		if self.loading then
			self:changePlane(false);
		end;
	elseif not self.isThreshing then
		self:setVehicleRpmUp(dt, false);
	end;
	
	if self.pipeIsUnloading then
		self:setAnimationTime(9, 1);
	else
		self:setAnimationTime(9, 0);
	end;
	
	if self:getIsActiveForSound() then
		if self.isClient and self.unloadingSound ~= nil then
			if self.pipeIsUnloading then
				if not self.unloadingSoundEnabled then
					playSample(self.unloadingSound, 0, self.unloadingSoundVolume, 0);
					self.unloadingSoundEnabled = true;
				end
			else
				if self.unloadingSoundEnabled then
					stopSample(self.unloadingSound);
					self.unloadingSoundEnabled = false;
				end
			end
		end

		local hydraulicSoundEnabled = false;
		
		for _, jointDesc in pairs(self.attacherJoints) do
			if jointDesc.jointType == Vehicle.JOINTTYPE_CUTTER then
				local x, y, z = getRotation(jointDesc.rotationNode);
				local jointRot = tonumber(string.format("%.2f", x));
				local jointMinRot = tonumber(string.format("%.2f", jointDesc.minRot[1]));
				local jointMaxRot = tonumber(string.format("%.2f", jointDesc.maxRot[1]));
				if jointRot > jointMinRot and jointRot < jointMaxRot then
					hydraulicSoundEnabled = true;
				end;
			end;
		end;
		
		if math.abs(self.rotatedTime) > 0.26 and math.abs(self.rotatedTime) < math.abs(self.maxRotTime) then
			hydraulicSoundEnabled = true;
			if self.autoRotateBackSpeed == 0 and math.abs(self.axisSide) == 0 then
				hydraulicSoundEnabled = false;
			end;
		end;
		
		for i=1, 4 do
			if not self.animationParts[i].inputDone then
				hydraulicSoundEnabled = true;
			end;
		end;
		if not self.animationParts[7].inputDone then
			hydraulicSoundEnabled = true;
		end;

		if self.foldAnimTime > 0 and self.foldAnimTime < 1 then
			hydraulicSoundEnabled = true;
		end;
		
		for cutter, _ in pairs(self.attachedCutters) do
			if cutter.foldAnimTime ~= nil then
				if cutter.foldAnimTime > 0 and cutter.foldAnimTime < 1 then
					hydraulicSoundEnabled = true;
				end;
			end
		end

		if hydraulicSoundEnabled then
			if not self.hydraulicSoundEnabled and self.hydraulicSound ~= nil then	
				playSample(self.hydraulicSound, 0, self.hydraulicSoundVolume, 0);
				self.hydraulicSoundEnabled = true;
			end;
		else
			if self.hydraulicSoundEnabled then
				stopSample(self.hydraulicSound);
				self.hydraulicSoundEnabled = false;
			end;
		end;
	end;
	
	if self.isAIThreshing then
		if self.steeringMode ~= RopaEuroTiger_XL.TotalSteer then
			self:setSteeringMode(RopaEuroTiger_XL.TotalSteer);
		end;
		if g_currentMission.environment.needsLights then
			if not self.B3["work"][1].a then
				self:setState("work:1", true);
			end;
		else
			if self.B3["work"][1].a then
				self:setState("work:1", false);
			end;
		end;
	end;
end;

function RopaEuroTiger_XL:updateTick(dt)
	if self.gaspedal ~= nil then		
		local percent = (self.lastSpeed*self.speedDisplayScale*3600) / self.gaspedal.maxSpeed;
		local minRot = self.gaspedal.rotMin;
		local maxRot = self.gaspedal.rotMax;
			
		local x = minRot[1] + (maxRot[1] - minRot[1]) * percent;
		local y = minRot[2] + (maxRot[2] - minRot[2]) * percent;
		local z = minRot[3] + (maxRot[3] - minRot[3]) * percent;
		setRotation(self.gaspedal.node, x, y, z);
	end;
			
	if self.brakepedal ~= nil then
		local rot = {getRotation(self.brakepedal.node)};
		local newRot = Utils.getMovedLimitedValues(rot, self.brakepedal.rotMax, self.brakepedal.rotMin, 3, self.brakepedal.rotTime, dt, not self.brakeLightsVisibility);
		setRotation(self.brakepedal.node, unpack(newRot));
	end;
	
	if self.isThreshing then
		self:setVehicleRpmUp(dt, true, 1000);
		local percent = self.grainTankFillLevel/self.grainTankCapacity*100;
		if percent >= 80 then
			self.screwConveyor.currentSpeed = self.screwConveyor.invertedSpeed;
		else
			self.screwConveyor.currentSpeed = self.screwConveyor.speed;
		end;
		rotate(self.screwConveyor.node, 0, 0, -dt*self.screwConveyor.currentSpeed);
		for _, rollNode in pairs(self.rollNodes) do
			rotate(rollNode.node, -dt*rollNode.speed[1], -dt*rollNode.speed[2], -dt*rollNode.speed[3]);
		end;
		self:setAnimationTime(6, 1);
	elseif not self.pipeIsUnloading then
		self:setVehicleRpmUp(dt, false);
		self:setAnimationTime(6, 0);
	end;
	
	if self.numAttachedCutters > 0 then
		local x, y, z = getRotation(self.attacherJoints[1].rotationNode); -- cutter attacher rotation node
		local attacherRotationPercent = (self.attacherJoints[1].maxRot[1]-x)/(self.attacherJoints[1].maxRot[1]-self.attacherJoints[1].minRot[1]);
		for _, hose in pairs(self.cutterHydraulicHoses) do
			local hoseRotX = hose.rotMax[1] - ((hose.rotMax[1] - hose.rotMin[1]) * attacherRotationPercent);
			local hoseRotY = hose.rotMax[2] - ((hose.rotMax[2] - hose.rotMin[2]) * attacherRotationPercent);
			local hoseRotZ = hose.rotMax[3] - ((hose.rotMax[3] - hose.rotMin[3]) * attacherRotationPercent);
			setRotation(hose.node, hoseRotX, hoseRotY, hoseRotZ);
			
			local hoseScaleX = hose.scaleMax[1] - ((hose.scaleMax[1] - hose.scaleMin[1]) * attacherRotationPercent);
			local hoseScaleY = hose.scaleMax[2] - ((hose.scaleMax[2] - hose.scaleMin[2]) * attacherRotationPercent);
			local hoseScaleZ = hose.scaleMax[3] - ((hose.scaleMax[3] - hose.scaleMin[3]) * attacherRotationPercent);
			setScale(hose.node, hoseScaleX, hoseScaleY, hoseScaleZ)
		end
	end;
end

function RopaEuroTiger_XL:onEnter(isControlling)
	self:setAnimationTime(5, self.animationParts[5].animDuration, true);
end

function RopaEuroTiger_XL:attachImplement(implement)
	if self.numAttachedCutters > 0 then
		for _, cutterAttachedItem in pairs(self.cutterAttachedItems) do
			setVisibility(cutterAttachedItem.node, cutterAttachedItem.visibility);
		end;
	end
end;

function RopaEuroTiger_XL:detachImplement(implementIndex)
	if self.numAttachedCutters == 0 then
		for _, cutterAttachedItem in pairs(self.cutterAttachedItems) do
			setVisibility(cutterAttachedItem.node, not cutterAttachedItem.visibility);
		end
	end
end;

function RopaEuroTiger_XL:changePlane(bool)
	if bool ~= nil then	
		self.loading = bool;

		if bool then
			for _, loadingPlane in pairs(self.loadingPlanes) do
				if loadingPlane.fillType == FruitUtil.fruitTypeToFillType[self.currentGrainTankFruitType] then
					setVisibility(loadingPlane.node, true);
				end;
			end;
			for _, unloadingPlane in pairs(self.unloadingPlanes) do
				setVisibility(unloadingPlane.node, false);
			end;
		else
			for _, loadingPlane in pairs(self.loadingPlanes) do
				setVisibility(loadingPlane.node, false);
			end;
			for _, unloadingPlane in pairs(self.unloadingPlanes) do
				if unloadingPlane.fillType == FruitUtil.fruitTypeToFillType[self.currentGrainTankFruitType] then
					setVisibility(unloadingPlane.node, true);
				end;
			end;
		end;
	else
		self:changePlane(not self.loading);
	end;
end;

function RopaEuroTiger_XL:setVehicleRpmUp(dt, isActive, rpm)
	if self.saveMinRpm ~= 0 and self.motor ~= nil then
		if dt ~= nil then
			if isActive == true then
				self.motor.minRpm = math.max(self.motor.minRpm-(dt/2), -rpm);
			else
				self.motor.minRpm = math.min(self.motor.minRpm+(dt*2), self.saveMinRpm);
			end;
		else
			self.motor.minRpm = self.saveMinRpm;
		end;
		if self.isMotorStarted then
			local fuelUsed = 0.0000011*math.abs(self.motor.minRpm);
			self:setFuelFillLevel(self.fuelFillLevel-fuelUsed);
			g_currentMission.missionStats.fuelUsageTotal = g_currentMission.missionStats.fuelUsageTotal + fuelUsed;
			g_currentMission.missionStats.fuelUsageSession = g_currentMission.missionStats.fuelUsageSession + fuelUsed;
		end;
	end;
end;


function RopaEuroTiger_XL:getIsStartThreshingAllowed(superFunc)
	if not self.isMotorStarted then
		return false;
	end
	if superFunc ~= nil then
		return superFunc(self)
	end
	return true;
end

function RopaEuroTiger_XL:getIsPipeStateChangeAllowed(superFunc, pipeState)
	if not self.isMotorStarted then
		return false;
	end
	if superFunc ~= nil then
		return superFunc(self, pipeState)
	end
	return true
end

function RopaEuroTiger_XL:getIsFoldAllowed(superFunc)
	if not self.isMotorStarted then
		return false;
	end
	if self.animationParts[7].currentPosition ~= 0 then
		return false;
	end;
	if superFunc ~= nil then
		return superFunc(self);
	end
	return true;
end

function RopaEuroTiger_XL:setPipeState(pipeState, noEventSend)
	if self.targetPipeState == 2 then
		self:setAnimationTime(7, 2600, true);
	elseif self.targetPipeState == 1 then
		self:setAnimationTime(7, self.animationParts[7].startPosition, true);
	end;
end;

function RopaEuroTiger_XL:getIsPipeUnloadingAllowed(superFunc)
	if self.animationParts[7].currentPosition >= 2550 then
		return true;
	end
	return false;
end

function RopaEuroTiger_XL:draw()
	if self.camIndex == self.cameraBend.camIndex then
		g_currentMission:addHelpButtonText(g_i18n:getText("RopaEuroTiger_CameraBend"), InputBinding.RopaEuroTiger_CameraBend);
	end;
	for cutter, _ in pairs(self.attachedCutters) do
		if cutter.lastTrailer ~= nil then
			g_currentMission:addHelpButtonText(g_i18n:getText("RopaEuroTiger_AttachToTrailer"), InputBinding.RopaEuroTiger_AttachToTrailer);
			g_currentMission.activeHudIconName = "attach";
		end;
	end
end;

function RopaEuroTiger_XL:onDeactivateSounds()
	if self.hydraulicSoundEnabled then
		stopSample(self.hydraulicSound);
		self.hydraulicSoundEnabled = false;
	end;
	if self.unloadingSoundEnabled then
		stopSample(self.unloadingSound);
		self.unloadingSoundEnabled = false;
	end;
end

function RopaEuroTiger_XL:onDeactivate()
	self:setVehicleRpmUp(nil, false);
	--self.motorSoundRunVolume = 0;
	RopaEuroTiger_XL.onDeactivateSounds(self)
end

function RopaEuroTiger_XL:onLeave()
	self:setAnimationTime(5, self.animationParts[5].startPosition, true);
	if self.deactivateOnLeave then
		RopaEuroTiger_XL.onDeactivate(self);
	else
		RopaEuroTiger_XL.onDeactivateSounds(self)
	end;
end;

--[[
function RopaEuroTiger_XL:toggleWindowState()
	if self.animationParts[9].clipEndTime then
		self:setAnimationTime(9, self.animationParts[9].startPosition);
	else
		self:setAnimationTime(9, self.animationParts[9].animDuration);
	end;
end;

function RopaEuroTiger_XL:toggleDoorState()
	if self.animationParts[10].clipEndTime then
		self:setAnimationTime(10, self.animationParts[10].startPosition);
	else
		self:setAnimationTime(10, self.animationParts[10].animDuration);
	end;
end;
]]

function RopaEuroTiger_XL:handleAttachAttachableEvent()
    if g_currentMission.attachableInMountRange ~= nil then
		if g_currentMission.attachableInMountRangeVehicle == self then
			if self.attacherJoints[g_currentMission.attachableInMountRangeIndex].jointIndex == 0 then
				local startLowered = nil;
				-- prevent startLowered when attach from trailer
				if g_currentMission.attachableInMountRange.isAttachedToTrailer then
					startLowered = false;
				end;
				self:attachImplement(g_currentMission.attachableInMountRange, g_currentMission.attachableInMountRangeIndex, nil, nil, startLowered);
				return true;
			end;
		else
			for _,implement in ipairs(self.attachedImplements) do
				if implement.object ~= nil and implement.object:handleAttachAttachableEvent() then
					return true;
				end;
			end;
		end;
	end;
	return false;
end;

function RopaEuroTiger_XL:setSteeringMode(steeringMode, noEventSend)
	SetSteeringModeEvent.sendEvent(self, steeringMode, noEventSend)
	self.steeringMode = steeringMode;
	
	if steeringMode == RopaEuroTiger_XL.SynchronizedSteering then
		self:setAnimationTime(1, 1500, true); -- wheels offset
		self:setAnimationTime(2, self.animationParts[2].animDuration, true); -- back wheels rot
		self:setAnimationTime(3, self.animationParts[3].startPosition, true); -- axis rot
		self:setAnimationTime(4, self.animationParts[4].startPosition, true); -- front wheels rot
		--self:setAnimationTime(6, self.animationParts[6].startPosition, true); -- markers
	elseif steeringMode == RopaEuroTiger_XL.OffsetSteerLeft then
		self:setAnimationTime(1, 0, true); -- wheels offset
		self:setAnimationTime(2, self.animationParts[2].animDuration, true); -- back wheels rot
		self:setAnimationTime(3, 1100, true); -- axis rot
		self:setAnimationTime(4, self.animationParts[4].animDuration, true); -- front wheels rot
		--self:setAnimationTime(6, self.animationParts[6].animDuration, true); -- markers
	elseif steeringMode == RopaEuroTiger_XL.OffsetSteerRight then
		self:setAnimationTime(1, self.animationParts[1].animDuration, true); -- wheels offset
		self:setAnimationTime(2, self.animationParts[2].animDuration, true); -- back wheels rot
		self:setAnimationTime(3, 1100, true); -- axis rot
		self:setAnimationTime(4, self.animationParts[4].animDuration, true); -- front wheels rot
		--self:setAnimationTime(6, self.animationParts[6].animDuration, true); -- markers
	elseif steeringMode == RopaEuroTiger_XL.TotalSteer then
		self:setAnimationTime(1, 1500, true); -- wheels offset
		self:setAnimationTime(2, self.animationParts[2].startPosition, true); -- back wheels rot
		self:setAnimationTime(3, self.animationParts[3].startPosition, true); -- axis rot
		self:setAnimationTime(4, self.animationParts[4].startPosition, true);  -- front wheels rot
		--self:setAnimationTime(6, self.animationParts[6].startPosition, true); -- markers
	elseif steeringMode == RopaEuroTiger_XL.AllWheelSteer then
		self:setAnimationTime(1, 1500, true); -- wheels offset
		self:setAnimationTime(2, self.animationParts[2].startPosition, true); -- back wheels rot
		self:setAnimationTime(3, self.animationParts[3].animDuration, true); -- axis rot
		self:setAnimationTime(4, self.animationParts[4].startPosition, true); -- front wheels rot
		--self:setAnimationTime(6, self.animationParts[6].startPosition, true); -- markers
	end
end;

-- events
SetSteeringModeEvent = {};
SetSteeringModeEvent_mt = Class(SetSteeringModeEvent, Event);

InitEventClass(SetSteeringModeEvent, "SetSteeringModeEvent");

function SetSteeringModeEvent:emptyNew()
    local self = Event:new(SetSteeringModeEvent_mt);
    self.className="SetSteeringModeEvent";
    return self;
end;

function SetSteeringModeEvent:new(vehicle, activeSteeringMode)
    local self = SetSteeringModeEvent:emptyNew()
    self.vehicle = vehicle;
	self.activeSteeringMode = activeSteeringMode;
    return self;
end;

function SetSteeringModeEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
	self.activeSteeringMode = streamReadInt8(streamId);
    self.vehicle = networkGetObject(id);
    self:run(connection);
end;

function SetSteeringModeEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));
	streamWriteInt8(streamId, self.activeSteeringMode);
end;

function SetSteeringModeEvent:run(connection)   
	self.vehicle:setSteeringMode(self.activeSteeringMode, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(SetSteeringModeEvent:new(self.vehicle, self.activeSteeringMode), nil, connection, self.vehicle);
    end;
end;

function SetSteeringModeEvent.sendEvent(vehicle, activeSteeringMode, noEventSend)
	if noEventSend == nil or noEventSend == false then
		if g_server ~= nil then
			g_server:broadcastEvent(SetSteeringModeEvent:new(vehicle, activeSteeringMode), nil, nil, vehicle);
		else
			g_client:getServerConnection():sendEvent(SetSteeringModeEvent:new(vehicle, activeSteeringMode));
		end;
	end;
end;