--[[
Script to animate some indoor parts

Author:		Ifko[nator]
Date:		17.03.2023
Version:	2.0

History:	V1.0 @19.12.2020 - initial implementation in FS 19
			--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
			v1.1 @26.12.2020 - fix for error: 
								Error: Running LUA method 'update'.
								C:/Users/USER_NAME/Documents/My Games/FarmingSimulator2019/mods/FS19_johnDeerePack/specializations/AnimatedIndoorParts.lua:2892: attempt to index local 'specFillUnit' (a nil value)
			--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
			v1.2 @27.12.2020 - fix for error: 
								Error: Running LUA method 'update'.
								C:/Users/USER_NAME/Documents/My Games/FarmingSimulator2019/mods/FS19_johnDeerePack/specializations/AnimatedIndoorParts.lua:2942: attempt to index local 'specFillUnit' (a nil value)
			--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
			v1.3 @29.12.2020 - fix for error: 
								Error: Running LUA method 'update'.
								C:/Users/USER_NAME/Documents/My Games/FarmingSimulator2019/mods/FS19_johnDeerePack/specializations/AnimatedIndoorParts.lua:1274: attempt to compare number with nil
			--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
			v1.4 @31.12.2020 -  following drive symbols are now also working for tool's with attacher joint type "trailerLow"
									TOOL_BACK_LOWERED
									TOOL_BACK_LIFTED
									TOOL_BACK_IS_UNFOLDED
									TOOL_BACK_IS_FOLDED
			--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
			v1.5 @04.01.2021 - added support for the tire pressure script from wopster
			--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
			v1.6 @17.02.2021 - added support for combine fill levels
			--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
			v1.7 @28.02.2021 - added support for new frontloader joint types
			--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
			v1.8 @09.06.2021 - hot fix for switching screens
			--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
			v2.0 @17.03.2023 - convert to FS 22
]]

AnimatedIndoorParts = {};
AnimatedIndoorParts.currentModName = g_currentModName;
AnimatedIndoorParts.baseKey = "vehicle.animatedIndoorParts";
AnimatedIndoorParts.baseDashboardKey = AnimatedIndoorParts.baseKey .. ".dashboards";
AnimatedIndoorParts.currentModDirectory = g_currentModDirectory;

function AnimatedIndoorParts.initSpecialization()
	Vehicle.xmlSchemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?)." .. AnimatedIndoorParts.currentModName .. ".animatedIndoorParts#currentScreenNumber", "Current selected screen.", 0);
	
	for _, supportedDashboardFuncion in pairs(AnimatedIndoorPartsUtil.supportetDashboardFunctions) do
		Dashboard.registerDashboardXMLPaths(Vehicle.xmlSchema, AnimatedIndoorParts.baseDashboardKey, supportedDashboardFuncion);
	end;

	Dashboard.registerDashboardWarningXMLPaths(Vehicle.xmlSchema, AnimatedIndoorParts.baseDashboardKey);
end;

function AnimatedIndoorParts.prerequisitesPresent(specializations)
	return SpecializationUtil.hasSpecialization(Drivable, specializations);
end;

function AnimatedIndoorParts.registerEventListeners(vehicleType)
	local functionNames = {
		"onLoad",
		"onUpdate",
		"onRegisterActionEvents"
	};
	
	for _, functionName in ipairs(functionNames) do
		SpecializationUtil.registerEventListener(vehicleType, functionName, AnimatedIndoorParts);
	end;
end;

function AnimatedIndoorParts.registerFunctions(vehicleType)
	local newFunctions = {
		"getGpsWorkingWidth",
		"getGpsCurrentLane",
		"getCurrentBaleCount",
		"getTotalBaleCount",
		"getCurrentWrappedBaleCount",
		"getTotalWrappedBaleCount",
		"getFuelUsage",
		"getMotorTemperature",
		"getPsCurrentLane",
		"getPsWorkingWidth",
		"getPsTramlineDistance",
		"getPsMaxLanes",
		"getCurrentFieldNumber",
		"getFillLevelPercentTotal",
		"getFillLevelTotal",
		"getFillLevelCombine",
		"getFillLevelPercentCombine",
		"getFillLevelPercentTrailer1",
		"getFillLevelTrailer1",
		"getFillLevelPercentTrailer2",
		"getFillLevelTrailer2",
		"getFillLevelPercentSowingMachine",
		"getSeedFillLevel",
		"getFertilizerFillLevel",
		"getFillLevelFillType",
		"getFillLevelPercentFillType",
		"getFillWeightTrailer1",
		"getFillWeightTrailer2",
		"getFillWeightCombine",
		"getCurrentFilledTrailerFillLevelPercent",
		"getCurrentFilledTrailerFillLevel",
		"getCurrentPressure",
		"getTargetPressure",
		"getCruiseControlSpeedFix",

		"setCurrentScreen"
	};
	
	for _, newFunction in ipairs(newFunctions) do
		SpecializationUtil.registerFunction(vehicleType, newFunction, AnimatedIndoorParts[newFunction]);
	end;
end;

function AnimatedIndoorParts:onLoad(savegame)
	if AnimatedIndoorPartsUtil == nil then
		print("Missing the AnimatedIndoorPartsUtil.lua! The AnimatedIndoorParts.lua will be NOT loaded!");

		return;
	end;

	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");

	specAnimatedIndoorParts.xmlFile = self.xmlFile.handle;

	AnimatedIndoorParts.debugPriority = AnimatedIndoorPartsUtil.getDebugPriority(specAnimatedIndoorParts.xmlFile, AnimatedIndoorParts.baseKey .. "#debugPriority");
	
	local specMotorized = AnimatedIndoorPartsUtil.getSpecByName(self, "motorized");

	specAnimatedIndoorParts.driveSymbols = {};

	local driveSymbolNumber = 0;

	AnimatedIndoorPartsUtil.printDebug("", AnimatedIndoorParts.debugPriority, false, "");
	AnimatedIndoorPartsUtil.printDebug("-----------------------------------------------Debug from the AnimatedIndoorParts.lua Start------------------------------------------------", AnimatedIndoorParts.debugPriority, false, "");

	while true do
		local driveSymbolKey = AnimatedIndoorParts.baseKey .. ".driveSymbols.driveSymbol(" .. tostring(driveSymbolNumber) .. ")";
		
		if not self.xmlFile:hasProperty(driveSymbolKey) then
			break;
		end;
		
		local driveSymbol = {};
		
		driveSymbol.node = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#node"), self.i3dMappings);
		
		if driveSymbol.node ~= nil then	
			driveSymbol.name = string.upper(Utils.getNoNil(getXMLString(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#name"), AnimatedIndoorPartsUtil.supportetDriveSymbolNames[1]));

			if AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.supportetDriveSymbolNames, driveSymbol.name) then
				setVisibility(driveSymbol.node, false);

				driveSymbol.emitColor = self:getDashboardColor(self.xmlFile, Utils.getNoNil(getXMLString(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#emitColor"), "GREEN"));
				driveSymbol.baseColor = self:getDashboardColor(self.xmlFile, Utils.getNoNil(getXMLString(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#baseColor"), "BLACK"));
				driveSymbol.intensity = Utils.getNoNil(getXMLFloat(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#intensity"), 1);
				driveSymbol.screenNumber = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#screenNumber"), 0);
				driveSymbol.disableOnMotorOff = Utils.getNoNil(getXMLBool(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#disableOnMotorOff"), true);
				driveSymbol.changeVisibility = Utils.getNoNil(getXMLBool(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#changeVisibility"), false);
				
				if driveSymbol.name ~= "DISABLE_ON_MOTOR_OFF" then
					if driveSymbol.emitColor == nil then
						AnimatedIndoorPartsUtil.printError("Failed to load emit color '" .. getXMLString(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#emitColor") .. "' for node " .. getXMLString(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#node") .. "! (drive symbol number '" .. driveSymbolNumber + 1 .. "')! Use default emit color 'GREEN' instead!", false, false, "AnimatedIndoorParts");
					
						driveSymbol.emitColor = self:getDashboardColor(self.xmlFile, "GREEN");
					end;
				
					if driveSymbol.baseColor == nil then
						AnimatedIndoorPartsUtil.printError("Failed to load base color '" .. getXMLString(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#baseColor") .. "' for node " .. getXMLString(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#node") .. "! (drive symbol number '" .. driveSymbolNumber + 1 .. "')! Use default base color 'BLACK' instead!", false, false, "AnimatedIndoorParts");
					
						driveSymbol.baseColor = self:getDashboardColor(self.xmlFile, "BLACK");
					end;
				end;
				
				if driveSymbol.name == "CONTROL_LAMP" then
					driveSymbol.displayTime = Utils.getNoNil(getXMLFloat(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#displayTime"), 2200);
					driveSymbol.currentTime = 1;
				elseif driveSymbol.name ~= "DISABLE_ON_MOTOR_OFF"
					and driveSymbol.name ~= "GPS_LANE_PLUS"
					and driveSymbol.name ~= "GPS_LANE_MINUS"
				then
					setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
					setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.emitColor[1], driveSymbol.emitColor[2], driveSymbol.emitColor[3], driveSymbol.emitColor[4], false);
					setShaderParameter(driveSymbol.node, "baseColor", driveSymbol.baseColor[1], driveSymbol.baseColor[2], driveSymbol.baseColor[3], driveSymbol.baseColor[4], false);
				end;


				table.insert(specAnimatedIndoorParts.driveSymbols, driveSymbol);

				AnimatedIndoorPartsUtil.printDebug("Load drive symbol '" .. driveSymbol.name .. "' (number '" .. driveSymbolNumber + 1 .. "', node '" .. getXMLString(specAnimatedIndoorParts.xmlFile, driveSymbolKey .. "#node") .. "') successfully.", AnimatedIndoorParts.debugPriority, true, "AnimatedIndoorParts");
			else
				AnimatedIndoorPartsUtil.printError("Found unsupportet drive symbol name '" .. driveSymbol.name .. "' (drive symbol number '" .. driveSymbolNumber + 1 .. "')! Supportet names: '" .. AnimatedIndoorPartsUtil.getSupportetValues(AnimatedIndoorPartsUtil.supportetDriveSymbolNames) .. "'. Skipping this entry!", false, false, "AnimatedIndoorParts");
			end;
		else
			AnimatedIndoorPartsUtil.printError("Invalid node for drive symbol number '" .. driveSymbolNumber + 1 .. "'! Skipping this entry!", false, false, "AnimatedIndoorParts");
		end;
		
		driveSymbolNumber = driveSymbolNumber + 1;
	end;

	specAnimatedIndoorParts.fillLevelBars = {};

	local fillLevelBarNumber = 0;
	
	while true do
		local fillLevelBarKey = AnimatedIndoorParts.baseKey .. ".fillLevelBar(" .. tostring(fillLevelBarNumber) .. ")";

		if not self.xmlFile:hasProperty(fillLevelBarKey) then
			break;
		end;

		local fillLevelBar = {};

		local node = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, fillLevelBarKey .. "#node"), self.i3dMappings);

		if node ~= nil then
			fillLevelBar.node = node;
			fillLevelBar.scaleDirection = string.upper(Utils.getNoNil(getXMLString(specAnimatedIndoorParts.xmlFile, fillLevelBarKey .. "#scaleDirection"), "X"));
			fillLevelBar.isTrailer1 = Utils.getNoNil(getXMLBool(specAnimatedIndoorParts.xmlFile, fillLevelBarKey .. "#isTrailer1"), false);
			fillLevelBar.isTrailer2 = Utils.getNoNil(getXMLBool(specAnimatedIndoorParts.xmlFile, fillLevelBarKey .. "#isTrailer2"), false);
			fillLevelBar.isCurrentFilledTrailer = Utils.getNoNil(getXMLBool(specAnimatedIndoorParts.xmlFile, fillLevelBarKey .. "#isCurrentFilledTrailer"), false);
			fillLevelBar.isSowingMachine = Utils.getNoNil(getXMLBool(specAnimatedIndoorParts.xmlFile, fillLevelBarKey .. "#isSowingMachine"), false);
			fillLevelBar.isSeedFillType = Utils.getNoNil(getXMLBool(specAnimatedIndoorParts.xmlFile, fillLevelBarKey .. "#isSeedFillType"), false);
			fillLevelBar.isFertilizerFillType = Utils.getNoNil(getXMLBool(specAnimatedIndoorParts.xmlFile, fillLevelBarKey .. "#isFertilizerFillType"), false);
			fillLevelBar.isCombine = Utils.getNoNil(getXMLBool(specAnimatedIndoorParts.xmlFile, fillLevelBarKey .. "#isCombine"), false);
			fillLevelBar.screenNumber = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, fillLevelBarKey .. "#screenNumber"), 0);

			if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.supportetScaleDirections, fillLevelBar.scaleDirection) then 
				AnimatedIndoorPartsUtil.printError("Invalid scale direction '" .. fillLevelBar.scaleDirection .. "' for the fill level bar! Supportet scale directions: '" .. AnimatedIndoorPartsUtil.getSupportetValues(AnimatedIndoorPartsUtil.supportetScaleDirections) .. "'. Using scale direction 'X' instead!", true, false, "AnimatedIndoorParts");

				fillLevelBar.scaleDirection = "X";
			end;

			setScale(fillLevelBar.node, 0, 0, 0);

			table.insert(specAnimatedIndoorParts.fillLevelBars, fillLevelBar);
		end;

		fillLevelBarNumber = fillLevelBarNumber + 1;
	end;

	specAnimatedIndoorParts.screens = {};
	
	local screenNumber = 0;

	while true do
		local screenKey = AnimatedIndoorParts.baseKey .. ".screens.screen(" .. tostring(screenNumber) .. ")";

		if not self.xmlFile:hasProperty(screenKey) then
			break;
		end;

		local screen = {};

		local node = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, screenKey .. "#node"), self.i3dMappings);

		if node ~= nil then
			if AnimatedIndoorPartsUtil.getAllowInsertScreen(self, specAnimatedIndoorParts, screenKey) then
				screen.node = node;

				setVisibility(screen.node, false);

				table.insert(specAnimatedIndoorParts.screens, screen);
			end;
		end;

		screenNumber = screenNumber + 1
	end;

	if #specAnimatedIndoorParts.screens > 0 then
		local currentScreenNumber = 1;
		
		if savegame ~= nil then
			currentScreenNumber = savegame.xmlFile:getValue(savegame.key .. "." .. AnimatedIndoorParts.currentModName .. ".animatedIndoorParts#currentScreenNumber", currentScreenNumber);
		end;

		if currentScreenNumber > #specAnimatedIndoorParts.screens then
			currentScreenNumber = 1;
		end;

		self:setCurrentScreen(currentScreenNumber);
	end;

	specAnimatedIndoorParts.startScreens = {};
	
	local startScreenNumber = 0;

	while true do
		local startScreenKey = AnimatedIndoorParts.baseKey .. ".startScreens.startScreen(" .. tostring(startScreenNumber) .. ")";

		if not self.xmlFile:hasProperty(startScreenKey) then
			break;
		end;

		local startScreen = {};

		local node = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, startScreenKey .. "#node"), self.i3dMappings);

		if node ~= nil then
			if AnimatedIndoorPartsUtil.getAllowInsertScreen(self, specAnimatedIndoorParts, startScreenKey) then
				startScreen.node = node;
				startScreen.startTime = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, startScreenKey .. "#startTime"), 0) * 1000; --## in seconds!
				startScreen.endTime = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, startScreenKey .. "#endTime"), 20) * 1000; --## in seconds!
				startScreen.currentTime = 0;

				local loadingBar = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, startScreenKey .. "#loadingBar"), self.i3dMappings);

				if loadingBar ~= nil then
					startScreen.loadingBar = loadingBar;
					startScreen.scaleDirection = string.upper(Utils.getNoNil(getXMLString(specAnimatedIndoorParts.xmlFile, startScreenKey .. "#scaleDirection"), "X"));

					if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.supportetScaleDirections, startScreen.scaleDirection) then 
						AnimatedIndoorPartsUtil.printError("Invalid scale direction '" .. startScreen.scaleDirection .. "' for the loading bar! Supportet scale directions: '" .. AnimatedIndoorPartsUtil.getSupportetValues(AnimatedIndoorPartsUtil.supportetScaleDirections) .. "'. Using scale direction 'X' instead!", true, false, "AnimatedIndoorParts");

						startScreen.scaleDirection = "X";
					end;

					setScale(startScreen.loadingBar, 0, 0, 0);
				end;

				setVisibility(startScreen.node, false);

				table.insert(specAnimatedIndoorParts.startScreens, startScreen);
			end;
		end;

		startScreenNumber = startScreenNumber + 1
	end;

	for _, radioChannel in pairs(AnimatedIndoorPartsUtil.radioChannels) do
		specAnimatedIndoorParts[radioChannel] = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, AnimatedIndoorParts.baseKey .. ".radio.channels." .. radioChannel .. "#node"), self.i3dMappings);

		if specAnimatedIndoorParts[radioChannel] ~= nil then
			setVisibility(specAnimatedIndoorParts[radioChannel], radioChannel == "off");
		end;
	end;

	specAnimatedIndoorParts.disableNodesOnStart = {};
	
	local disableNodeOnStartNumber = 0;

	while true do
		local disableNodeOnStartKey = AnimatedIndoorParts.baseKey .. ".disableNodesOnStart.disableNodeOnStart(" .. tostring(disableNodeOnStartNumber) .. ")";

		if not self.xmlFile:hasProperty(disableNodeOnStartKey) then
			break;
		end;

		local disableNodeOnStart = {};

		local node = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, disableNodeOnStartKey .. "#node"), self.i3dMappings);

		if node ~= nil then
			disableNodeOnStart.node = node;
			
			setVisibility(disableNodeOnStart.node, false);

			table.insert(specAnimatedIndoorParts.disableNodesOnStart, disableNodeOnStart);
		end;

		disableNodeOnStartNumber = disableNodeOnStartNumber + 1
	end;

	specAnimatedIndoorParts.fillTypeIcons = {};

	local fillTypeIconNumber = 0;

	while true do
		local fillTypeIconKey = AnimatedIndoorParts.baseKey .. ".fillTypeIcon(" .. tostring(fillTypeIconNumber) .. ")";

		if not self.xmlFile:hasProperty(fillTypeIconKey) then
			break;
		end;

		local fillTypeIcon = {};

		local node = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, fillTypeIconKey .. "#node"), self.i3dMappings);

		if node ~= nil then
			fillTypeIcon.node = node;
			fillTypeIcon.trailerNumber = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, fillTypeIconKey .. "#trailerNumber"), 1);
			fillTypeIcon.fillUnitIndex = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, fillTypeIconKey .. "#fillUnitIndex"), 1);
			fillTypeIcon.currentFillType = FillType.UNKNOWN;
			fillTypeIcon.screenNumber = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, fillTypeIconKey .. "#screenNumber"), 0);

			setVisibility(fillTypeIcon.node, false);

			table.insert(specAnimatedIndoorParts.fillTypeIcons, fillTypeIcon);
		end;

		fillTypeIconNumber = fillTypeIconNumber + 1;
	end;

	specAnimatedIndoorParts.seedFruitTypeIcons = {};

	local seedFruitTypeIconNumber = 0;

	while true do
		local seedFruitTypeIconKey = AnimatedIndoorParts.baseKey .. ".seedFruitTypeIcon(" .. tostring(seedFruitTypeIconNumber) .. ")";

		if not self.xmlFile:hasProperty(seedFruitTypeIconKey) then
			break;
		end;

		local seedFruitTypeIcon = {};

		local node = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, seedFruitTypeIconKey .. "#node"), self.i3dMappings);

		if node ~= nil then
			seedFruitTypeIcon.node = node;
			seedFruitTypeIcon.screenNumber = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, seedFruitTypeIconKey .. "#screenNumber"), 0);
			
			setVisibility(seedFruitTypeIcon.node, false);

			table.insert(specAnimatedIndoorParts.seedFruitTypeIcons, seedFruitTypeIcon);
		end;

		seedFruitTypeIconNumber = seedFruitTypeIconNumber + 1;
	end;

	specAnimatedIndoorParts.harvestInputFruitTypeIcons = {};

	local harvestInputFruitTypeIconNumber = 0;

	while true do
		local harvestInputFruitTypeIconKey = AnimatedIndoorParts.baseKey .. ".harvestInputFruitTypeIcon(" .. tostring(harvestInputFruitTypeIconNumber) .. ")";

		if not self.xmlFile:hasProperty(harvestInputFruitTypeIconKey) then
			break;
		end;

		local harvestInputFruitTypeIcon = {};

		local node = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, harvestInputFruitTypeIconKey .. "#node"), self.i3dMappings);

		if node ~= nil then
			harvestInputFruitTypeIcon.node = node;
			harvestInputFruitTypeIcon.screenNumber = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, harvestInputFruitTypeIconKey .. "#screenNumber"), 0);
			
			setVisibility(harvestInputFruitTypeIcon.node, false);

			table.insert(specAnimatedIndoorParts.harvestInputFruitTypeIcons, harvestInputFruitTypeIcon);
		end;

		harvestInputFruitTypeIconNumber = harvestInputFruitTypeIconNumber + 1;
	end;

	specAnimatedIndoorParts.harvestOutputFruitTypeIcons = {};

	local harvestOutputFruitTypeIconNumber = 0;

	while true do
		local harvestOutputFruitTypeIconKey = AnimatedIndoorParts.baseKey .. ".harvestOutputFruitTypeIcon(" .. tostring(harvestOutputFruitTypeIconNumber) .. ")";

		if not self.xmlFile:hasProperty(harvestOutputFruitTypeIconKey) then
			break;
		end;

		local harvestOutputFruitTypeIcon = {};

		local node = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, harvestOutputFruitTypeIconKey .. "#node"), self.i3dMappings);

		if node ~= nil then
			harvestOutputFruitTypeIcon.node = node;
			harvestOutputFruitTypeIcon.screenNumber = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, harvestOutputFruitTypeIconKey .. "#screenNumber"), 0);
			
			setVisibility(harvestOutputFruitTypeIcon.node, false);

			table.insert(specAnimatedIndoorParts.harvestOutputFruitTypeIcons, harvestOutputFruitTypeIcon);
		end;

		harvestOutputFruitTypeIconNumber = harvestOutputFruitTypeIconNumber + 1;
	end;

	specAnimatedIndoorParts.fillTypeTexts = {};

	local fillTypeTextNumber = 0;

	while true do
		local fillTypeTextKey = AnimatedIndoorParts.baseKey .. ".fillTypeText(" .. tostring(fillTypeTextNumber) .. ")";

		if not self.xmlFile:hasProperty(fillTypeTextKey) then
			break;
		end;

		local fillTypeText = {};

		local node = I3DUtil.indexToObject(self.components, getXMLString(specAnimatedIndoorParts.xmlFile, fillTypeTextKey .. "#node"), self.i3dMappings);

		if node ~= nil then
			fillTypeText.node = node;
			fillTypeText.fillType = Utils.getNoNil(getXMLString(specAnimatedIndoorParts.xmlFile, fillTypeTextKey .. "#fillType"), "unknown");
			fillTypeText.showIfEmpty = Utils.getNoNil(getXMLBool(specAnimatedIndoorParts.xmlFile, fillTypeTextKey .. "#showIfEmpty"), false);
			fillTypeText.screenNumber = Utils.getNoNil(getXMLInt(specAnimatedIndoorParts.xmlFile, fillTypeTextKey .. "#screenNumber"), 0);

			if fillTypeText.fillType ~= "unknown" or fillTypeText.showIfEmpty then
				if g_fillTypeManager:getFillTypeByName(fillTypeText.fillType) ~= nil then
					setVisibility(fillTypeText.node, false);

					table.insert(specAnimatedIndoorParts.fillTypeTexts, fillTypeText);
				else
					AnimatedIndoorPartsUtil.printError("Invalid fill type ('" .. fillTypeText.fillType .. "') for fill type text node " .. getXMLString(specAnimatedIndoorParts.xmlFile, fillTypeTextKey .. "#node") .. "! (fill type text number '" .. fillTypeTextNumber + 1 .. "')! This entry will be skipped!", false, false, "AnimatedIndoorParts");
				end;
			end;
		end;

		fillTypeTextNumber = fillTypeTextNumber + 1;
	end;

	specAnimatedIndoorParts.animations = {};

	local animationNumber = 0;

	while true do
		local animationKey = AnimatedIndoorParts.baseKey .. ".animations.animation(" .. tostring(animationNumber) .. ")";

		if not self.xmlFile:hasProperty(animationKey) then
			break;
		end;

		local animation = {};

		local lowerAnimationName = Utils.getNoNil(getXMLString(specAnimatedIndoorParts.xmlFile, animationKey .. "#lowerAnimationName"), "unknown");
		local liftAnimationName = Utils.getNoNil(getXMLString(specAnimatedIndoorParts.xmlFile, animationKey .. "#liftAnimationName"), "unknown");
		
		if lowerAnimationName ~= "unknown" and liftAnimationName ~= "unknown" then
			animation.lowerAnimationName = lowerAnimationName;
			animation.liftAnimationName = liftAnimationName;
			animation.name = string.upper(Utils.getNoNil(getXMLString(specAnimatedIndoorParts.xmlFile, animationKey .. "#name"), AnimatedIndoorPartsUtil.supportedAnimationNames[1]));

			if AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.supportedAnimationNames, animation.name) then
				table.insert(specAnimatedIndoorParts.animations, animation);
			else
				AnimatedIndoorPartsUtil.printError("Found unsupportet animation name '" .. animation.name .. "' (animation number '" .. animationNumber + 1 .. "')! Supportet names: '" .. AnimatedIndoorPartsUtil.getSupportetValues(AnimatedIndoorPartsUtil.supportedAnimationNames) .. "'. Skipping this entry!", false, false, "AnimatedIndoorParts");
			end;
		else
			AnimatedIndoorPartsUtil.printError("Missing 'lowerAnimationName' or 'liftAnimationName' for node " .. getXMLString(specAnimatedIndoorParts.xmlFile, animationKey .. "#node") .. "! (animation number '" .. animationNumber + 1 .. "')! This entry will be skipped!", false, false, "AnimatedIndoorParts");
		end;

		animationNumber = animationNumber + 1;
	end;

	AnimatedIndoorPartsUtil.printDebug("", AnimatedIndoorParts.debugPriority, false, "");
	AnimatedIndoorPartsUtil.printDebug("-----------------------------------------------Debug from the AnimatedIndoorParts.lua End------------------------------------------------", AnimatedIndoorParts.debugPriority, false, "");

	specAnimatedIndoorParts.gpsModName = AnimatedIndoorPartsUtil.getModNameBySpecialization("GlobalPositioningSystem");
	specAnimatedIndoorParts.lsaModName = AnimatedIndoorPartsUtil.getModNameByTitle("Lenkachse sperren", "Lock steering axle");
	specAnimatedIndoorParts.aufbwModName = AnimatedIndoorPartsUtil.getModNameByTitle("Automatisches Abladen für Ballenwickler", "Automatic unload for bale wrappers");
	specAnimatedIndoorParts.bsaModName = AnimatedIndoorPartsUtil.getModNameBySpecialization("BuyableSteeringAxle");
	specAnimatedIndoorParts.psModName = AnimatedIndoorPartsUtil.getModNameBySpecialization("ProSeedTramLines");
	specAnimatedIndoorParts.laModName = AnimatedIndoorPartsUtil.getModNameBySpecialization("LiftAxle");
	
	specAnimatedIndoorParts.isLowFillLevel = false;
	specAnimatedIndoorParts.cardinalDirection = 0;
	specAnimatedIndoorParts.currentFieldNumber = 0;
	specAnimatedIndoorParts.currentSeedFillLevelPercent = 0;
	specAnimatedIndoorParts.currentFertilizerFillLevelPercent = 0;
	specAnimatedIndoorParts.currentFertilizerFillLevel = 0;
	specAnimatedIndoorParts.text = "";
	
	if self.loadDashboardsFromXML ~= nil then
		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "textRendering",
				valueObject = specAnimatedIndoorParts,
				valueFunc = "text"
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "cruiseControlSpeed",
				valueObject = self,
				valueFunc = "getCruiseControlSpeedFix",
				minFunc = 0,
				maxFunc = 80
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fillLevelPercentTotal",
				valueObject = self,
				valueFunc = "getFillLevelPercentTotal",
				minFunc = 0,
				maxFunc = 100
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fillLevelTotal",
				valueObject = self,
				valueFunc = "getFillLevelTotal",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fillLevelPercentTrailer1",
				valueObject = self,
				valueFunc = "getFillLevelPercentTrailer1",
				minFunc = 0,
				maxFunc = 100
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fillLevelTrailer1",
				valueObject = self,
				valueFunc = "getFillLevelTrailer1",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fillLevelPercentTrailer2",
				valueObject = self,
				valueFunc = "getFillLevelPercentTrailer2",
				minFunc = 0,
				maxFunc = 100
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fillLevelTrailer2",
				valueObject = self,
				valueFunc = "getFillLevelTrailer2",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "currentFilledTrailerFillLevelPercent",
				valueObject = self,
				valueFunc = "getCurrentFilledTrailerFillLevelPercent",
				minFunc = 0,
				maxFunc = 100
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "currentFilledTrailerFillLevel",
				valueObject = self,
				valueFunc = "getCurrentFilledTrailerFillLevel",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,

			{
				valueTypeToLoad = "fillLevelPercentSowingMachine",
				valueObject = self,
				valueFunc = "getFillLevelPercentSowingMachine",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "lowFuelFillLevel",
				valueObject = specAnimatedIndoorParts,
				valueFunc = "isLowFillLevel"
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,

			{
				valueTypeToLoad = "currentBaleCount",
				valueObject = self,
				valueFunc = "getCurrentBaleCount",
				minFunc = 0,
				maxFunc = 99999
			}
		);
		
		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,

			{
				valueTypeToLoad = "totalBaleCount",
				valueObject = self,
				valueFunc = "getTotalBaleCount",
				minFunc = 0,
				maxFunc = 99999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,

			{
				valueTypeToLoad = "currentWrappedBaleCount",
				valueObject = self,
				valueFunc = "getCurrentWrappedBaleCount",
				minFunc = 0,
				maxFunc = 99999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,

			{
				valueTypeToLoad = "totalWrappedBaleCount",
				valueObject = self,
				valueFunc = "getTotalWrappedBaleCount",
				minFunc = 0,
				maxFunc = 99999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "currentAngle",
				valueObject = specAnimatedIndoorParts,
				valueFunc = "cardinalDirection",
				minFunc = 0,
				maxFunc = 360
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "seedFillLevelPercent",
				valueObject = specAnimatedIndoorParts,
				valueFunc = "currentSeedFillLevelPercent",
				minFunc = 0,
				maxFunc = 100
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "seedFillLevel",
				valueObject = self,
				valueFunc = "getSeedFillLevel",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fertilizerFillLevelPercent",
				valueObject = specAnimatedIndoorParts,
				valueFunc = "currentFertilizerFillLevelPercent",
				minFunc = 0,
				maxFunc = 100
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fertilizerFillLevel",
				valueObject = self,
				valueFunc = "getFertilizerFillLevel",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fillWeightTrailer1",
				valueObject = self,
				valueFunc = "getFillWeightTrailer1",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey, 
			
			{
				valueTypeToLoad = "fillWeightTrailer2",
				valueObject = self,
				valueFunc = "getFillWeightTrailer2",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fillLevelCombine",
				valueObject = self,
				valueFunc = "getFillLevelCombine",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "fillLevelPercentCombine",
				valueObject = self,
				valueFunc = "getFillLevelPercentCombine",
				minFunc = 0,
				maxFunc = 100
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey, 
			
			{
				valueTypeToLoad = "fillWeightCombine",
				valueObject = self,
				valueFunc = "getFillWeightCombine",
				minFunc = 0,
				maxFunc = 999999
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "currentEnviromentTemperature",
				valueObject = specAnimatedIndoorParts,
				valueFunc = "getCurrentEnviromentTemperature"
			}
		);

		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,
			
			{
				valueTypeToLoad = "currentBaleSize",
				valueObject = specAnimatedIndoorParts,
				valueFunc = "getCurrentBaleSize"
			}
		);

		if specAnimatedIndoorParts.gpsModName ~= "" then
			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey, 

				{
					valueTypeToLoad = "gpsWorkingWidth",
					valueObject = self,
					valueFunc = "getGpsWorkingWidth",
					minFunc = 0,
					maxFunc = 99.99
				}
			);

			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey, 

				{
					valueTypeToLoad = "gpsCurrentLane",
					valueObject = self,
					valueFunc = "getGpsCurrentLane",
					minFunc = 0,
					maxFunc = 99999
				}
			);

			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey, 

				{
					valueTypeToLoad = "currentFieldNumber",
					valueObject = self,
					valueFunc = "getCurrentFieldNumber",
					minFunc = 0,
					maxFunc = 999
				}
			);
		end;

		if specMotorized ~= nil then
			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey, 

				{
					valueTypeToLoad = "currentFuelUsage",
					valueObject = self,
					valueFunc = "getFuelUsage",
					minFunc = 0,
					maxFunc = 9999
				}
			);
			
			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey, 

				{
					valueTypeToLoad = "currentMotorTemperature",
					valueObject = self,
					valueFunc = "getMotorTemperature",
					minFunc = 0,
					maxFunc = 999
				}
			);
		end;

		if specAnimatedIndoorParts.psModName ~= "" then
			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey, 

				{
					valueTypeToLoad = "psCurrentLane",
					valueObject = self,
					valueFunc = "getPsCurrentLane",
					minFunc = 0,
					maxFunc = 9999
				}
			);

			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey,

				{
					valueTypeToLoad = "psMaxLanes",
					valueObject = self,
					valueFunc = "getPsMaxLanes",
					minFunc = 0,
					maxFunc = 9999
				}
			);

			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey,

				{
					valueTypeToLoad = "psWorkingWidth",
					valueObject = self,
					valueFunc = "getPsWorkingWidth",
					minFunc = 0,
					maxFunc = 9999
				}
			);

			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey,

				{
					valueTypeToLoad = "psTramlineDistance",
					valueObject = self,
					valueFunc = "getPsTramlineDistance",
					minFunc = 0,
					maxFunc = 9999
				}
			);
		end;

		local specTirePressure = AnimatedIndoorPartsUtil.getSpecByName(self, "tirePressure");

		if specTirePressure ~= nil then
			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey,
				{
					valueTypeToLoad = "currentTirePressure",
					valueObject = self,
					valueFunc = "getCurrentPressure",
					minFunc = 0,
					maxFunc = 9
				}
			);

			self:loadDashboardsFromXML(
				self.xmlFile, 
				AnimatedIndoorParts.baseDashboardKey,
				{
					valueTypeToLoad = "tragetTirePressure",
					valueObject = self,
					valueFunc = "getTargetPressure",
					minFunc = 0,
					maxFunc = 9
				}
			);
		end;
	end;

	if self.spec_enterable.cameras ~= nil then
		for cameraNumber, camera in pairs(self.spec_enterable.cameras) do
			if camera.isInside then
				camera.oldFovSaved = math.deg(getFovY(camera.cameraNode));
			end;
		end;
	end;

	specAnimatedIndoorParts.inputIsPressed = false;
	specAnimatedIndoorParts.resetIndoorCamera = false;
end;

function AnimatedIndoorParts:saveToXMLFile(xmlFile, key, usedModNames)
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");

	if #specAnimatedIndoorParts.screens > 0 and specAnimatedIndoorParts.currentScreenNumber > 1 then
		xmlFile:setValue(key .. "#currentScreenNumber", specAnimatedIndoorParts.currentScreenNumber);
	end;
end;

function AnimatedIndoorParts:onRegisterActionEvents(isActiveForInput)
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	
	if self.isClient then
		self:clearActionEventsTable(specAnimatedIndoorParts.actionEvents);
        
		if self:getIsActiveForInput(true, true) then
           	for _, input in pairs(AnimatedIndoorPartsUtil.inputs) do
				local inputNeedBePressed = input == "INDOOR_ZOOM_CAMERA_BUTTON_JD_PACK" or input == "INDOOR_RESET_CAMERA_BUTTON_JD_PACK";
				local isScreenInput = input == "SWITCH_SCREEN_UP_BUTTON_JD_PACK" or input == "SWITCH_SCREEN_DOWN_BUTTON_JD_PACK";
				
				local _, actionEventId = self:addActionEvent(specAnimatedIndoorParts.actionEvents, InputAction[input], self, AnimatedIndoorParts.changeIndoorCameraAndSwitchScreen, false, true, inputNeedBePressed, true, nil);

				g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL);
				g_inputBinding:setActionEventTextVisibility(actionEventId, (isScreenInput and #specAnimatedIndoorParts.screens > 1) or inputNeedBePressed);
				g_inputBinding:setActionEventActive(actionEventId, (isScreenInput and #specAnimatedIndoorParts.screens > 1) or inputNeedBePressed);
				g_inputBinding:setActionEventText(actionEventId, AnimatedIndoorPartsUtil.getL10nText("input_" .. input));
			end;
		end;
	end;
end;

function AnimatedIndoorParts.changeIndoorCameraAndSwitchScreen(self, actionName, inputValue, callbackState, isAnalog)
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");

	if #specAnimatedIndoorParts.screens > 1 then
		if actionName == "SWITCH_SCREEN_UP_BUTTON_JD_PACK" then
			if specAnimatedIndoorParts.currentScreenNumber < #specAnimatedIndoorParts.screens then
				self:setCurrentScreen(specAnimatedIndoorParts.currentScreenNumber + 1);
			else
				self:setCurrentScreen(1);
			end;
		elseif actionName == "SWITCH_SCREEN_DOWN_BUTTON_JD_PACK" then
			if specAnimatedIndoorParts.currentScreenNumber > 1 then
				self:setCurrentScreen(specAnimatedIndoorParts.currentScreenNumber - 1);
			else
				self:setCurrentScreen(#specAnimatedIndoorParts.screens);
			end;
		end;
	end;

	if actionName == "INDOOR_ZOOM_CAMERA_BUTTON_JD_PACK" then
		specAnimatedIndoorParts.inputIsPressed = true;
	elseif actionName == "INDOOR_RESET_CAMERA_BUTTON_JD_PACK" then
		specAnimatedIndoorParts.resetIndoorCamera = true;
	end;
end;

function AnimatedIndoorParts:setCurrentScreen(currentScreenNumber, noEventSend)
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");

    if currentScreenNumber ~= specAnimatedIndoorParts.currentScreenNumber then
		if not noEventSend then
			if g_server ~= nil then
				g_server:broadcastEvent(AnimatedIndoorPartsScreenEvent.new(self, currentScreenNumber), nil, nil, self);
			else
				g_client:getServerConnection():sendEvent(AnimatedIndoorPartsScreenEvent.new(self, currentScreenNumber));
			end;
		end;

		specAnimatedIndoorParts.currentScreenNumber = currentScreenNumber;
	end;
end;

function AnimatedIndoorParts:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(self, "fillUnit");
	local specDrivable = AnimatedIndoorPartsUtil.getSpecByName(self, "drivable");
	local specWearable = AnimatedIndoorPartsUtil.getSpecByName(self, "wearable");
	local specTrailer = AnimatedIndoorPartsUtil.getSpecByName(self, "trailer");
	local specMotorized = AnimatedIndoorPartsUtil.getSpecByName(self, "motorized");
	local specCombine = AnimatedIndoorPartsUtil.getSpecByName(self, "combine");
	local specDischargeable = AnimatedIndoorPartsUtil.getSpecByName(self, "dischargeable");
	local specPipe = AnimatedIndoorPartsUtil.getSpecByName(self, "pipe");
	
	local isTruckABSActive, truckIsTipping, airCompressorDoRefill = false, false, false;
	local warningWorkshop, brakesWearedOut, stopImminently, motorNeedMaintrace, oilFillLevelToLow, lowOilPressure = {false, false}, false, false, false, false, false;
	local vehicleSchemaIsTurnedOn = {false, false, false, false, false};
	local isStrawChopperActive, isOverloading, isGrainTankUnfolded, isPipeUnfolded, currentHarvestInputFruitType, currentHarvestOutputFruitType = false, false, false, false, FillType.UNKNOWN, FillType.UNKNOWN;
	
	local dirX, _, dirZ = localDirectionToWorld(self.rootNode, 0, 0, 1);
	
	specAnimatedIndoorParts.cardinalDirection = MathUtil.round(math.deg(math.abs(MathUtil.getYRotationFromDirection(dirX, dirZ) - math.pi)), 1);
	specAnimatedIndoorParts.getCurrentEnviromentTemperature = MathUtil.round(g_currentMission.environment.weather:getCurrentTemperature(), 1);
	specAnimatedIndoorParts.getCurrentBaleSize = 0;

	local isEnteredAsPassenger = false;

	--[[if self:getIsActiveForInput() then
		renderText(0.5, 0.5, 0.02, "Aktuelle Temperatur = " .. tostring(g_currentMission.environment.weather:getCurrentTemperature()))
		renderText(0.5, 0.48, 0.02, "Aktuelle Temperaturvorhersage = " .. tostring(g_currentMission.environment.weather:getCurrentTemperatureTrend()))
		renderText(0.5, 0.46, 0.02, "Aktuelles Wetter = " .. tostring(g_currentMission.environment.weather:getCurrentWeatherType()))
		renderText(0.5, 0.44, 0.02, "Aktuelle Wettervorhersage = " .. tostring(g_currentMission.environment.weather:getNextWeatherType(g_currentMission.environment.currentDay, g_currentMission.environment.currentHour)))
	end;]]

    if g_universalPassenger ~= nil then
        isEnteredAsPassenger = g_universalPassenger.currentPassengerVehicle ~= nil;
    end;

	if self:getIsActive() or isEnteredAsPassenger then
		for radioChannelNumber, radioChannel in pairs(AnimatedIndoorPartsUtil.radioChannels) do
			if radioChannel == "off" then
				if specAnimatedIndoorParts[radioChannel] ~= nil then	
					setVisibility(specAnimatedIndoorParts[radioChannel], (g_currentMission:getIsRadioPlaying() == nil or not g_currentMission:getIsRadioPlaying()) and self:getIsMotorStarted());
				end;
			else
				if g_currentMission:getIsRadioPlaying() ~= nil and g_soundPlayer ~= nil then
					if specAnimatedIndoorParts[radioChannel] ~= nil then
						if radioChannel ~= "internet" then
							setVisibility(specAnimatedIndoorParts[radioChannel], g_soundPlayer.currentChannel == (radioChannelNumber - 2) and g_currentMission:getIsRadioPlaying() and self:getIsMotorStarted());
						else
							setVisibility(specAnimatedIndoorParts[radioChannel], g_soundPlayer.currentChannel > 5 and g_currentMission:getIsRadioPlaying() and self:getIsMotorStarted());
						end;
					end;
				end;
			end;
		end;
	end;
	
	if specTrailer ~= nil and specTrailer.tipState ~= nil then
		truckIsTipping = specTrailer.tipState ~= Trailer.TIPSTATE_CLOSED;
	end;

	if self.getIsTurnedOn ~= nil and self:getIsTurnedOn() then
		vehicleSchemaIsTurnedOn[1] = true;
	end;
	
	if specCombine ~= nil then
		isStrawChopperActive = not specCombine.isSwathActive;
		isGrainTankUnfolded = self:getToggledFoldDirection() == 1;
		
		if specCombine.lastValidInputFruitType ~= nil then
			currentHarvestInputFruitType = g_fruitTypeManager:getFillTypeIndexByFruitTypeIndex(specCombine.lastValidInputFruitType);
		end;
	end;

	if specDischargeable ~= nil then
		isOverloading = specDischargeable.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF;
	end;

	if specPipe ~= nil then
		isPipeUnfolded = specPipe.targetState == specPipe.numStates;
	end;

	if specDrivable ~= nil then	
		isTruckABSActive = specDrivable.axisForward <= -1 and self.movingDirection > 0 and self:getLastSpeed() > 10;
	end;

	for _, fillUnit in pairs(specFillUnit.fillUnits) do
		if fillUnit.fillType == FillType.DIESEL then
			specAnimatedIndoorParts.isLowFillLevel = fillUnit.fillLevel / fillUnit.capacity < 0.2;
		end;
	end;

	if specWearable ~= nil then
		warningWorkshop[1] = self:getVehicleDamage() >= 0.7;
		warningWorkshop[2] = self:getVehicleDamage() >= 0.8;
		brakesWearedOut = self:getVehicleDamage() >= 0.82;
		motorNeedMaintrace = self:getVehicleDamage() >= 0.9;
		lowOilPressure = self:getVehicleDamage() >= 0.92;
		oilFillLevelToLow = self:getVehicleDamage() >= 0.95;
		stopImminently = self:getVehicleDamage() >= 1;
	end;

	if specMotorized ~= nil then
		if specMotorized.consumersByFillTypeName ~= nil and specMotorized.consumersByFillTypeName.air ~= nil then
			airCompressorDoRefill = Utils.getNoNil(specMotorized.consumersByFillTypeName.air.doRefill, false);
		end;

		local allowShowScreens = true;
		
		if #specAnimatedIndoorParts.startScreens > 0 then
			for startScreenNumber, startScreen in pairs(specAnimatedIndoorParts.startScreens) do
				if self:getIsMotorStarted() then
					startScreen.currentTime = MathUtil.clamp((startScreen.currentTime + dt), 0, startScreen.endTime);
				else
					startScreen.currentTime = 0;
				end;
				
				if startScreen.startTime <= startScreen.currentTime then
					local startScreenTimePercentage = (startScreen.currentTime - startScreen.startTime) / (startScreen.endTime - startScreen.startTime);
					
					if startScreen.loadingBar ~= nil then
						AnimatedIndoorPartsUtil.setBarScale(startScreen.loadingBar, startScreenTimePercentage, startScreen.scaleDirection);
					end;
					
					setVisibility(startScreen.node, self:getIsMotorStarted() and startScreenTimePercentage < 1);
				else
					if startScreen.loadingBar ~= nil then
						AnimatedIndoorPartsUtil.setBarScale(startScreen.loadingBar, 0, startScreen.scaleDirection);
					end;
					
					setVisibility(startScreen.node, false);
				end;
				
				allowShowScreens = startScreen.currentTime >= startScreen.endTime;
			end;
		end;
		
		local camIsInside = false;
		
		if self:getIsActiveForInput(true, true) then
			local specEnterable = self.spec_enterable;
	
			if self.isClient and specEnterable.cameras ~= nil then
				for cameraNumber, camera in pairs(specEnterable.cameras) do
					if camera.oldFovSaved ~= nil then
						local allowUpdateFov = false;
						
						if camera.seatCam ~= nil and camera.seatCam ~= 0 then 
							local user = g_currentMission.userManager:getUserByUserId(self.spec_enterable.controllerUserId);

							if g_gameStateManager:getGameState() == GameState.PLAY 
								and self:getControllerName() == user:getNickname()
								and specEnterable.camIndex == specEnterable.seatCamIndex
							then
								local newFov = math.deg(getFovY(camera.seatCam));
								
								if specAnimatedIndoorParts.inputIsPressed then
									if newFov > 23 then
										newFov = math.max(newFov - 0.055 * dt, 23);
									
										allowUpdateFov = true;
									end;
								else
									if newFov < camera.oldFovSaved then	
										newFov = math.min(newFov + 0.055 * dt, camera.oldFovSaved);
									
										allowUpdateFov = true;
									end;
								end;
							
								if allowUpdateFov then
									setFovY(camera.seatCam, math.rad(newFov));
								end;
							
								if specAnimatedIndoorParts.resetIndoorCamera then
									camera:resetCamera();
								end;
							
								camIsInside = true;
							end;
						else
							if camera.isInside and camera.isActivated then
								local newFov = math.deg(getFovY(camera.cameraNode));

								if specAnimatedIndoorParts.inputIsPressed then
									if newFov > 23 then
										newFov = math.max(newFov - 0.055 * dt, 23);

										allowUpdateFov = true;
									end;
								else
									if newFov < camera.oldFovSaved then	
										newFov = math.min(newFov + 0.055 * dt, camera.oldFovSaved);

										allowUpdateFov = true;
									end;
								end;

								if allowUpdateFov then
									setFovY(camera.cameraNode, math.rad(newFov));
								end;

								if specAnimatedIndoorParts.resetIndoorCamera then
									camera:resetCamera();
								end;

								camIsInside = true;
							end;
						end;
					end;
				end;
			end;

			for currentNumber = 1, 2 do
				local inputButton = specAnimatedIndoorParts.actionEvents[InputAction[AnimatedIndoorPartsUtil.inputs[currentNumber]]];
	
				if inputButton ~= nil then
					g_inputBinding:setActionEventTextVisibility(inputButton.actionEventId, camIsInside);
					g_inputBinding:setActionEventActive(inputButton.actionEventId, camIsInside);
				end;
			end;
	
			specAnimatedIndoorParts.inputIsPressed = false;
			specAnimatedIndoorParts.resetIndoorCamera = false;
		end;

		if #specAnimatedIndoorParts.disableNodesOnStart > 0 then
			for _, disableNodeOnStart in pairs(specAnimatedIndoorParts.disableNodesOnStart) do
				if disableNodeOnStart.node ~= nil then
					setVisibility(disableNodeOnStart.node, self:getIsMotorStarted() and allowShowScreens);
				end;
			end;
		end;

		if #specAnimatedIndoorParts.screens > 0 then
			if self:getIsActiveForInput(true, true) then
				for currentNumber = 3, 4 do
					local actionEvent = specAnimatedIndoorParts.actionEvents[InputAction[AnimatedIndoorPartsUtil.inputs[currentNumber]]];
				
					if actionEvent ~= nil then
						--## disable buttons on start

						g_inputBinding:setActionEventTextVisibility(actionEvent.actionEventId, allowShowScreens and camIsInside and #specAnimatedIndoorParts.screens > 1);
						g_inputBinding:setActionEventActive(actionEvent.actionEventId, allowShowScreens and camIsInside and #specAnimatedIndoorParts.screens > 1);
					end;
				end;
			end;

			for screenNumber, screen in pairs(specAnimatedIndoorParts.screens) do
				setVisibility(screen.node, screenNumber == specAnimatedIndoorParts.currentScreenNumber and self:getIsMotorStarted() and allowShowScreens);
			end;
		end;
	end;

	if #specAnimatedIndoorParts.fillLevelBars > 0 then
		for _, fillLevelBar in pairs(specAnimatedIndoorParts.fillLevelBars) do
			if fillLevelBar.screenNumber == specAnimatedIndoorParts.currentScreenNumber or fillLevelBar.screenNumber == 0 then
				setVisibility(fillLevelBar.node, self:getIsMotorStarted());

				local percentage = self:getFillLevelPercentTotal() / 100; 

				if fillLevelBar.isTrailer1 then
					percentage = self:getFillLevelPercentTrailer1() / 100;
				elseif fillLevelBar.isTrailer2 then
					percentage = self:getFillLevelPercentTrailer2() / 100;
				elseif fillLevelBar.isCurrentFilledTrailer then
					percentage = self:getCurrentFilledTrailerFillLevelPercent() / 100;
				elseif fillLevelBar.isSowingMachine then
					percentage = self:getFillLevelPercentSowingMachine() / 100;
				elseif fillLevelBar.isSeedFillType then
					percentage = self:getFillLevelPercentFillType("SEED") / 100;

					specAnimatedIndoorParts.currentSeedFillLevelPercent = percentage * 100;
				elseif fillLevelBar.isFertilizerFillType then
					percentage = self:getFillLevelPercentFillType("FERTILIZER") / 100;

					if percentage == 0 then
						percentage = self:getFillLevelPercentFillType("LIQUIDFERTILIZER") / 100;

						if percentage == 0 then
							percentage = self:getFillLevelPercentFillType("LIME") / 100;

							if percentage == 0 then
								percentage = self:getFillLevelPercentFillType("HERBICIDE") / 100;
							end;
						end;
					end;

					specAnimatedIndoorParts.currentFertilizerFillLevelPercent = percentage * 100;
					
					specAnimatedIndoorParts.currentFertilizerFillLevel = self:getFillLevelFillType("FERTILIZER");

					if specAnimatedIndoorParts.currentFertilizerFillLevel == 0 then
						specAnimatedIndoorParts.currentFertilizerFillLevel = self:getFillLevelFillType("LIQUIDFERTILIZER");

						if specAnimatedIndoorParts.currentFertilizerFillLevel == 0 then
							specAnimatedIndoorParts.currentFertilizerFillLevel = self:getFillLevelFillType("LIME");

							if specAnimatedIndoorParts.currentFertilizerFillLevel == 0 then
								specAnimatedIndoorParts.currentFertilizerFillLevel = self:getFillLevelFillType("HERBICIDE");
							end;
						end;
					end;
				elseif fillLevelBar.isCombine then
					percentage = self:getFillLevelPercentCombine() / 100;
				end;
			
				AnimatedIndoorPartsUtil.setBarScale(fillLevelBar.node, percentage, fillLevelBar.scaleDirection);
			end;
		end;
	end;

	if #specAnimatedIndoorParts.fillTypeTexts > 0 then
		for _, fillTypeText in pairs(specAnimatedIndoorParts.fillTypeTexts) do
			if fillTypeText.screenNumber == specAnimatedIndoorParts.currentScreenNumber or fillTypeText.screenNumber == 0 then
				if fillTypeText.showIfEmpty then
					setVisibility(fillTypeText.node, self:getIsMotorStarted() and specAnimatedIndoorParts.currentFertilizerFillLevel == 0);
				else
					setVisibility(fillTypeText.node, self:getIsMotorStarted() and self:getFillLevelFillType(fillTypeText.fillType) > 0);
				end;
			else
				if getVisibility(fillTypeText.node)	then
					setVisibility(fillTypeText.node, false);
				end;
			end;
		end;
	end;

	if specAttacherJoints ~= nil then
		local isPTOFrontActive, isPTOBackActive, moveDownFront, moveDownBack = false, false, false, false;
		local isLeftTipping, isRightTipping, isBackTipping, isGrainDoorTipping = {false, false}, {false, false}, {false, false}, {false, false};
		local numberOfAttachedTrailers, numberOfAttachedTrailersWithTurnLights, trailerIsTippingActive, trailerIsTipping, trailerHasSteerAxle = 0, 0, false, {false, false}, false;
		local diffLockFront, diffLockBack, handbrakeIsActive, fourWheel = false, false, false, false;
		local gpsIsActive, gpsSteeringIsActive, gpsCurrentLane = false, false, 0;
		local steerAxleIsLocked, ridgeMarkerState, isPickupLowered, toolFrontIsUnfolded, toolBackIsUnfolded, hasFoldablePartsBack, hasFoldablePartsFront = false, 0, false, false, false, false, false;
		local cutterSpeed, currentSeedFruitType, currentFillUnit = 0, FillType.UNKNOWN, {1, 2};

		currentFillUnit[1], currentFillUnit[2] = {FillType.UNKNOWN, FillType.UNKNOWN}, {FillType.UNKNOWN, FillType.UNKNOWN};

		local drivableIsSelected, toolFrontIsSelected1, toolBackIsSelected1 = self:getIsSelected(), false, false, false;
		local toolFrontIsSelected2, toolBackIsSelected2 = false, false;
		local frontloaderIsSelected, frontloaderToolIsSelected = false, false;
		local currentShutoffModeIsLeft, currentShutoffModeIsRight, currentModeIsAuto, currentModeIsSemi, currentModeIsManual, createTramLines = false, false, false, false, false, false;
		local frontMoveAlpha, backMoveAlpha, liftAxleIsDown = 0, 0, true;
		local toolFrontIsAttached1, toolFrontIsAttached2, toolBackIsAttached1, toolBackIsAttached2, frontloaderIsAttached, frontloaderToolIsAttached = false, false, false, false, false, false;
		local currentWorkMode, baleWrapperIsActive, currentDropModeStateIsAutomatic, currentDropModeStateIsCollect = "", false, false, false;
		
		if specAnimatedIndoorParts.gpsModName ~= "" then
			local specGlobalPositioningSystem = AnimatedIndoorPartsUtil.getSpecByName(self, "globalPositioningSystem", specAnimatedIndoorParts.gpsModName);
			
			if specGlobalPositioningSystem.guidanceData ~= nil then
				gpsIsActive = specGlobalPositioningSystem.guidanceIsActive;
				gpsSteeringIsActive = specGlobalPositioningSystem.guidanceSteeringIsActive;
				gpsCurrentLane = specGlobalPositioningSystem.guidanceData.currentLane;
			end;
		end;
	
		if self.spec_vca == nil then
			if self.vData ~= nil then
				diffLockFront, diffLockBack, handbrakeIsActive, fourWheel = self.vData.is[1], self.vData.is[2], self.vData.is[13], self.vData.is[3] == 1;
			end;
		else
			diffLockFront, diffLockBack, handbrakeIsActive, fourWheel = self.spec_vca.diffLockFront and self.spec_vca.diffLockAWD, self.spec_vca.diffLockBack, self.spec_vca.newHandbrake, self.spec_vca.diffLockAWD;
		end;

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			--## tool 1
			
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local object_specAttachable = AnimatedIndoorPartsUtil.getSpecByName(object, "attachable");
			local object_specTrailer = AnimatedIndoorPartsUtil.getSpecByName(object, "trailer");
			local object_specLockSteeringAxles = AnimatedIndoorPartsUtil.getSpecByName(object, "lockSteeringAxles", specAnimatedIndoorParts.lsaModName);
			local object_specBuyableSteeringAxle = AnimatedIndoorPartsUtil.getSpecByName(object, "buyableSteeringAxle", specAnimatedIndoorParts.bsaModName);
			local object_specAutomaticUnloadForBaleWrapper = AnimatedIndoorPartsUtil.getSpecByName(object, "automaticUnloadForBaleWrapper", specAnimatedIndoorParts.aufbwModName);
			local object_specLiftAxle = AnimatedIndoorPartsUtil.getSpecByName(object, "liftAxle", specAnimatedIndoorParts.laModName);
			local object_specRidgeMarker = AnimatedIndoorPartsUtil.getSpecByName(object, "ridgeMarker");
			local object_specFoldable = AnimatedIndoorPartsUtil.getSpecByName(object, "foldable");
			local object_specCutter = AnimatedIndoorPartsUtil.getSpecByName(object, "cutter");
			local object_specLights = AnimatedIndoorPartsUtil.getSpecByName(object, "lights");
			local object_specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			local object_specSowingMachine = AnimatedIndoorPartsUtil.getSpecByName(object, "sowingMachine");
			local object_specWorkMode = AnimatedIndoorPartsUtil.getSpecByName(object, "workMode");
			local object_specMower = AnimatedIndoorPartsUtil.getSpecByName(object, "mower");
			local object_specBaler = AnimatedIndoorPartsUtil.getSpecByName(object, "baler");

			local attacherVehicle_zScale = nil;

			if specAnimatedIndoorParts.psModName ~= "" then
				local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);

				if specProSeedTramLines ~= nil then
					currentModeIsAuto = specProSeedTramLines.tramLineMode == 2;
					currentModeIsSemi = specProSeedTramLines.tramLineMode == 1;
					currentModeIsManual = specProSeedTramLines.tramLineMode == 0;
					currentShutoffModeIsLeft = specProSeedTramLines.shutoffMode == 1;
					currentShutoffModeIsRight = specProSeedTramLines.shutoffMode == 2;
					createTramLines = specProSeedTramLines.createTramLines;
				end;
			end;
			
			if object_specCutter ~= nil then
				for animationNumber, animationNode in pairs(object_specCutter.animationNodes) do
					if getName(animationNode.node) == "reelRotSpeed" then
						local maxSpeed = math.deg(animationNode.rotSpeed) * 1000;
						local divident = 1;
						local multiplikator = 2;
						
						if string.find(maxSpeed, "-") then
							maxSpeed = -maxSpeed;
							divident = 2;
							multiplikator = 1;
						end;

						if object_specCutter.cutterSpeed == nil then
							object_specCutter.cutterSpeed = 0;
						end;

						if self:getIsTurnedOn() then
							if object_specCutter.cutterSpeed < maxSpeed then	
								object_specCutter.cutterSpeed = math.min(object_specCutter.cutterSpeed + (animationNode.turnOnFadeTime / 1000) * multiplikator, maxSpeed);
							end;

							vehicleSchemaIsTurnedOn[2] = true;
						else
							if object_specCutter.cutterSpeed > 0 then	
								object_specCutter.cutterSpeed = math.max(object_specCutter.cutterSpeed - (animationNode.turnOffFadeTime / 1000) / divident, 0);
							end;
						end;

						cutterSpeed = object_specCutter.cutterSpeed;

						AnimatedIndoorPartsUtil.renderText(0.5, 0.5, 0.02, "cutterSpeed = " .. cutterSpeed, AnimatedIndoorParts.debugPriority);
					end;
				end;

				if object_specCutter.currentInputFruitType ~= nil then
					outputFruitType = object_specCutter.currentInputFruitType;
		
					if object_specCutter.fruitTypeConverters[outputFruitType] ~= nil then
						outputFruitType = object_specCutter.fruitTypeConverters[outputFruitType].fillTypeIndex;
					end;
		
					currentHarvestOutputFruitType = outputFruitType;
				end;
			end;
			
			if object_specRidgeMarker ~= nil then
				ridgeMarkerState = object_specRidgeMarker.ridgeMarkerState;
			end;

			if object_specLockSteeringAxles ~= nil and object_specLockSteeringAxles.lockSteeringAxle ~= nil then
				steerAxleIsLocked = object_specLockSteeringAxles.lockSteeringAxle;
				trailerHasSteerAxle = true;
			end;

			if object_specBuyableSteeringAxle ~= nil and object_specBuyableSteeringAxle.lockSteeringAxle ~= nil then
				steerAxleIsLocked = object_specBuyableSteeringAxle.lockSteeringAxle;
				trailerHasSteerAxle = true;
			end;

			if object_specAutomaticUnloadForBaleWrapper ~= nil then
				baleWrapperIsActive = object_specAutomaticUnloadForBaleWrapper.baleWrapperIsActive;
				currentDropModeStateIsAutomatic = object_specAutomaticUnloadForBaleWrapper.currentDropModeState == 2;
				currentDropModeStateIsCollect = object_specAutomaticUnloadForBaleWrapper.currentDropModeState == 3;
			end;

			if object_specBaler ~= nil then
				local baleTypeDef = object_specBaler.baleTypes[object_specBaler.currentBaleTypeIndex];
				local baleSize = baleTypeDef.length;

				if baleTypeDef.isRoundBale then
					baleSize = object_specBaler.baleTypes[object_specBaler.currentBaleTypeIndex].diameter;
				end;
				
				if baleSize ~= nil then
					specAnimatedIndoorParts.getCurrentBaleSize = baleSize;
				end;
				
				if not currentDropModeStateIsAutomatic and object_specBaler.toggleableAutomaticDrop then
					currentDropModeStateIsAutomatic = object_specBaler.automaticDrop or object_specBaler.platformAutomaticDrop;
				end;
			end;

			if object_specLiftAxle ~= nil then
				liftAxleIsDown = object_specLiftAxle.isDown;
			end;
			
			if object_specTrailer ~= nil then
				trailerIsTippingActive = object_specTrailer.tipState ~= nil and object_specTrailer.tipState ~= Trailer.TIPSTATE_CLOSED;
				trailerIsTipping[1] = trailerIsTippingActive;
				
				if object_specTrailer.preferedTipSideIndex ~= nil then
					local tipSide = object_specTrailer.tipSides[object_specTrailer.preferedTipSideIndex];
					
					if tipSide ~= nil then
						isLeftTipping[1] = tipSide.name == g_i18n:getText("info_tipSideLeft");
						isRightTipping[1] = tipSide.name == g_i18n:getText("info_tipSideRight");
						isBackTipping[1] = tipSide.name == g_i18n:getText("info_tipSideBack");
						isGrainDoorTipping[1] = tipSide.name == g_i18n:getText("info_tipSideBackGrainDoor");
					end;
				end;
			end;

			if object_specLights ~= nil then
				if AnimatedIndoorPartsUtil.getHasTurnLights(object.xmlFile) then
					numberOfAttachedTrailersWithTurnLights = numberOfAttachedTrailersWithTurnLights + 1;
				end;
			end;

			if object_specFillUnit ~= nil then
				for fillUnitNumber = 1, 2 do
					local fillUnit = object_specFillUnit.fillUnits[fillUnitNumber];

					if fillUnit ~= nil and fillUnit.showOnHud then
						currentFillUnit[fillUnitNumber][1] = object:getFillUnitFillType(fillUnitNumber);
					end;
				end;
			end;

			if object_specSowingMachine ~= nil then
				currentSeedFruitType = g_fruitTypeManager:getFillTypeIndexByFruitTypeIndex(object_specSowingMachine.workAreaParameters.seedsFruitType);
			end;

			if object_specMower ~= nil
				and object_specWorkMode ~= nil 
				and object_specWorkMode.workModes ~= nil
				and object_specWorkMode.workModes[object_specWorkMode.state] ~= nil
				and object_specWorkMode.workModes[object_specWorkMode.state].name ~= nil
			then
				local currentWorkModeName = object_specWorkMode.workModes[object_specWorkMode.state].name;

				if currentWorkModeName == g_i18n:getText("action_workMode_broad") then
					currentWorkMode = "BROAD";
				elseif currentWorkModeName == g_i18n:getText("action_workMode_leftSwath") then
					currentWorkMode = "LEFT_SWATH";
				elseif currentWorkModeName == g_i18n:getText("action_workMode_rightSwath") then
					currentWorkMode = "RIGHT_SWATH";
				elseif currentWorkModeName == g_i18n:getText("action_workMode_swath") then
					currentWorkMode = "SWATH";
				end;
			end;

			if object_specAttachable.attacherVehicle ~= nil then
				local attacherJointVehicleSpec = AnimatedIndoorPartsUtil.getSpecByName(object_specAttachable.attacherVehicle, "attacherJoints");
				local implementIndex = object_specAttachable.attacherVehicle:getImplementIndexByObject(object);
				local implement = attacherJointVehicleSpec.attachedImplements[implementIndex];
				local inputJointDescIndex = object_specAttachable.inputAttacherJointDescIndex;
				local jointDescIndex = implement.jointDescIndex;
				local jointDesc = attacherJointVehicleSpec.attacherJoints[jointDescIndex];
				
				if jointDesc.jointType == AttacherJoints.JOINTTYPE_TRAILER or jointDesc.jointType == AttacherJoints.JOINTTYPE_TRAILERLOW and object_specTrailer ~= nil then
					numberOfAttachedTrailers = numberOfAttachedTrailers + 1;
				end;

				if jointDesc.jointType == AttacherJoints.JOINTTYPE_TRAILERLOW then
					moveDownBack = object:getIsImplementChainLowered();

					if object.getIsTurnedOn ~= nil then
						isPTOBackActive = object:getIsTurnedOn();
						vehicleSchemaIsTurnedOn[3] = object:getIsTurnedOn();
					end;

					if object_specFoldable ~= nil then
						hasFoldablePartsBack = object_specFoldable.foldingParts ~= nil and #object_specFoldable.foldingParts > 0;

						local isPlayingAnimation = false;
						
						for _, foldingPart in pairs(object_specFoldable.foldingParts) do
							isPlayingAnimation = object:getIsAnimationPlaying(foldingPart.animationName);
						end;
					
						toolBackIsUnfolded = object:getIsUnfolded() or isPlayingAnimation;
					end;

					toolBackIsAttached1 = true;
				end;
				
				if jointDesc.bottomArm ~= nil then
					if jointDesc.bottomArm.zScale == 1 then
						moveDownFront = object:getIsImplementChainLowered();
						
						if object.getIsTurnedOn ~= nil then	
							isPTOFrontActive = object:getIsTurnedOn();
							vehicleSchemaIsTurnedOn[2] = object:getIsTurnedOn();
						end;

						if object_specFoldable ~= nil then
							hasFoldablePartsFront = object_specFoldable.foldingParts ~= nil and #object_specFoldable.foldingParts > 0;

							local isPlayingAnimation = false;
							
							for _, foldingPart in pairs(object_specFoldable.foldingParts) do
								isPlayingAnimation = object:getIsAnimationPlaying(foldingPart.animationName);
							end;

							toolFrontIsUnfolded = object:getIsUnfolded() or isPlayingAnimation;
						end;

						toolFrontIsSelected1 = object:getIsSelected();

						frontMoveAlpha, upperDistanceToGround, lowerDistanceToGround = AnimatedIndoorPartsUtil.getJointMoveAlpha(jointDesc, object, self, dt);

						if self:getIsMotorStarted() then
							--renderText(0.2, 0.5, 0.02, "frontMoveAlpha = " .. tostring(frontMoveAlpha) .. " upperDistanceToGround = " .. tostring(upperDistanceToGround) .. " lowerDistanceToGround = " .. tostring(lowerDistanceToGround));
						end;

						toolFrontIsAttached1 = true;
					elseif jointDesc.bottomArm.zScale == -1 then
						moveDownBack = object:getIsImplementChainLowered();
						
						if object.getIsTurnedOn ~= nil then
							isPTOBackActive = object:getIsTurnedOn();
							vehicleSchemaIsTurnedOn[3] = object:getIsTurnedOn();
						end;

						if object_specFoldable ~= nil then
							hasFoldablePartsBack = object_specFoldable.foldingParts ~= nil and #object_specFoldable.foldingParts > 0;

							local isPlayingAnimation = false;
							
							for _, foldingPart in pairs(object_specFoldable.foldingParts) do
								isPlayingAnimation = object:getIsAnimationPlaying(foldingPart.animationName);
							end;
						
							toolBackIsUnfolded = object:getIsUnfolded() or isPlayingAnimation;
						end;

						toolBackIsSelected1 = object:getIsSelected();
						toolBackIsAttached1 = true;
				
						backMoveAlpha, upperDistanceToGround, lowerDistanceToGround = AnimatedIndoorPartsUtil.getJointMoveAlpha(jointDesc, object, self, dt);

						if self:getIsMotorStarted() then
							--renderText(0.2, 0.48, 0.02, "backMoveAlpha = " .. tostring(backMoveAlpha) .. " upperDistanceToGround = " .. tostring(upperDistanceToGround) .. " lowerDistanceToGround = " .. tostring(lowerDistanceToGround));
						end;
					end;

					attacherVehicle_zScale = jointDesc.bottomArm.zScale;
				else
					if object.getIsTurnedOn ~= nil then	
						if not jointDesc.invertX then
							isPTOBackActive = object:getIsTurnedOn();
							vehicleSchemaIsTurnedOn[3] = object:getIsTurnedOn();
							
							if not moveDownBack then
								moveDownBack = object:getIsImplementChainLowered();
							end;

							if jointDesc.jointType ~= AttacherJoints.JOINTTYPE_FRONTLOADER then
								toolBackIsSelected1 = object:getIsSelected();
								toolBackIsAttached1 = true;
							end;
						else
							isPTOFrontActive = object:getIsTurnedOn();
							vehicleSchemaIsTurnedOn[2] = object:getIsTurnedOn();

							if jointDesc.jointType ~= AttacherJoints.JOINTTYPE_FRONTLOADER then
								toolFrontIsSelected1 = object:getIsSelected();
								toolFrontIsAttached1 = true;
							end;
						end;
					else
						if jointDesc.jointType ~= AttacherJoints.JOINTTYPE_ATTACHABLEFRONTLOADER
							and jointDesc.jointType ~= AttacherJoints.JOINTTYPE_ATTACHABLEFRONTLOADERHAUER
							and jointDesc.jointType ~= AttacherJoints.JOINTTYPE_ATTACHABLEFRONTLOADERJOHNDEERE
							and jointDesc.jointType ~= AttacherJoints.JOINTTYPE_ATTACHABLEFRONTLOADERSTOLL
							and jointDesc.jointType ~= AttacherJoints.JOINTTYPE_FRONTLOADER
						then
							if not jointDesc.invertX then
								toolBackIsSelected1 = object:getIsSelected();
								toolBackIsAttached1 = true;
							else
								toolFrontIsSelected1 = object:getIsSelected();
								toolFrontIsAttached1 = true;
							end;
						else
							if jointDesc.jointType ~= AttacherJoints.JOINTTYPE_FRONTLOADER then
								frontloaderIsSelected = object:getIsSelected();
								frontloaderIsAttached = true;
							end;
						end;
					end;

					attacherVehicle_invertX = jointDesc.invertX;
				end;
			end;

			local object_specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

			if object_specAttacherJoints ~= nil then
				for attachedImplement = 1, #object_specAttacherJoints.attachedImplements do
					--## tool 2
					
					local object = object_specAttacherJoints.attachedImplements[attachedImplement].object;
					local object_specLockSteeringAxles = AnimatedIndoorPartsUtil.getSpecByName(object, "lockSteeringAxles", specAnimatedIndoorParts.lsaModName);
					local object_specBuyableSteeringAxle = AnimatedIndoorPartsUtil.getSpecByName(object, "buyableSteeringAxle", specAnimatedIndoorParts.bsaModName);
					local object_specAttachable = AnimatedIndoorPartsUtil.getSpecByName(object, "attachable");
					local object_specTrailer = AnimatedIndoorPartsUtil.getSpecByName(object, "trailer");
					local object_specLights = AnimatedIndoorPartsUtil.getSpecByName(object, "lights");
					local object_specRidgeMarker = AnimatedIndoorPartsUtil.getSpecByName(object, "ridgeMarker");
					local object_specFoldable = AnimatedIndoorPartsUtil.getSpecByName(object, "foldable");
					local object_specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
					local object_specSowingMachine = AnimatedIndoorPartsUtil.getSpecByName(object, "sowingMachine");
					
					local attacherJointVehicleSpec = AnimatedIndoorPartsUtil.getSpecByName(object_specAttachable.attacherVehicle, "attacherJoints");
					local implementIndex = object_specAttachable.attacherVehicle:getImplementIndexByObject(object);
					local implement = attacherJointVehicleSpec.attachedImplements[implementIndex];
					local inputJointDescIndex = object_specAttachable.inputAttacherJointDescIndex;
					local jointDescIndex = implement.jointDescIndex;
					local jointDesc = attacherJointVehicleSpec.attacherJoints[jointDescIndex];

					if jointDesc.jointType == AttacherJoints.JOINTTYPE_TRAILER or jointDesc.jointType == AttacherJoints.JOINTTYPE_TRAILERLOW or object_specTrailer ~= nil then
						numberOfAttachedTrailers = numberOfAttachedTrailers + 1;
					end;

					if specAnimatedIndoorParts.psModName ~= "" then
						local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);
		
						if specProSeedTramLines ~= nil then
							currentModeIsAuto = specProSeedTramLines.tramLineMode == 2;
							currentModeIsSemi = specProSeedTramLines.tramLineMode == 1;
							currentModeIsManual = specProSeedTramLines.tramLineMode == 0;
							currentShutoffModeIsLeft = specProSeedTramLines.shutoffMode == 1;
							currentShutoffModeIsRight = specProSeedTramLines.shutoffMode == 2;
							createTramLines = specProSeedTramLines.createTramLines;
						end;
					end;

					if jointDesc.jointType ~= AttacherJoints.JOINTTYPE_FRONTLOADER then
						if attacherVehicle_zScale ~= nil then	
							if attacherVehicle_zScale == 1 then
								toolFrontIsSelected2 = object:getIsSelected();
								toolFrontIsAttached2 = true;
							elseif attacherVehicle_zScale == -1 then
								toolBackIsSelected2 = object:getIsSelected();
								toolBackIsAttached2 = true;
								moveDownBack = object:getIsImplementChainLowered();
								
								if object.getIsTurnedOn ~= nil then
									if not isPTOBackActive then	
										isPTOBackActive = object:getIsTurnedOn();
									end;

									vehicleSchemaIsTurnedOn[4] = object:getIsTurnedOn();
								end;

								if object_specFoldable ~= nil then
									hasFoldablePartsBack = object_specFoldable.foldingParts ~= nil and #object_specFoldable.foldingParts > 0;

									local isPlayingAnimation = false;
							
									for _, foldingPart in pairs(object_specFoldable.foldingParts) do
										isPlayingAnimation = object:getIsAnimationPlaying(foldingPart.animationName);
									end;

									toolBackIsUnfolded = object:getIsUnfolded() or isPlayingAnimation;
								end;
							end;
						else
							if not attacherVehicle_invertX then
								toolBackIsSelected2 = object:getIsSelected();
								toolBackIsAttached2 = true;
								moveDownBack = object:getIsImplementChainLowered();
								
								if object.getIsTurnedOn ~= nil then
									if not isPTOBackActive then	
										isPTOBackActive = object:getIsTurnedOn();
									end;

									vehicleSchemaIsTurnedOn[4] = object:getIsTurnedOn();
								end;

								if object_specFoldable ~= nil then
									hasFoldablePartsBack = object_specFoldable.foldingParts ~= nil and #object_specFoldable.foldingParts > 0;

									local isPlayingAnimation = false;
							
									for _, foldingPart in pairs(object_specFoldable.foldingParts) do
										isPlayingAnimation = object:getIsAnimationPlaying(foldingPart.animationName);
									end;

									toolBackIsUnfolded = object:getIsUnfolded() or isPlayingAnimation;
								end;
							else
								toolFrontIsSelected2 = object:getIsSelected();
								toolFrontIsAttached2 = true;
							end;
						end;
					else
						frontloaderToolIsSelected = object:getIsSelected();
						frontloaderToolIsAttached = true;

						if object.getIsTurnedOn ~= nil then
							vehicleSchemaIsTurnedOn[5] = object:getIsTurnedOn();
						end;
					end;

					if object_specRidgeMarker ~= nil then
						ridgeMarkerState = object_specRidgeMarker.ridgeMarkerState;
					end;
				
					if object_specTrailer ~= nil then
						trailerIsTippingActive = object_specTrailer.tipState ~= nil and object_specTrailer.tipState ~= Trailer.TIPSTATE_CLOSED;
						trailerIsTipping[2] = trailerIsTippingActive;

						if object_specTrailer.preferedTipSideIndex ~= nil then
							local tipSide = object_specTrailer.tipSides[object_specTrailer.preferedTipSideIndex];
							
							if tipSide ~= nil then
								isLeftTipping[2] = tipSide.name == g_i18n:getText("info_tipSideLeft");
								isRightTipping[2] = tipSide.name == g_i18n:getText("info_tipSideRight");
								isBackTipping[2] = tipSide.name == g_i18n:getText("info_tipSideBack");
								isGrainDoorTipping[2] = tipSide.name == g_i18n:getText("info_tipSideBackGrainDoor");
							end;
						end;
					end;

					if object_specLights ~= nil then
						if AnimatedIndoorPartsUtil.getHasTurnLights(object.xmlFile) then
							numberOfAttachedTrailersWithTurnLights = numberOfAttachedTrailersWithTurnLights + 1;
						end;
					end;

					if object_specFillUnit ~= nil then
						for fillUnitNumber = 1, 2 do
							local fillUnit = object_specFillUnit.fillUnits[fillUnitNumber];
							
							if fillUnit ~= nil and fillUnit.showOnHud then
								currentFillUnit[fillUnitNumber][2] = object:getFillUnitFillType(fillUnitNumber);
							end;
						end;
					end;

					if object_specSowingMachine ~= nil then
						currentSeedFruitType = g_fruitTypeManager:getFillTypeIndexByFruitTypeIndex(object_specSowingMachine.workAreaParameters.seedsFruitType);
					end;

					if not steerAxleIsLocked then
						if object_specLockSteeringAxles ~= nil and object_specLockSteeringAxles.lockSteeringAxle ~= nil then
							steerAxleIsLocked = object_specLockSteeringAxles.lockSteeringAxle;
							trailerHasSteerAxle = true;
						end;
					
						if object_specBuyableSteeringAxle ~= nil and object_specBuyableSteeringAxle.lockSteeringAxle ~= nil then
							steerAxleIsLocked = object_specBuyableSteeringAxle.lockSteeringAxle;
							trailerHasSteerAxle = true;
						end;
					end;

					local object_specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

					if object_specAttacherJoints ~= nil then
						for attachedImplement = 1, #object_specAttacherJoints.attachedImplements do
							--## tool 3

							local object = object_specAttacherJoints.attachedImplements[attachedImplement].object;
							local object_specRidgeMarker = AnimatedIndoorPartsUtil.getSpecByName(object, "ridgeMarker");
							local object_specFoldable = AnimatedIndoorPartsUtil.getSpecByName(object, "foldable");
							local object_specSowingMachine = AnimatedIndoorPartsUtil.getSpecByName(object, "sowingMachine");

							if object_specRidgeMarker ~= nil then
								ridgeMarkerState = object_specRidgeMarker.ridgeMarkerState;
							end;

							if object_specFoldable ~= nil then
								hasFoldablePartsBack = object_specFoldable.foldingParts ~= nil and #object_specFoldable.foldingParts > 0;

								local isPlayingAnimation = false;
						
								for _, foldingPart in pairs(object_specFoldable.foldingParts) do
									isPlayingAnimation = object:getIsAnimationPlaying(foldingPart.animationName);
								end;

								toolBackIsUnfolded = object:getIsUnfolded() or isPlayingAnimation;
							end;

							if object_specSowingMachine ~= nil then
								currentSeedFruitType = g_fruitTypeManager:getFillTypeIndexByFruitTypeIndex(object_specSowingMachine.workAreaParameters.seedsFruitType);
							end;
						end;
					end;
				end;
			end;
		end;

		if #specAnimatedIndoorParts.fillTypeIcons > 0 then
			for _, fillTypeIcon in pairs(specAnimatedIndoorParts.fillTypeIcons) do
				if fillTypeIcon.screenNumber == specAnimatedIndoorParts.currentScreenNumber or fillTypeIcon.screenNumber == 0 then
					if fillTypeIcon.node ~= nil then
						local currentFillType = currentFillUnit[fillTypeIcon.fillUnitIndex][fillTypeIcon.trailerNumber];

						setVisibility(fillTypeIcon.node, currentFillType ~= FillType.UNKNOWN);

						if currentFillType ~= FillType.UNKNOWN then		
							local oldMaterial = getMaterial(fillTypeIcon.node, 0);
							local newMaterial = g_materialManager:getMaterial(currentFillType, "icon", 1);

							setVisibility(fillTypeIcon.node, newMaterial ~= nil);
						
							if newMaterial ~= nil then
								ConfigurationUtil.replaceMaterialRec(self, fillTypeIcon.node, oldMaterial, newMaterial);
							end;
						end;
					end;
				else
					if getVisibility(fillTypeIcon.node) then
						setVisibility(fillTypeIcon.node, false);
					end;
				end;
			end;
		end;

		if #specAnimatedIndoorParts.seedFruitTypeIcons > 0 then
			for _, seedFruitTypeIcon in pairs(specAnimatedIndoorParts.seedFruitTypeIcons) do
				if seedFruitTypeIcon.screenNumber == specAnimatedIndoorParts.currentScreenNumber or seedFruitTypeIcon.screenNumber == 0 then	
					if seedFruitTypeIcon.node ~= nil then
						setVisibility(seedFruitTypeIcon.node, currentSeedFruitType ~= FillType.UNKNOWN);
					
						if currentSeedFruitType ~= FillType.UNKNOWN then
							local oldMaterial = getMaterial(seedFruitTypeIcon.node, 0);
							local newMaterial = g_materialManager:getMaterial(currentSeedFruitType, "icon", 1);

							setVisibility(seedFruitTypeIcon.node, newMaterial ~= nil);

							if newMaterial ~= nil then
								ConfigurationUtil.replaceMaterialRec(self, seedFruitTypeIcon.node, oldMaterial, newMaterial);
							end;
						end;
					end;
				else
					if getVisibility(seedFruitTypeIcon.node) then
						setVisibility(seedFruitTypeIcon.node, false);
					end;
				end;
			end;
		end;

		if #specAnimatedIndoorParts.harvestInputFruitTypeIcons > 0 then
			for _, harvestInputFruitTypeIcon in pairs(specAnimatedIndoorParts.harvestInputFruitTypeIcons) do
				if harvestInputFruitTypeIcon.screenNumber == specAnimatedIndoorParts.currentScreenNumber or harvestInputFruitTypeIcon.screenNumber == 0 then
					if harvestInputFruitTypeIcon.node ~= nil and currentHarvestInputFruitType ~= nil then
						setVisibility(harvestInputFruitTypeIcon.node, currentHarvestInputFruitType ~= FillType.UNKNOWN);
					
						if currentHarvestInputFruitType ~= FillType.UNKNOWN then
							local oldMaterial = getMaterial(harvestInputFruitTypeIcon.node, 0);
							local newMaterial = g_materialManager:getMaterial(currentHarvestInputFruitType, "icon", 1);

							setVisibility(harvestInputFruitTypeIcon.node, newMaterial ~= nil);

							if newMaterial ~= nil then
								ConfigurationUtil.replaceMaterialRec(self, harvestInputFruitTypeIcon.node, oldMaterial, newMaterial);
							end;
						end;
					end;
				else
					if getVisibility(harvestInputFruitTypeIcon.node) then
						setVisibility(harvestInputFruitTypeIcon.node, false);
					end;
				end;
			end;
		end;

		if #specAnimatedIndoorParts.harvestOutputFruitTypeIcons > 0 then
			for _, harvestOutputFruitTypeIcon in pairs(specAnimatedIndoorParts.harvestOutputFruitTypeIcons) do
				if harvestOutputFruitTypeIcon.screenNumber == specAnimatedIndoorParts.currentScreenNumber or harvestOutputFruitTypeIcon.screenNumber == 0 then
					if harvestOutputFruitTypeIcon.node ~= nil then
						setVisibility(harvestOutputFruitTypeIcon.node, currentHarvestOutputFruitType ~= FillType.UNKNOWN);
					
						if currentHarvestOutputFruitType ~= FillType.UNKNOWN then
							local oldMaterial = getMaterial(harvestOutputFruitTypeIcon.node, 0);
							local newMaterial = g_materialManager:getMaterial(currentHarvestOutputFruitType, "icon", 1);

							setVisibility(harvestOutputFruitTypeIcon.node, newMaterial ~= nil);

							if newMaterial ~= nil then
								ConfigurationUtil.replaceMaterialRec(self, harvestOutputFruitTypeIcon.node, oldMaterial, newMaterial);
							end;
						end;
					end;
				else
					if getVisibility(harvestOutputFruitTypeIcon.node) then
						setVisibility(harvestOutputFruitTypeIcon.node, false);
					end;
				end;
			end;
		end;

		if specAnimatedIndoorParts.driveSymbols ~= nil then
			for _, driveSymbol in pairs(specAnimatedIndoorParts.driveSymbols) do
				if driveSymbol.screenNumber == specAnimatedIndoorParts.currentScreenNumber or driveSymbol.screenNumber == 0 then
					if driveSymbol.name == "DISABLE_ON_MOTOR_OFF" then
						setVisibility(driveSymbol.node, not self:getIsMotorStarted());
					end;

					if self:getIsMotorStarted() then
						if driveSymbol.name ~= "CONTROL_LAMP"
							and driveSymbol.name ~= "DISABLE_ON_MOTOR_OFF"
							and driveSymbol.name ~= "GPS_LANE_PLUS"
							and driveSymbol.name ~= "GPS_LANE_MINUS"
						then
							if driveSymbol.name == "TOOL_FRONT_LOWERED"
								or driveSymbol.name == "TOOL_FRONT_LIFTED"
								or driveSymbol.name == "TOOL_BACK_LOWERED"
								or driveSymbol.name == "TOOL_BACK_LIFTED"
							then
								if not driveSymbol.changeVisibility then
									setVisibility(driveSymbol.node, true);
									setShaderParameter(driveSymbol.node, "lightControl", 0, 0, 0, 0, false);
								end;
							else
								setVisibility(driveSymbol.node, true);
								setShaderParameter(driveSymbol.node, "lightControl", 0, 0, 0, 0, false);
							end;
						end;

						if currentWorkMode ~= "" then
							if driveSymbol.name == "WORK_MODE_" .. currentWorkMode then
								setVisibility(driveSymbol.node, true);
							else
								if driveSymbol.name:find("WORK_MODE") then
									setVisibility(driveSymbol.node, false);
								end;
							end;
						else	
							for _, workMode in pairs(AnimatedIndoorPartsUtil.workModes) do
								if driveSymbol.name == "WORK_MODE_" .. workMode then
									setVisibility(driveSymbol.node,  driveSymbol.name == "WORK_MODE_BROAD");
								end;
							end;
						end;

						if driveSymbol.name == "FOUR_WHEEL" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, fourWheel);
							else
								if fourWheel then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "DIFF_LOCK_FRONT" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, diffLockFront);
							else
								if diffLockFront then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "DIFF_LOCK_BACK" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, diffLockBack);
							else
								if diffLockBack then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "PTO_FRONT" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, isPTOFrontActive);
							else
								if isPTOFrontActive then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "PTO_BACK" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, isPTOBackActive);
							else
								if isPTOBackActive then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "TIPPING_ACTIVE" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, trailerIsTippingActive);
							else
								if trailerIsTippingActive then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "TIPPING_INACTIVE" then
							if driveSymbol.changeVisibility then
								if not trailerIsTippingActive then
									setVisibility(driveSymbol.node, true);
								else
									setVisibility(driveSymbol.node, false);
								end;
							else
								if not trailerIsTippingActive then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "TOOL_FRONT_LOWERED" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, moveDownFront);
							else
								if moveDownFront then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);	
								end;
							end;

						elseif driveSymbol.name == "TOOL_BACK_LOWERED" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, moveDownBack);
							else
								if moveDownBack then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);	
								end;
							end;

						elseif driveSymbol.name == "TOOL_FRONT_LIFTED" then
							if driveSymbol.changeVisibility then
								if not moveDownFront then
									setVisibility(driveSymbol.node, true);
								else
									setVisibility(driveSymbol.node, false);
								end;
							else
								if not moveDownFront then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);	
								end;
							end;

						elseif driveSymbol.name == "TOOL_BACK_LIFTED" then
							if driveSymbol.changeVisibility then
								if not moveDownBack then
									setVisibility(driveSymbol.node, true);
								else
									setVisibility(driveSymbol.node, false);
								end;
							else
								if not moveDownBack then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);	
								end;
							end;

						elseif driveSymbol.name == "HANDBRAKE" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, handbrakeIsActive);
							else
								if handbrakeIsActive then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "CONTROL_LAMP" then
							if driveSymbol.currentTime < driveSymbol.displayTime then 
								setVisibility(driveSymbol.node, true);
							end;

							if driveSymbol.currentTime < (driveSymbol.displayTime + driveSymbol.displayTime) and driveSymbol.currentTime > driveSymbol.displayTime then 
								setVisibility(driveSymbol.node, false);
							end;

							driveSymbol.currentTime = driveSymbol.currentTime + dt;
						
						elseif driveSymbol.name == "BALE_WRAPPER_ACTIVE" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, baleWrapperIsActive);
							else
								if baleWrapperIsActive then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "BALE_DROP_STATE_MANUAL" then
							if driveSymbol.changeVisibility then
								if not currentDropModeStateIsAutomatic and not currentDropModeStateIsCollect then
									setVisibility(driveSymbol.node, true);
								else
									setVisibility(driveSymbol.node, false);
								end;
							else
								if not currentDropModeStateIsAutomatic and not currentDropModeStateIsCollect then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "BALE_DROP_STATE_AUTOMATIC" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, currentDropModeStateIsAutomatic);
							else
								if currentDropModeStateIsAutomatic then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "BALE_DROP_STATE_COLLECT" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, currentDropModeStateIsCollect);
							else
								if currentDropModeStateIsCollect then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "LIFT_AXLE_STATE_DOWN" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, liftAxleIsDown);
							else
								if liftAxleIsDown then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "LIFT_AXLE_STATE_UP" then
							if driveSymbol.changeVisibility then
								if not liftAxleIsDown then
									setVisibility(driveSymbol.node, true);
								else
									setVisibility(driveSymbol.node, false);
								end;
							else
								if not liftAxleIsDown then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "GPS_STEERING_ACTIVE" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, gpsSteeringIsActive);
							else
								if gpsSteeringIsActive then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "GPS_ACTIVE" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, gpsIsActive);
							else
								if gpsIsActive then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "GPS_LANE_PLUS" then
							setVisibility(driveSymbol.node, gpsCurrentLane > 0);

						elseif driveSymbol.name == "GPS_LANE_MINUS" then
							setVisibility(driveSymbol.node, gpsCurrentLane < 0);
						
						elseif driveSymbol.name == "STEERAXLE_IS_LOCKED" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, steerAxleIsLocked and trailerHasSteerAxle);
							else
								if trailerHasSteerAxle then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								else
									setShaderParameter(driveSymbol.node, "baseColor", 0, 0, 0, 0, false);
								end

								if steerAxleIsLocked then
									setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.baseColor[1], driveSymbol.baseColor[2], driveSymbol.baseColor[3], driveSymbol.baseColor[4], false);
								else 
									setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.emitColor[1], driveSymbol.emitColor[2], driveSymbol.emitColor[3], driveSymbol.emitColor[4], false);
								end;
							end;
						
						elseif driveSymbol.name == "STEERAXLE_IS_UNLOCKED" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, not steerAxleIsLocked or not trailerHasSteerAxle);
							else
								if not steerAxleIsLocked then
									setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.baseColor[1], driveSymbol.baseColor[2], driveSymbol.baseColor[3], driveSymbol.baseColor[4], false);
								else 
									setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.emitColor[1], driveSymbol.emitColor[2], driveSymbol.emitColor[3], driveSymbol.emitColor[4], false);
								end;
							end;

						elseif driveSymbol.name == "RIDGE_MARKER_LEFT" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, ridgeMarkerState == 1);
							else
								if ridgeMarkerState == 1 then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "RIDGE_MARKER_RIGHT" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, ridgeMarkerState == 2);
							else
								if ridgeMarkerState == 2 then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "TIPPING_ACTIVE_TRUCK" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, truckIsTipping);
							else
								if truckIsTipping then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "TOOL_FRONT_IS_UNFOLDED" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, toolFrontIsUnfolded and hasFoldablePartsFront);
							else
								if toolFrontIsUnfolded and hasFoldablePartsFront then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "TOOL_FRONT_IS_FOLDED" then
							if driveSymbol.changeVisibility then
								if not toolFrontIsUnfolded then
									setVisibility(driveSymbol.node, hasFoldablePartsFront);
								else
									setVisibility(driveSymbol.node, false);
								end;
							else
								if not toolFrontIsUnfolded and hasFoldablePartsFront then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "TOOL_BACK_IS_UNFOLDED" then
							if driveSymbol.changeVisibility then
								if toolBackIsUnfolded then
									setVisibility(driveSymbol.node, hasFoldablePartsBack);
								else
									setVisibility(driveSymbol.node, false);
								end;
							else
								if toolBackIsUnfolded and hasFoldablePartsBack then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "TOOL_BACK_IS_FOLDED" then
							if driveSymbol.changeVisibility then
								if not toolBackIsUnfolded then
									setVisibility(driveSymbol.node, hasFoldablePartsBack);
								else
									setVisibility(driveSymbol.node, false);
								end;
							else
								if not toolBackIsUnfolded and hasFoldablePartsBack then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "ABS_TRUCK" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, isTruckABSActive);
							else
								if isTruckABSActive then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "ABS_TRAILER" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, isTruckABSActive and numberOfAttachedTrailers >= 1);
							else
								if isTruckABSActive and numberOfAttachedTrailers >= 1 then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "WEARED_OUT_BRAKES" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, brakesWearedOut);
							else
								if brakesWearedOut then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "WARNING_BRAKE_COMPRESSOR_FILL_LEVEL_TO_LOW" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, airCompressorDoRefill);
							else
								if airCompressorDoRefill then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						
						elseif driveSymbol.name == "WARNING_WORKSHOP_1" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, warningWorkshop[1]);
							else
								if warningWorkshop[1] then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "WARNING_WORKSHOP_2" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, warningWorkshop[2]);
							else
								if warningWorkshop[2] then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "STOP_IMMINENTLY" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, stopImminently);
							else
								if stopImminently then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "MOTOR_NEED_MAINTRACE" then
							if motorNeedMaintrace then
								setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							end;

							setVisibility(driveSymbol.node, motorNeedMaintrace);
						
						elseif driveSymbol.name == "LOW_OIL_PRESSURE" then
							if lowOilPressure then
								setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							end;

							setVisibility(driveSymbol.node, lowOilPressure);

						elseif driveSymbol.name == "OIL_FILL_LEVEL_TO_LOW" then
							if oilFillLevelToLow then
								setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							end;

							setVisibility(driveSymbol.node, oilFillLevelToLow);

						elseif driveSymbol.name == "VEHICLE_SELECTION" then
							setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.baseColor[1], driveSymbol.baseColor[2], driveSymbol.baseColor[3], driveSymbol.baseColor[4], false);

							if drivableIsSelected then
								setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.emitColor[1], driveSymbol.emitColor[2], driveSymbol.emitColor[3], driveSymbol.emitColor[4], false);
							end;

							if vehicleSchemaIsTurnedOn[1] then
								local color = AnimatedIndoorPartsUtil.getSchemaColor(drivableIsSelected);

								setShaderParameter(driveSymbol.node, "emitColor", color[1], color[2], color[3], 1, false);
							end;

						elseif driveSymbol.name == "TOOL_FRONT_SELECTION_1" then
							setVisibility(driveSymbol.node, toolFrontIsAttached1);
							
							setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.baseColor[1], driveSymbol.baseColor[2], driveSymbol.baseColor[3], driveSymbol.baseColor[4], false);

							if toolFrontIsSelected1 then
								setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.emitColor[1], driveSymbol.emitColor[2], driveSymbol.emitColor[3], driveSymbol.emitColor[4], false);
							end;

							if vehicleSchemaIsTurnedOn[2] then
								local color = AnimatedIndoorPartsUtil.getSchemaColor(toolFrontIsSelected1);

								setShaderParameter(driveSymbol.node, "emitColor", color[1], color[2], color[3], 1, false);
							end;

						elseif driveSymbol.name == "TOOL_FRONT_SELECTION_2" then
							setVisibility(driveSymbol.node, toolFrontIsAttached2);
							
							if toolFrontIsSelected2 then
								setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.emitColor[1], driveSymbol.emitColor[2], driveSymbol.emitColor[3], driveSymbol.emitColor[4], false);
							end;

							setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);

						elseif driveSymbol.name == "TOOL_BACK_SELECTION_1" then
							setVisibility(driveSymbol.node, toolBackIsAttached1);
							
							setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.baseColor[1], driveSymbol.baseColor[2], driveSymbol.baseColor[3], driveSymbol.baseColor[4], false);

							if toolBackIsSelected1 then
								setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.emitColor[1], driveSymbol.emitColor[2], driveSymbol.emitColor[3], driveSymbol.emitColor[4], false);
							end;

							if vehicleSchemaIsTurnedOn[3] then
								local color = AnimatedIndoorPartsUtil.getSchemaColor(toolBackIsSelected1);

								setShaderParameter(driveSymbol.node, "emitColor", color[1], color[2], color[3], 1, false);
							end;

						elseif driveSymbol.name == "TOOL_BACK_SELECTION_2" then
							setVisibility(driveSymbol.node, toolBackIsAttached2);
							
							setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.baseColor[1], driveSymbol.baseColor[2], driveSymbol.baseColor[3], driveSymbol.baseColor[4], false);

							if toolBackIsSelected2 then
								setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.emitColor[1], driveSymbol.emitColor[2], driveSymbol.emitColor[3], driveSymbol.emitColor[4], false);
							end;

							if vehicleSchemaIsTurnedOn[4] then
								local color = AnimatedIndoorPartsUtil.getSchemaColor(toolBackIsSelected2);

								setShaderParameter(driveSymbol.node, "emitColor", color[1], color[2], color[3], 1, false);
							end;
						elseif driveSymbol.name == "FRONTLOADER_SELECTION" then
							setVisibility(driveSymbol.node, frontloaderIsAttached);
							
							setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.baseColor[1], driveSymbol.baseColor[2], driveSymbol.baseColor[3], driveSymbol.baseColor[4], false);

							if frontloaderIsSelected then
								setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.emitColor[1], driveSymbol.emitColor[2], driveSymbol.emitColor[3], driveSymbol.emitColor[4], false);
							end;

						elseif driveSymbol.name == "FRONTLOADER_TOOL_SELECTION" then
							setVisibility(driveSymbol.node, frontloaderToolIsAttached);
							
							setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.baseColor[1], driveSymbol.baseColor[2], driveSymbol.baseColor[3], driveSymbol.baseColor[4], false);

							if frontloaderToolIsSelected then
								setShaderParameter(driveSymbol.node, "emitColor", driveSymbol.emitColor[1], driveSymbol.emitColor[2], driveSymbol.emitColor[3], driveSymbol.emitColor[4], false);
							end;

							if vehicleSchemaIsTurnedOn[5] then
								local color = AnimatedIndoorPartsUtil.getSchemaColor(frontloaderToolIsSelected);

								setShaderParameter(driveSymbol.node, "emitColor", color[1], color[2], color[3], 1, false);
							end;

						elseif driveSymbol.name == "STRAW_CHOPPER" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, isStrawChopperActive);
							else
								if isStrawChopperActive then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "OVERLOADING_ACTIVE" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, isOverloading);
							else
								if isOverloading then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "GRAIN_TANK_UNFOLDED" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, isGrainTankUnfolded);
							else
								if isGrainTankUnfolded then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "PIPE_UNFOLDED" then
							if driveSymbol.changeVisibility then
								setVisibility(driveSymbol.node, isPipeUnfolded);
							else
								if isPipeUnfolded then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;

						elseif driveSymbol.name == "RIDGE_MARKER_LEFT_UP" then
							setVisibility(driveSymbol.node, ridgeMarkerState ~= 1);
						
						elseif driveSymbol.name == "RIDGE_MARKER_RIGHT_UP" then
							setVisibility(driveSymbol.node, ridgeMarkerState ~= 2);
						
						elseif driveSymbol.name == "RIDGE_MARKER_LEFT_DOWN" then
							setVisibility(driveSymbol.node, ridgeMarkerState == 1);

						elseif driveSymbol.name == "RIDGE_MARKER_RIGHT_DOWN" then
							setVisibility(driveSymbol.node, ridgeMarkerState == 2);	

						elseif driveSymbol.name == "CARDINAL_DIRECTION_NORTH" then
							setVisibility(driveSymbol.node, specAnimatedIndoorParts.cardinalDirection >= 0 and (specAnimatedIndoorParts.cardinalDirection < 50 or specAnimatedIndoorParts.cardinalDirection > 355));

						elseif driveSymbol.name == "CARDINAL_DIRECTION_NORTH_EAST" then
							setVisibility(driveSymbol.node, specAnimatedIndoorParts.cardinalDirection >= 50 and specAnimatedIndoorParts.cardinalDirection < 85);

						elseif driveSymbol.name == "CARDINAL_DIRECTION_EAST" then
							setVisibility(driveSymbol.node, specAnimatedIndoorParts.cardinalDirection >= 85 and specAnimatedIndoorParts.cardinalDirection < 125);

						elseif driveSymbol.name == "CARDINAL_DIRECTION_SOUTH_EAST" then
							setVisibility(driveSymbol.node, specAnimatedIndoorParts.cardinalDirection >= 125 and specAnimatedIndoorParts.cardinalDirection < 165);

						elseif driveSymbol.name == "CARDINAL_DIRECTION_SOUTH" then
							setVisibility(driveSymbol.node, specAnimatedIndoorParts.cardinalDirection >= 165 and specAnimatedIndoorParts.cardinalDirection < 205);

						elseif driveSymbol.name == "CARDINAL_DIRECTION_SOUTH_WEST" then
							setVisibility(driveSymbol.node, specAnimatedIndoorParts.cardinalDirection >= 205 and specAnimatedIndoorParts.cardinalDirection < 245);

						elseif driveSymbol.name == "CARDINAL_DIRECTION_WEST" then
							setVisibility(driveSymbol.node, specAnimatedIndoorParts.cardinalDirection >= 245 and specAnimatedIndoorParts.cardinalDirection < 295);

						elseif driveSymbol.name == "CARDINAL_DIRECTION_NORTH_WEST" then
							setVisibility(driveSymbol.node, specAnimatedIndoorParts.cardinalDirection >= 295 and specAnimatedIndoorParts.cardinalDirection < 355);

						elseif driveSymbol.name == "CARDINAL_DIRECTION_SIGN" then
							setVisibility(driveSymbol.node, true);

						elseif driveSymbol.name == "NO_FIELD" then
							setVisibility(driveSymbol.node, specAnimatedIndoorParts.currentFieldNumber == 0);

						elseif driveSymbol.name == "PS_MODE_AUTO" then
							setVisibility(driveSymbol.node, currentModeIsAuto);

						elseif driveSymbol.name == "PS_MODE_SEMI" then
							setVisibility(driveSymbol.node, currentModeIsSemi);

						elseif driveSymbol.name == "PS_MODE_MANUAL" then
							setVisibility(driveSymbol.node, currentModeIsManual);

						elseif driveSymbol.name == "PS_LANE_LEFT" then
							setVisibility(driveSymbol.node, true);

							local currentLightControl = 0;

							if currentShutoffModeIsRight then
								currentLightControl = driveSymbol.intensity;
							end;

							setShaderParameter(driveSymbol.node, "lightControl", currentLightControl, 0, 0, 0, false);

						elseif driveSymbol.name == "PS_LANE_RIGHT" then
							setVisibility(driveSymbol.node, true);

							local currentLightControl = 0;

							if currentShutoffModeIsLeft then
								currentLightControl = driveSymbol.intensity;
							end;

							setShaderParameter(driveSymbol.node, "lightControl", currentLightControl, 0, 0, 0, false);

						elseif driveSymbol.name == "PS_TRAM_LANE_LEFT" then
							setVisibility(driveSymbol.node, true);

							local currentLightControl = 0;

							if createTramLines or currentShutoffModeIsRight then
								currentLightControl = driveSymbol.intensity;
							end;

							setShaderParameter(driveSymbol.node, "lightControl", currentLightControl, 0, 0, 0, false);

						elseif driveSymbol.name == "PS_TRAM_LANE_RIGHT" then
							setVisibility(driveSymbol.node, true);

							local currentLightControl = 0;

							if createTramLines or currentShutoffModeIsLeft then
								currentLightControl = driveSymbol.intensity;
							end;

							setShaderParameter(driveSymbol.node, "lightControl", currentLightControl, 0, 0, 0, false);
						end;

						for trailerNumber = 1, 2 do
							if driveSymbol.name == "TIPPING_SIDE_LEFT_" .. trailerNumber then
								setVisibility(driveSymbol.node, isLeftTipping[trailerNumber]);

								if isLeftTipping[trailerNumber] then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							
							elseif driveSymbol.name == "TIPPING_SIDE_RIGHT_" .. trailerNumber then
								setVisibility(driveSymbol.node, isRightTipping[trailerNumber]);

								if isRightTipping[trailerNumber] then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							
							elseif driveSymbol.name == "TIPPING_SIDE_BACK_" .. trailerNumber then
								setVisibility(driveSymbol.node, isBackTipping[trailerNumber]);

								if isBackTipping[trailerNumber] then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;

							elseif driveSymbol.name == "TIPPING_SIDE_GRAINDOOR_" .. trailerNumber then
								setVisibility(driveSymbol.node, isGrainDoorTipping[trailerNumber]);

								if isGrainDoorTipping[trailerNumber] then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;

							elseif driveSymbol.name == "TRAILER_" .. trailerNumber then
								if numberOfAttachedTrailers >= trailerNumber then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;

							elseif driveSymbol.name == "TIPPING_ACTIVE_TRAILER_" .. trailerNumber then
								setVisibility(driveSymbol.node, numberOfAttachedTrailers >= trailerNumber and trailerIsTipping[trailerNumber]);
								
								if numberOfAttachedTrailers >= trailerNumber and trailerIsTipping[trailerNumber] then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							
							elseif driveSymbol.name == "TIPPING_INACTIVE_TRAILER_" .. trailerNumber then
								if not trailerIsTipping[trailerNumber] then
									setVisibility(driveSymbol.node, true);
								else
									setVisibility(driveSymbol.node, false);
								end;
								
								if not trailerIsTipping[trailerNumber] then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						end;
						
						local intensity = 0;
	
						local turnLightDirections = {
							"ANY",
							"LEFT",
							"RIGHT"
						};
	
						for _, turnLightDirection in pairs(turnLightDirections) do
							for trailerNumber = 1, 2 do
								if driveSymbol.name == "TURN_LIGHT_TRAILER_" .. turnLightDirection .. "_" .. trailerNumber then
									if numberOfAttachedTrailersWithTurnLights >= trailerNumber then		
										if turnLightDirection == "ANY" then	
											if self.spec_lights.turnLightState ~= Lights.TURNLIGHT_OFF then
												intensity = driveSymbol.intensity;
											end;
										else
											if self.spec_lights.turnLightState == Lights["TURNLIGHT_" .. turnLightDirection] or self.spec_lights.turnLightState == Lights.TURNLIGHT_HAZARD then
												intensity = driveSymbol.intensity;
											end;
										end;
									end;
								
									setShaderParameter(driveSymbol.node, "lightControl", intensity, 0, 0, 0, false);
								end;
							end;
						end;
					else
						if driveSymbol.name ~= "DISABLE_ON_MOTOR_OFF" then	
							setVisibility(driveSymbol.node, not driveSymbol.disableOnMotorOff);
						end;

						if driveSymbol.name == "CONTROL_LAMP" then
							driveSymbol.currentTime = driveSymbol.intensity;
						end;
						
						if getHasClassId(driveSymbol.node, ClassIds.SHAPE) and getHasShaderParameter(driveSymbol.node, "lightControl") then
							setShaderParameter(driveSymbol.node, "lightControl", 0, 0, 0, 0, false);
						end;
					end;
				else
					if getVisibility(driveSymbol.node) then
						setVisibility(driveSymbol.node, false);
					end;
				end;
			end;
		end;

		if specAnimatedIndoorParts.animations ~= nil then
			for _, animation in pairs(specAnimatedIndoorParts.animations) do
				if animation.name == "TOOL_FRONT" then
					if self:getAnimationExists(animation.lowerAnimationName)
						and self:getAnimationExists(animation.liftAnimationName)
						and not (self:getIsAnimationPlaying(animation.liftAnimationName) or self:getIsAnimationPlaying(animation.lowerAnimationName))
					then
						local animationName = animation.liftAnimationName;
					
						if not moveDownFront then
							animationName = animation.lowerAnimationName;
						end;

						self:playAnimation(animationName, 1, nil, true);
					end;
				elseif animation.name == "TOOL_BACK" then
					if self:getAnimationExists(animation.lowerAnimationName)
						and self:getAnimationExists(animation.liftAnimationName)
						and not (self:getIsAnimationPlaying(animation.liftAnimationName) or self:getIsAnimationPlaying(animation.lowerAnimationName))
					then
						local animationName = animation.liftAnimationName;
					
						if not moveDownBack then
							animationName = animation.lowerAnimationName;
						end;

						self:playAnimation(animationName, 1, nil, true);
					end;
				elseif animation.name == "PTO_FRONT" then
					if self:getAnimationExists(animation.lowerAnimationName)
						and self:getAnimationExists(animation.liftAnimationName)
						and not (self:getIsAnimationPlaying(animation.liftAnimationName) or self:getIsAnimationPlaying(animation.lowerAnimationName))
					then
						local animationName = animation.liftAnimationName;
					
						if not isPTOFrontActive then
							animationName = animation.lowerAnimationName;
						end;

						self:playAnimation(animationName, 1, nil, true);
					end;
				elseif animation.name == "PTO_BACK" then 
					if self:getAnimationExists(animation.lowerAnimationName)
						and self:getAnimationExists(animation.liftAnimationName)
						and not (self:getIsAnimationPlaying(animation.liftAnimationName) or self:getIsAnimationPlaying(animation.lowerAnimationName))
					then
						local animationName = animation.liftAnimationName;
					
						if not isPTOBackActive then
							animationName = animation.lowerAnimationName;
						end;

						self:playAnimation(animationName, 1, nil, true);
					end;
				end;
			end;
		end;
	end;
end;

function AnimatedIndoorParts:getFillLevelPercentSowingMachine()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local percentage = 0;
	local fillLevel = 0;
	local capacity = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		--## tool 1
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
		local specSowingMachine = AnimatedIndoorPartsUtil.getSpecByName(object, "sowingMachine");
		
		if specFillUnit ~= nil and specSowingMachine ~= nil then
			for _, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					fillLevel = fillLevel + fillUnit.fillLevel;
					capacity = capacity + fillUnit.capacity;
				end;
			end;
		end;

		local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			--## tool 2
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			local specSowingMachine = AnimatedIndoorPartsUtil.getSpecByName(object, "sowingMachine");
	
			if specFillUnit ~= nil and specSowingMachine ~= nil then
				for _, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						fillLevel = fillLevel + fillUnit.fillLevel;
						capacity = capacity + fillUnit.capacity;
					end;
				end;
			end;

			local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

			for attachedImplement = 1, #specAttacherJoints.attachedImplements do
				--## tool 3
				local object = specAttacherJoints.attachedImplements[attachedImplement].object;
				local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
				local specSowingMachine = AnimatedIndoorPartsUtil.getSpecByName(object, "sowingMachine");
			
				if specFillUnit ~= nil and specSowingMachine ~= nil then
					for _, fillUnit in pairs(specFillUnit.fillUnits) do
						if fillUnit.showOnHud then	
							fillLevel = fillLevel + fillUnit.fillLevel;
							capacity = capacity + fillUnit.capacity;
						end;
					end;
				end;
			end;
		end;

		if fillLevel > 0 and capacity > 0 then
			percentage = math.floor((fillLevel / capacity) * 100);
		end;
	end;

	return percentage;
end;

function AnimatedIndoorParts:getSeedFillLevel()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
		local specSowingMachine = AnimatedIndoorPartsUtil.getSpecByName(object, "sowingMachine");
		
		if specFillUnit ~= nil then --and specSowingMachine ~= nil then
			for _, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					if string.find(g_fillTypeManager:getFillTypeNameByIndex(fillUnit.fillType), "SEED") then	
						fillLevel = fillLevel + fillUnit.fillLevel;
					end;
				end;
			end;
		end;

		local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			local specSowingMachine = AnimatedIndoorPartsUtil.getSpecByName(object, "sowingMachine");
		
			if specFillUnit ~= nil then --and specSowingMachine ~= nil then
				for _, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						if string.find(g_fillTypeManager:getFillTypeNameByIndex(fillUnit.fillType), "SEED") then	
							fillLevel = fillLevel + fillUnit.fillLevel;
						end;
					end;
				end;
			end;
		end;
	end;

	if fillLevel >= 1000 then
		fillLevel = AnimatedIndoorPartsUtil.formatNumber(fillLevel);
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getFertilizerFillLevel()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;

	local fillTypeTable = {
		"FERTILIZER",
		"LIQUIDFERTILIZER",
		"LIME",
		"HERBICIDE",
	};
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
		
		if specFillUnit ~= nil then
			for _, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					for _, fillType in pairs(fillTypeTable) do	
						if g_fillTypeManager:getFillTypeNameByIndex(fillUnit.fillType)  == fillType then	
							fillLevel = fillLevel + fillUnit.fillLevel;

							break;
						end;
					end;
				end;
			end;
		end;

		local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			
			if specFillUnit ~= nil then
				for _, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						for _, fillType in pairs(fillTypeTable) do	
							if g_fillTypeManager:getFillTypeNameByIndex(fillUnit.fillType)  == fillType then	
								fillLevel = fillLevel + fillUnit.fillLevel;

								break;
							end;
						end;
					end;
				end;
			end;
		end;
	end;

	if fillLevel >= 1000 then
		fillLevel = AnimatedIndoorPartsUtil.formatNumber(fillLevel);
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getFillLevelPercentFillType(fillTypeName)
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local percentage = 0;
	local fillLevel = 0;
	local capacity = 0; 

	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
	
		if specFillUnit ~= nil then
			for _, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					if string.find(g_fillTypeManager:getFillTypeNameByIndex(fillUnit.fillType), fillTypeName) then	
						fillLevel = fillLevel + fillUnit.fillLevel;
						capacity = capacity + fillUnit.capacity;
					end;
				end;
			end;
		end;

		local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			
			if specFillUnit ~= nil then
				for _, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						if string.find(g_fillTypeManager:getFillTypeNameByIndex(fillUnit.fillType), fillTypeName) then	
							fillLevel = fillLevel + fillUnit.fillLevel;
							capacity = capacity + fillUnit.capacity;
						end;
					end;
				end;
			end;
		end;

		if fillLevel > 0 and capacity > 0 then
			percentage = math.floor((fillLevel / capacity) * 100);
		end;
	end;

	return percentage;
end;

function AnimatedIndoorParts:getFillLevelFillType(fillTypeName)
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;
	
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
	
		if specFillUnit ~= nil then
			for _, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					if string.find(g_fillTypeManager:getFillTypeNameByIndex(fillUnit.fillType), fillTypeName) then	
						fillLevel = fillLevel + fillUnit.fillLevel;
					end;
				end;
			end;
		end;

		local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			
			if specFillUnit ~= nil then
				for _, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						if string.find(g_fillTypeManager:getFillTypeNameByIndex(fillUnit.fillType), fillTypeName) then	
							fillLevel = fillLevel + fillUnit.fillLevel;
						end;
					end;
				end;
			end;
		end;
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getFillLevelPercentTotal()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local percentage = 0;
	local fillLevel = 0;
	local capacity = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
		local isStrawHarvestMachine = string.find(object.configFileName, "strawHarvest/vehicles/krone");
		local isUnSupportetFillType = false;

		if specFillUnit ~= nil then
			for _, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
						fillLevel = fillLevel + fillUnit.fillLevel;
						capacity = capacity + fillUnit.capacity;
					end;
				end;
			end;
		end;

		local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			
			if specFillUnit ~= nil then
				for _, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
							fillLevel = fillLevel + fillUnit.fillLevel;
							capacity = capacity + fillUnit.capacity;
						end;
					end;
				end;
			end;
		end;

		if fillLevel > 0 and capacity > 0 then
			percentage = math.floor((fillLevel / capacity) * 100);
		end;
	end;

	return percentage;
end;

function AnimatedIndoorParts:getFillLevelTotal()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
		
		if specFillUnit ~= nil then
			for _, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
						fillLevel = fillLevel + fillUnit.fillLevel;
					end;
				end;
			end;
		end;

		local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");
			
		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			
			if specFillUnit ~= nil then
				for _, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
							fillLevel = fillLevel + fillUnit.fillLevel;
						end;
					end;
				end;
			end;
		end;
	end;

	if fillLevel >= 1000 then
		fillLevel = AnimatedIndoorPartsUtil.formatNumber(fillLevel);
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getFillLevelPercentTrailer1()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local percentage = 0;
	local fillLevel = 0;
	local capacity = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
		
		if specFillUnit ~= nil then
			for _, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
						fillLevel = fillLevel + fillUnit.fillLevel;
						capacity = capacity + fillUnit.capacity;
					end;
				end;
			end;
		end;
		
		if fillLevel > 0 and capacity > 0 then
			percentage = math.floor((fillLevel / capacity) * 100);
		end;
	end;

	return percentage;
end;

function AnimatedIndoorParts:getFillLevelTrailer1()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
		
		if specFillUnit ~= nil then
			for _, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
						fillLevel = fillLevel + fillUnit.fillLevel;
					end;
				end;
			end;
		end;
	end;

	if fillLevel >= 1000 then
		fillLevel = AnimatedIndoorPartsUtil.formatNumber(fillLevel);
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getCurrentFilledTrailerFillLevelPercent()
	local specPipe = AnimatedIndoorPartsUtil.getSpecByName(self, "pipe");
	local percentage = 0;
	local fillLevel = 0;
	local capacity = 0;
		
	if specPipe ~= nil then
		local dischargeNode = self:getDischargeNodeByIndex(self:getPipeDischargeNodeIndex())
		
		if dischargeNode ~= nil then
			local currentFilledTrailer = AnimatedIndoorPartsUtil.getCurrentFilledTrailer(self, dischargeNode, specPipe);
		
			if currentFilledTrailer ~= nil then
				local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(currentFilledTrailer, "fillUnit");
				
				if specFillUnit ~= nil then
					local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(currentFilledTrailer, "attacherJoints");

					for _, fillUnit in ipairs(specFillUnit.fillUnits) do
						if fillUnit.showOnHud then	
							fillLevel = fillLevel + fillUnit.fillLevel;
							capacity = capacity + fillUnit.capacity;
						end;
					end;

					for attachedImplement = 1, #specAttacherJoints.attachedImplements do
						local object = specAttacherJoints.attachedImplements[attachedImplement].object;
						local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");

						if specFillUnit ~= nil then
							for _, fillUnit in ipairs(specFillUnit.fillUnits) do
								if fillUnit.showOnHud then	
									fillLevel = fillLevel + fillUnit.fillLevel;
									capacity = capacity + fillUnit.capacity;
								end;
							end;
						end;
					end;

					if fillLevel > 0 and capacity > 0 then
						percentage = math.floor((fillLevel / capacity) * 100);
					end;
				end;
			end;
		end;
	end;

	return percentage;
end;

function AnimatedIndoorParts:getCurrentFilledTrailerFillLevel()
	local specPipe = AnimatedIndoorPartsUtil.getSpecByName(self, "pipe");
	local fillLevel = 0;
		
	if specPipe ~= nil then
		local dischargeNode = self:getDischargeNodeByIndex(self:getPipeDischargeNodeIndex())
		
		if dischargeNode ~= nil then
			local currentFilledTrailer = AnimatedIndoorPartsUtil.getCurrentFilledTrailer(self, dischargeNode, specPipe);
		
			if currentFilledTrailer ~= nil then
				local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(currentFilledTrailer, "fillUnit");
				local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(currentFilledTrailer, "attacherJoints");

				if specFillUnit ~= nil then
					for _, fillUnit in ipairs(specFillUnit.fillUnits) do
						if fillUnit.showOnHud then	
							fillLevel = fillLevel + fillUnit.fillLevel;
						end;
					end;

					for attachedImplement = 1, #specAttacherJoints.attachedImplements do
						local object = specAttacherJoints.attachedImplements[attachedImplement].object;
						local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");

						if specFillUnit ~= nil then
							for _, fillUnit in ipairs(specFillUnit.fillUnits) do
								if fillUnit.showOnHud then	
									fillLevel = fillLevel + fillUnit.fillLevel;
								end;
							end;
						end;
					end;
				end;
			end;
		end;
	end;

	if fillLevel >= 1000 then
		fillLevel = AnimatedIndoorPartsUtil.formatNumber(fillLevel);
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getFillLevelPercentTrailer2()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local percentage = 0;
	local fillLevel = 0;
	local capacity = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			
			if specFillUnit ~= nil then
				for _, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
							fillLevel = fillLevel + fillUnit.fillLevel;
							capacity = capacity + fillUnit.capacity;
						end;
					end;
				end;
			end;
		end;

		if fillLevel > 0 and capacity > 0 then
			percentage = math.floor((fillLevel / capacity) * 100);
		end;
	end;

	return percentage;
end;

function AnimatedIndoorParts:getFillLevelTrailer2()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");
			
		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			
			if specFillUnit ~= nil then
				for _, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
							fillLevel = fillLevel + fillUnit.fillLevel;
						end;
					end;
				end;
			end;
		end;
	end;

	if fillLevel >= 1000 then
		fillLevel = AnimatedIndoorPartsUtil.formatNumber(fillLevel);
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getFillWeightTrailer1()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local fillWeight = {0, 0};
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
		
		if specFillUnit ~= nil then
			for fillUnitNumber, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
						fillWeight[fillUnitNumber] = fillUnit.fillLevel * g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).massPerLiter * 1000;
					end;
				end;
			end;
		end;
	end;

	local totalWeight = fillWeight[1] + fillWeight[2];

	if totalWeight >= 1000 then
		totalWeight = AnimatedIndoorPartsUtil.formatNumber(totalWeight);
	end;

	return totalWeight;
end;

function AnimatedIndoorParts:getFillWeightTrailer2()
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local fillWeight = {0, 0};
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");
			
		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(object, "fillUnit");
			
			if specFillUnit ~= nil then
				for fillUnitNumber, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
							fillWeight[fillUnitNumber] = fillUnit.fillLevel * g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).massPerLiter * 1000;
						end;
					end;
				end;
			end;
		end;
	end;

	local totalWeight = fillWeight[1] + fillWeight[2];

	if totalWeight >= 1000 then
		totalWeight = AnimatedIndoorPartsUtil.formatNumber(totalWeight);
	end;

	return totalWeight;
end;

function AnimatedIndoorParts:getFillLevelCombine()
	local fillLevel = 0;
		
	local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(self, "fillUnit");

	if specFillUnit ~= nil then
		for fillUnitNumber, fillUnit in pairs(specFillUnit.fillUnits) do
			if fillUnit.showOnHud then
				if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
					fillLevel = fillLevel + fillUnit.fillLevel;
				end;
			end;
		end;
	end;

	if fillLevel >= 1000 then
		fillLevel = AnimatedIndoorPartsUtil.formatNumber(fillLevel);
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getFillLevelPercentCombine()
	local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(self, "fillUnit");
	local percentage = 0;
	local fillLevel = 0;
	local capacity = 0;
		
	if specFillUnit ~= nil then
		for _, fillUnit in pairs(specFillUnit.fillUnits) do
			if fillUnit.showOnHud then
				if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
					fillLevel = fillLevel + fillUnit.fillLevel;
					capacity = capacity + fillUnit.capacity;
				end;
			end;
		end;

		if fillLevel > 0 and capacity > 0 then
			percentage = math.floor((fillLevel / capacity) * 100);
		end;
	end;

	return percentage;
end;

function AnimatedIndoorParts:getFillWeightCombine()
	local fillWeight = 0;
		
	local specFillUnit = AnimatedIndoorPartsUtil.getSpecByName(self, "fillUnit");

	if specFillUnit ~= nil then
		for fillUnitNumber, fillUnit in pairs(specFillUnit.fillUnits) do
			if fillUnit.showOnHud then
				if not AnimatedIndoorPartsUtil.getIsSupportetValue(AnimatedIndoorPartsUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name) then	
					fillWeight = fillUnit.fillLevel * g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).massPerLiter * 1000;
				end;
			end;
		end;
	end;

	if fillWeight >= 1000 then
		fillWeight = AnimatedIndoorPartsUtil.formatNumber(fillWeight);
	end;

	return fillWeight;
end;

function AnimatedIndoorParts:getGpsWorkingWidth()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specGlobalPositioningSystem = AnimatedIndoorPartsUtil.getSpecByName(self, "globalPositioningSystem", specAnimatedIndoorParts.gpsModName);
	local gpsWorkingWidth = 0;

	if specGlobalPositioningSystem ~= nil and specGlobalPositioningSystem.guidanceData ~= nil then
		gpsWorkingWidth = specGlobalPositioningSystem.guidanceData.width;
	end;

	return gpsWorkingWidth;
end;

function AnimatedIndoorParts:getGpsCurrentLane()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specGlobalPositioningSystem = AnimatedIndoorPartsUtil.getSpecByName(self, "globalPositioningSystem", specAnimatedIndoorParts.gpsModName);
	local currentLane = 0;

	if specGlobalPositioningSystem ~= nil and specGlobalPositioningSystem.guidanceData ~= nil then
		currentLane = specGlobalPositioningSystem.guidanceData.currentLane;

		if currentLane < 0 then
			currentLane = -currentLane;
		end;
	end;

	if currentLane >= 1000 then
		currentLane = AnimatedIndoorPartsUtil.formatNumber(currentLane);
	end;

	return currentLane;
end;

function AnimatedIndoorParts:getCurrentBaleCount()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local currentBaleCount = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specBaleCounter = AnimatedIndoorPartsUtil.getSpecByName(object, "baleCounter");

		if specBaleCounter ~= nil then
			currentBaleCount = specBaleCounter.countToday;
		end;
	end;

	if currentBaleCount >= 1000 then
		currentBaleCount = AnimatedIndoorPartsUtil.formatNumber(currentBaleCount);
	end;

	return currentBaleCount;
end;

function AnimatedIndoorParts:getTotalBaleCount()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local totalBaleCount = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specBaleCounter = AnimatedIndoorPartsUtil.getSpecByName(object, "baleCounter");

		if specBaleCounter ~= nil then
			totalBaleCount = specBaleCounter.countTotal;
		end;
	end;

	if totalBaleCount >= 1000 then
		totalBaleCount = AnimatedIndoorPartsUtil.formatNumber(totalBaleCount);
	end;

	return totalBaleCount;
end;

function AnimatedIndoorParts:getCurrentWrappedBaleCount()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local currentWrappedBaleCount = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specWrappedBaleCounter = AnimatedIndoorPartsUtil.getSpecByName(object, "wrappedBaleCounter");

		if specWrappedBaleCounter  ~= nil then
			currentWrappedBaleCount = specWrappedBaleCounter.countToday;
		end;
	end;

	if currentWrappedBaleCount >= 1000 then
		currentWrappedBaleCount = AnimatedIndoorPartsUtil.formatNumber(currentWrappedBaleCount);
	end;

	return currentWrappedBaleCount;
end;

function AnimatedIndoorParts:getTotalWrappedBaleCount()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local totalWrappedBaleCount = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specWrappedBaleCounter = AnimatedIndoorPartsUtil.getSpecByName(object, "wrappedBaleCounter");

		if specWrappedBaleCounter ~= nil then
			totalWrappedBaleCount = specWrappedBaleCounter.countTotal;
		end;
	end;

	if totalWrappedBaleCount >= 1000 then
		totalWrappedBaleCount = AnimatedIndoorPartsUtil.formatNumber(totalWrappedBaleCount);
	end;

	return totalWrappedBaleCount;
end;

function AnimatedIndoorParts:getFuelUsage()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specMotorized = AnimatedIndoorPartsUtil.getSpecByName(self, "motorized");
	
	return specMotorized.lastFuelUsageDisplay;
end;

function AnimatedIndoorParts:getMotorTemperature()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specMotorized = AnimatedIndoorPartsUtil.getSpecByName(self, "motorized");
	
	return specMotorized.motorTemperature.value;
end;

function AnimatedIndoorParts:getPsCurrentLane()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local currentLane = 0;

	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);

		if specProSeedTramLines ~= nil then
			currentLane = specProSeedTramLines.currentLane;
		else
			local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

			for attachedImplement = 1, #specAttacherJoints.attachedImplements do
				local object = specAttacherJoints.attachedImplements[attachedImplement].object;
				local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);
				
				if specProSeedTramLines ~= nil then
					currentLane = specProSeedTramLines.currentLane;
				else
					local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

					for attachedImplement = 1, #specAttacherJoints.attachedImplements do
						local object = specAttacherJoints.attachedImplements[attachedImplement].object;
						local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);
						
						if specProSeedTramLines ~= nil then
							currentLane = specProSeedTramLines.currentLane;
						end;
					end;
				end;
			end;
		end;
	end;

	return currentLane;
end;

function AnimatedIndoorParts:getPsMaxLanes()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local maxLanes = 1;

	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);

		if specProSeedTramLines ~= nil then
			maxLanes = specProSeedTramLines.tramLineDistance / specProSeedTramLines.workingWidthRounded;
		else
			local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

			for attachedImplement = 1, #specAttacherJoints.attachedImplements do
				local object = specAttacherJoints.attachedImplements[attachedImplement].object;
				local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);
				
				if specProSeedTramLines ~= nil then
					maxLanes = specProSeedTramLines.tramLineDistance / specProSeedTramLines.workingWidthRounded;
				else
					local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

					for attachedImplement = 1, #specAttacherJoints.attachedImplements do
						local object = specAttacherJoints.attachedImplements[attachedImplement].object;
						local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);
						
						if specProSeedTramLines ~= nil then
							maxLanes = specProSeedTramLines.tramLineDistance / specProSeedTramLines.workingWidthRounded;
						end;
					end;
				end;
			end;
		end;
	end;

	return maxLanes;
end;

function AnimatedIndoorParts:getPsWorkingWidth()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local currentWorkingWidth = 0;

	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);

		if specProSeedTramLines ~= nil then
			currentWorkingWidth = specProSeedTramLines.workingWidth;
		else
			local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

			for attachedImplement = 1, #specAttacherJoints.attachedImplements do
				local object = specAttacherJoints.attachedImplements[attachedImplement].object;
				local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);
				
				if specProSeedTramLines ~= nil then
					currentWorkingWidth = specProSeedTramLines.workingWidth;
				else
					local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

					for attachedImplement = 1, #specAttacherJoints.attachedImplements do
						local object = specAttacherJoints.attachedImplements[attachedImplement].object;
						local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);
						
						if specProSeedTramLines ~= nil then
							currentWorkingWidth = specProSeedTramLines.workingWidth;
						end;
					end;
				end;
			end;
		end;
	end;

	return currentWorkingWidth;
end;

function AnimatedIndoorParts:getPsTramlineDistance()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(self, "attacherJoints");
	local currentTramlineDistance = 0;

	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);

		if specProSeedTramLines ~= nil then
			currentTramlineDistance = specProSeedTramLines.tramLineDistance;
		else
			local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

			for attachedImplement = 1, #specAttacherJoints.attachedImplements do
				local object = specAttacherJoints.attachedImplements[attachedImplement].object;
				local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);
				
				if specProSeedTramLines ~= nil then
					currentTramlineDistance = specProSeedTramLines.tramLineDistance;
				else
					local specAttacherJoints = AnimatedIndoorPartsUtil.getSpecByName(object, "attacherJoints");

					for attachedImplement = 1, #specAttacherJoints.attachedImplements do
						local object = specAttacherJoints.attachedImplements[attachedImplement].object;
						local specProSeedTramLines = AnimatedIndoorPartsUtil.getSpecByName(object, "proSeedTramLines", specAnimatedIndoorParts.psModName);
						
						if specProSeedTramLines ~= nil then
							currentTramlineDistance = specProSeedTramLines.tramLineDistance;
						end;
					end;
				end;
			end;
		end;
	end;

	return currentTramlineDistance;
end;

function AnimatedIndoorParts:getCurrentFieldNumber()
	local specAnimatedIndoorParts = AnimatedIndoorPartsUtil.getSpecByName(self, "animatedIndoorParts");
	local currentFieldNumber = 0;
	local posX, posY, posZ = nil, nil, nil;

    if g_currentMission.controlledVehicle ~= nil then
    	posX, posY, posZ = getWorldTranslation(g_currentMission.controlledVehicle.rootNode);
    end;

	if posX ~= nil and posY ~= nil and posZ ~= nil then
		local farmland = g_farmlandManager:getFarmlandAtWorldPosition(posX, posZ);
		local isOnField, _ = FSDensityMapUtil.getFieldDataAtWorldPosition(posX, posY, posZ);

    	if farmland ~= nil and isOnField then
			local fieldMapping = g_fieldManager.farmlandIdFieldMapping[farmland.id];

    		if fieldMapping ~= nil and fieldMapping[1] ~= nil then
    		    currentFieldNumber = fieldMapping[1].fieldId;
    		end;
		end;
	end;

	for _, dashboard in pairs(self.spec_dashboard.dashboards) do
		if dashboard.node ~= nil and dashboard.valueFunc ~= nil and dashboard.valueFunc == "getCurrentFieldNumber" then
			if currentFieldNumber == 0 then
				--## fix to disable the numbers if the field number is 0
				setVisibility(dashboard.node, false);
			end;
		end;
	end;
	
	specAnimatedIndoorParts.currentFieldNumber = currentFieldNumber;

	return currentFieldNumber;
end;

function AnimatedIndoorParts:getCurrentPressure()
	local specTirePressure = AnimatedIndoorPartsUtil.getSpecByName(self, "tirePressure");
	local currentTirePressure = 0;

	if specTirePressure ~= nil then
		currentTirePressure = self:getInflationPressure() / 100;
	end;

	return currentTirePressure;
end;

function AnimatedIndoorParts:getTargetPressure()
	local specTirePressure = AnimatedIndoorPartsUtil.getSpecByName(self, "tirePressure");
	local targetTirePressure = 0;

	if specTirePressure ~= nil then
		targetTirePressure = self:getInflationPressureTarget() / 100;
	end;

	return targetTirePressure;
end;

function AnimatedIndoorParts:getCruiseControlSpeedFix()
	local specDrivable = AnimatedIndoorPartsUtil.getSpecByName(self, "drivable");
	local speed = 0;

	if specDrivable ~= nil then
		speed = specDrivable.cruiseControl.speed;
	end;

	if string.find(speed, ".") then
		local addValue = 3;

		if speed < 10 then
			addValue = 2;
		end;
		
		local speedDecimal = string.sub(speed, string.find(speed, ".") + addValue);

		if speedDecimal == "" then
			speed = math.ceil(speed);
		end;
	end;

	return speed;
end;

-----------------------------------------------------------------------------
--## Multiplayer Event
-----------------------------------------------------------------------------

AnimatedIndoorPartsScreenEvent = {};
AnimatedIndoorPartsScreenEvent_mt = Class(AnimatedIndoorPartsScreenEvent, Event);

InitEventClass(AnimatedIndoorPartsScreenEvent, "AnimatedIndoorPartsScreenEvent");

function AnimatedIndoorPartsScreenEvent.emptyNew()
	local self = Event.new(AnimatedIndoorPartsScreenEvent_mt);
    
	return self;
end;

function AnimatedIndoorPartsScreenEvent.new(tractor, currentScreenNumber)
	local self = AnimatedIndoorPartsScreenEvent.emptyNew();
	
	self.tractor = tractor;
	self.currentScreenNumber = currentScreenNumber;

	return self;
end;

function AnimatedIndoorPartsScreenEvent:readStream(streamId, connection)
	self.tractor = NetworkUtil.readNodeObject(streamId);
	self.currentScreenNumber = streamReadInt16(streamId);
	
	self:run(connection);
end;

function AnimatedIndoorPartsScreenEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.tractor);

	streamWriteInt16(streamId, self.currentScreenNumber);
end;

function AnimatedIndoorPartsScreenEvent:run(connection)
	if not connection:getIsServer() then
		g_server:broadcastEvent(AnimatedIndoorPartsScreenEvent.new(self.tractor, self.currentScreenNumber), nil, connection, self.tractor);
	end;
	
    if self.tractor ~= nil then
        self.tractor:setCurrentScreen(self.currentScreenNumber, true);
	end;
end;