--[[
Script to animate some indoor parts with gps mod and enhanced vehicle mod

Author:		Ifko[nator]
Date:		19.12.2020
Version:	1.0

History:	V 1.0 @ 19.12.2020 - initial release in FS 19
]]

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

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 = {
		"getFillLevelPercentTotal",
		"getFillLevelTotal",
		"getGpsWorkingWidth",
		"getGpsCurrentLane",
		"getCurrentBaleCount",
		"getTotalBaleCount",
		"getCurrentWrappedBaleCount",
		"getTotalWrappedBaleCount",
		"getFuelUsage",
		"getMotorTemperature",
		"getPsCurrentLane",
		"getPsWorkingWidth",
		"getPsTramlineDistance",
		"getPsMaxLanes",
		"getCurrentFieldNumber",
		"getFillLevelPercentTrailer1",
		"getFillLevelTrailer1",
		"getFillLevelPercentTrailer2",
		"getFillLevelTrailer2",
		"getCurrentFilledTrailerFillLevelPercent",
		"getCurrentFilledTrailerFillLevel",
		"getFillLevelPercentSowingMachine",
		"getFillLevelSowingMachine",
		"getFillLevelFillType",
		"getFillLevelPercentFillType",
		"getFillWeightTrailer1",
		"getFillWeightTrailer2",

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

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

		return;
	end;

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

	specAnimatedIndoorParts.driveSymbols = {};

	local driveSymbolNumber = 0;

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

	while true do
		local driveSymbolKey = AnimatedIndoorParts.baseKey .. ".driveSymbols.driveSymbol(" .. tostring(driveSymbolNumber) .. ")";
		
		if not hasXMLProperty(self.xmlFile, driveSymbolKey) then
			break;
		end;
		
		local driveSymbol = {};
		
		driveSymbol.node = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, driveSymbolKey .. "#node"), self.i3dMappings);
		
		if driveSymbol.node ~= nil then	
			driveSymbol.name = string.upper(Utils.getNoNil(getXMLString(self.xmlFile, driveSymbolKey .. "#name"), JohnDeerePackUtil.supportetDriveSymbolNames[1]));
			driveSymbol.emitColor = self:getDashboardColor(Utils.getNoNil(getXMLString(self.xmlFile, driveSymbolKey .. "#emitColor"), "GREEN"));
			driveSymbol.baseColor = self:getDashboardColor(Utils.getNoNil(getXMLString(self.xmlFile, driveSymbolKey .. "#baseColor"), "BLACK"));
			driveSymbol.intensity = Utils.getNoNil(getXMLFloat(self.xmlFile, driveSymbolKey .. "#intensity"), 1);
			driveSymbol.screenNumber = Utils.getNoNil(getXMLInt(self.xmlFile, driveSymbolKey .. "#screenNumber"), 0);
			
			if driveSymbol.name ~= "DISABLE_ON_MOTOR_OFF" then
				if driveSymbol.emitColor == nil then
					JohnDeerePackUtil.printError("Failed to load emit color '" .. getXMLString(self.xmlFile, driveSymbolKey .. "#emitColor") .. "' for node " .. getXMLString(self.xmlFile, driveSymbolKey .. "#node") .. "! (drive symbol number '" .. driveSymbolNumber + 1 .. "')! Use default emit color 'GREEN' instead!", false, false, "AnimatedIndoorParts");
				
					driveSymbol.emitColor = self:getDashboardColor("GREEN");
				end;
			
				if driveSymbol.baseColor == nil then
					JohnDeerePackUtil.printError("Failed to load base color '" .. getXMLString(self.xmlFile, driveSymbolKey .. "#baseColor") .. "' for node " .. getXMLString(self.xmlFile, driveSymbolKey .. "#node") .. "! (drive symbol number '" .. driveSymbolNumber + 1 .. "')! Use default base color 'BLACK' instead!", false, false, "AnimatedIndoorParts");
				
					driveSymbol.baseColor = self:getDashboardColor("BLACK");
				end;
			end;

			if JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.supportetDriveSymbolNames, driveSymbol.name) then
				setVisibility(driveSymbol.node, false);
				
				if driveSymbol.name == "CONTROL_LAMP" then
					driveSymbol.displayTime = Utils.getNoNil(getXMLFloat(self.xmlFile, driveSymbolKey .. "#displayTime"), 2200);
					driveSymbol.currentTime = 1;
				elseif driveSymbol.name ~= "DISABLE_ON_MOTOR_OFF" 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);

				JohnDeerePackUtil.printDebug("Load drive symbol '" .. driveSymbol.name .. "' (number '" .. driveSymbolNumber + 1 .. "', node '" .. getXMLString(self.xmlFile, driveSymbolKey .. "#node") .. "') successfully.", AnimatedIndoorParts.debugPriority, true, "AnimatedIndoorParts");
			else
				JohnDeerePackUtil.printError("Found unsupportet drive symbol name '" .. driveSymbol.name .. "' (drive symbol number '" .. driveSymbolNumber + 1 .. "')! Supportet names: '" .. JohnDeerePackUtil.getSupportetValues(JohnDeerePackUtil.supportetDriveSymbolNames) .. "'. Skipping this entry!", false, false, "AnimatedIndoorParts");
			end;
		else
			JohnDeerePackUtil.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 hasXMLProperty(self.xmlFile, fillLevelBarKey) then
			break;
		end;

		local fillLevelBar = {};

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

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

			if not JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.supportetScaleDirections, fillLevelBar.scaleDirection) then 
				JohnDeerePackUtil.printError("Invalid scale direction '" .. fillLevelBar.scaleDirection .. "' for the fill level bar! Supportet scale directions: '" .. JohnDeerePackUtil.getSupportetValues(JohnDeerePackUtil.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 hasXMLProperty(self.xmlFile, screenKey) then
			break;
		end;

		local screen = {};

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

		if node ~= nil then
			local allowInsertScreen = true;
			
			local configName = Utils.getNoNil(getXMLString(self.xmlFile, screenKey .. "#configName"), "");
			local activeConfigs = Utils.getNoNil(getXMLString(self.xmlFile, screenKey .. "#activeConfigs"), "");
			
    		if configName ~= "" and activeConfig ~= "" then
				allowInsertScreen = false;
				
				local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName);
			
    		    if storeItem ~= nil and storeItem.configurations ~= nil and storeItem.configurations[configName] ~= nil then
					local activeConfigs = StringUtil.splitString(", ", activeConfigs);
    		        local configurations = storeItem.configurations[configName];
    		        local config = configurations[self.configurations[configName]];
				
    		        for _, activeConfig in pairs(activeConfigs) do
						if g_i18n:hasText(activeConfig) then
							activeConfig = g_i18n:getText(activeConfig);
						end;
						
						if config.name == activeConfig then	
							allowInsertScreen = true;

							break;
						end;
    		        end;
    		    end;
    		end;

			if allowInsertScreen 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 = Utils.getNoNil(getXMLInt(savegame.xmlFile, savegame.key .. "." .. AnimatedIndoorParts.currentModName .. ".animatedIndoorParts#currentScreenNumber"), currentScreenNumber);
		end;

		self:setCurrentScreen(currentScreenNumber);
	end;

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

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

		if not hasXMLProperty(self.xmlFile, startScreenKey) then
			break;
		end;

		local startScreen = {};

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

		if node ~= nil then
			local allowInsertScreen = true;
			
			local configName = Utils.getNoNil(getXMLString(self.xmlFile, startScreenKey .. "#configName"), "");
			local activeConfigs = Utils.getNoNil(getXMLString(self.xmlFile, startScreenKey .. "#activeConfigs"), "");
			
    		if configName ~= "" and activeConfig ~= "" then
				allowInsertScreen = false;
				
				local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName);
			
    		    if storeItem ~= nil and storeItem.configurations ~= nil and storeItem.configurations[configName] ~= nil then
					local activeConfigs = StringUtil.splitString(", ", activeConfigs);
    		        local configurations = storeItem.configurations[configName];
    		        local config = configurations[self.configurations[configName]];
				
    		        for _, activeConfig in pairs(activeConfigs) do
						if g_i18n:hasText(activeConfig) then
							activeConfig = g_i18n:getText(activeConfig);
						end;
						
						if config.name == activeConfig then	
							allowInsertScreen = true;

							break;
						end;
    		        end;
    		    end;
    		end;

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

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

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

					if not JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.supportetScaleDirections, startScreen.scaleDirection) then 
						JohnDeerePackUtil.printError("Invalid scale direction '" .. startScreen.scaleDirection .. "' for the loading bar! Supportet scale directions: '" .. JohnDeerePackUtil.getSupportetValues(JohnDeerePackUtil.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(JohnDeerePackUtil.radioChannels) do
		specAnimatedIndoorParts[radioChannel] = I3DUtil.indexToObject(self.components, getXMLString(self.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 hasXMLProperty(self.xmlFile, disableNodeOnStartKey) then
			break;
		end;

		local disableNodeOnStart = {};

		local node = I3DUtil.indexToObject(self.components, getXMLString(self.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 hasXMLProperty(self.xmlFile, fillTypeIconKey) then
			break;
		end;

		local fillTypeIcon = {};

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

		if node ~= nil then
			fillTypeIcon.node = node;
			fillTypeIcon.trailerNumber = Utils.getNoNil(getXMLInt(self.xmlFile, fillTypeIconKey .. "#trailerNumber"), 1);
			fillTypeIcon.fillUnitIndex = Utils.getNoNil(getXMLInt(self.xmlFile, fillTypeIconKey .. "#fillUnitIndex"), 1);
			fillTypeIcon.currentFillType = FillType.UNKNOWN;
			fillTypeIcon.screenNumber = Utils.getNoNil(getXMLInt(self.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 hasXMLProperty(self.xmlFile, seedFruitTypeIconKey) then
			break;
		end;

		local seedFruitTypeIcon = {};

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

		if node ~= nil then
			seedFruitTypeIcon.node = node;
			seedFruitTypeIcon.screenNumber = Utils.getNoNil(getXMLInt(self.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 hasXMLProperty(self.xmlFile, harvestInputFruitTypeIconKey) then
			break;
		end;

		local harvestInputFruitTypeIcon = {};

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

		if node ~= nil then
			harvestInputFruitTypeIcon.node = node;
			harvestInputFruitTypeIcon.screenNumber = Utils.getNoNil(getXMLInt(self.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 hasXMLProperty(self.xmlFile, harvestOutputFruitTypeIconKey) then
			break;
		end;

		local harvestOutputFruitTypeIcon = {};

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

		if node ~= nil then
			harvestOutputFruitTypeIcon.node = node;
			harvestOutputFruitTypeIcon.screenNumber = Utils.getNoNil(getXMLInt(self.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 hasXMLProperty(self.xmlFile, fillTypeTextKey) then
			break;
		end;

		local fillTypeText = {};

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

		if node ~= nil then
			fillTypeText.node = node;
			fillTypeText.fillType = Utils.getNoNil(getXMLString(self.xmlFile, fillTypeTextKey .. "#fillType"), "unknown");
			fillTypeText.showIfEmpty = Utils.getNoNil(getXMLBool(self.xmlFile, fillTypeTextKey .. "#showIfEmpty"), false);
			fillTypeText.screenNumber = Utils.getNoNil(getXMLInt(self.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
					JohnDeerePackUtil.printError("Invalid fill type ('" .. fillTypeText.fillType .. "') for fill type text node " .. getXMLString(self.xmlFile, fillTypeTextKey .. "#node") .. "! (fill type text number '" .. fillTypeTextNumber + 1 .. "')! This entry will be skipped!", false, false, "AnimatedIndoorParts");
				end;
			end;
		end;

		fillTypeTextNumber = fillTypeTextNumber + 1;
	end;

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

	specAnimatedIndoorParts.gpsModName = "";
	specAnimatedIndoorParts.lsaModName = "";
	specAnimatedIndoorParts.bsaModName = "";
	specAnimatedIndoorParts.psModName = "";
	
	for _, mod in pairs(g_modManager.mods) do
		if _G[tostring(mod.modName)].GlobalPositioningSystem ~= nil then		
			if g_modIsLoaded[tostring(mod.modName)] then	
				specAnimatedIndoorParts.gpsModName = mod.modName;

				JohnDeerePackUtil.printDebug("Found GPS Mod with name '" .. mod.modName .. ".zip'.", AnimatedIndoorParts.debugPriority, true, "AnimatedIndoorParts");

				break;
			end;
		end;
	end;

	for _, mod in pairs(g_modManager.mods) do
		if mod.title == "Lenkachse sperren" or mod.title == "Lock steering axle" then	
			if g_modIsLoaded[tostring(mod.modName)] then	
				specAnimatedIndoorParts.lsaModName = mod.modName;

				JohnDeerePackUtil.printDebug("Found Lock Steering Axles Mod with name '" .. mod.modName .. ".zip'.", AnimatedIndoorParts.debugPriority, true, "AnimatedIndoorParts");

				break;
			end;
		end;
	end;

	for _, mod in pairs(g_modManager.mods) do
		if _G[tostring(mod.modName)].BuyableSteeringAxle ~= nil then		
			if g_modIsLoaded[tostring(mod.modName)] then	
				specAnimatedIndoorParts.bsaModName = mod.modName;

				JohnDeerePackUtil.printDebug("Found Buyable Steering Axle in '" .. mod.modName .. ".zip'.", AnimatedIndoorParts.debugPriority, true, "AnimatedIndoorParts");

				break;
			end;
		end;
	end;

	for _, mod in pairs(g_modManager.mods) do
		if _G[tostring(mod.modName)].ProSeedTramLines ~= nil then		
			if g_modIsLoaded[tostring(mod.modName)] then	
				specAnimatedIndoorParts.psModName = mod.modName;

				JohnDeerePackUtil.printDebug("Found Pro Seed Mod with name '" .. mod.modName .. ".zip'.", AnimatedIndoorParts.debugPriority, true, "AnimatedIndoorParts");

				break;
			end;
		end;
	end;
	
	specAnimatedIndoorParts.isLowFillLevel = false;
	specAnimatedIndoorParts.cardinalDirection = 0;
	specAnimatedIndoorParts.currentFieldNumber = 0;
	specAnimatedIndoorParts.currentSeedFillLevelPercent = 0;
	specAnimatedIndoorParts.currentSeedFillLevel = 0;
	specAnimatedIndoorParts.currentFertilizerFillLevelPercent = 0;
	specAnimatedIndoorParts.currentFertilizerFillLevel = 0;
	
	if self.loadDashboardsFromXML ~= nil then
		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 = "fillLevelSowingMachine",
				valueObject = self,
				valueFunc = "getFillLevelSowingMachine",
				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 = 9999
			}
		);
		
		self:loadDashboardsFromXML(
			self.xmlFile, 
			AnimatedIndoorParts.baseDashboardKey,

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

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

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

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

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

		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 = specAnimatedIndoorParts,
				valueFunc = "currentSeedFillLevel",
				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 = specAnimatedIndoorParts,
				valueFunc = "currentFertilizerFillLevel",
				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
			}
		);

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

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

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

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

			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 = 12
				}
			);

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

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

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

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

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

				{
					valueTypeToLoad = "psTramlineDistance",
					valueObject = self,
					valueFunc = "getPsTramlineDistance",
					minFunc = 0,
					maxFunc = 72
				}
			);
		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 = JohnDeerePackUtil.getSpecByName(self, "animatedIndoorParts");

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

function AnimatedIndoorParts:onRegisterActionEvents(isActiveForInput)
	local specAnimatedIndoorParts = JohnDeerePackUtil.getSpecByName(self, "animatedIndoorParts");
	
	if self.isClient then
		self:clearActionEventsTable(specAnimatedIndoorParts.actionEvents);
        
		if self:getIsActiveForInput(true, true) then
            local actionEventId;
			
			for _, input in pairs({"INDOOR_CAMERA_ZOOM_BUTTON", "INDOOR_CAMERA_RESET_BUTTON"}) do
				_, actionEventId = self:addActionEvent(specAnimatedIndoorParts.actionEvents, InputAction[input], self, AnimatedIndoorParts.changeIndoorCamera, false, true, true, true, nil);

				g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL);
				g_inputBinding:setActionEventTextVisibility(actionEventId, true);
				g_inputBinding:setActionEventActive(actionEventId, true);
				g_inputBinding:setActionEventText(actionEventId, JohnDeerePackUtil.getL10nText("input_" .. input));
			end;

			for _, input in pairs({"SWITCH_SCREEN_UP_BUTTON", "SWITCH_SCREEN_DOWN_BUTTON"}) do	
				_, actionEventId = self:addActionEvent(specAnimatedIndoorParts.actionEvents, InputAction[input], self, AnimatedIndoorParts.switchScreen, false, true, false, true, nil);

				g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL);
				g_inputBinding:setActionEventTextVisibility(actionEventId, #specAnimatedIndoorParts.screens > 1);
				g_inputBinding:setActionEventActive(actionEventId, #specAnimatedIndoorParts.screens > 1);
				g_inputBinding:setActionEventText(actionEventId, JohnDeerePackUtil.getL10nText("input_" .. input));
			end;
		end;
	end;
end;

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

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

function AnimatedIndoorParts:setCurrentScreen(currentScreenNumber, noEventSend)
	local specAnimatedIndoorParts = JohnDeerePackUtil.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.changeIndoorCamera(self, actionName, inputValue, callbackState, isAnalog)
	local specAnimatedIndoorParts = JohnDeerePackUtil.getSpecByName(self, "animatedIndoorParts");

	if actionName == "INDOOR_CAMERA_ZOOM_BUTTON" then
		specAnimatedIndoorParts.inputIsPressed = true;
	else
		specAnimatedIndoorParts.resetIndoorCamera = true;
	end;
end;

function AnimatedIndoorParts:setBarScale(bar, percentage, scaleDirection)
	if scaleDirection == "X" then
		setScale(bar, percentage, 1, 1);
	elseif scaleDirection == "Y" then
		setScale(bar, 1, percentage, 1);
	elseif scaleDirection == "Z" then
		setScale(bar, 1, 1, percentage);
	elseif scaleDirection == "ALL" then
		setScale(bar, percentage, percentage, percentage);
	end;
end;

function AnimatedIndoorParts:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
	local specAnimatedIndoorParts = JohnDeerePackUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local specFillUnit = JohnDeerePackUtil.getSpecByName(self, "fillUnit");
	local specDrivable = JohnDeerePackUtil.getSpecByName(self, "drivable");
	local specWearable = JohnDeerePackUtil.getSpecByName(self, "wearable");
	local specTrailer = JohnDeerePackUtil.getSpecByName(self, "trailer");
	local specMotorized = JohnDeerePackUtil.getSpecByName(self, "motorized");
	local specCombine = JohnDeerePackUtil.getSpecByName(self, "combine");
	local specDischargeable = JohnDeerePackUtil.getSpecByName(self, "dischargeable");
	local specPipe = JohnDeerePackUtil.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};
	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);

	local isEnteredAsPassenger = false;

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

	if self:getIsActive() or isEnteredAsPassenger then
		for radioChannelNumber, radioChannel in pairs(JohnDeerePackUtil.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 > 4 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:getWearTotalAmount() >= 0.7;
		warningWorkshop[2] = self:getWearTotalAmount() >= 0.8;
		brakesWearedOut = self:getWearTotalAmount() >= 0.82;
		motorNeedMaintrace = self:getWearTotalAmount() >= 0.9;
		lowOilPressure = self:getWearTotalAmount() >= 0.92;
		oilFillLevelToLow = self:getWearTotalAmount() >= 0.95;
		stopImminently = self:getWearTotalAmount() >= 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;
		local camIsInside = false;

		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
						self:setBarScale(startScreen.loadingBar, startScreenTimePercentage, startScreen.scaleDirection);
					end;

					setVisibility(startScreen.node, self:getIsMotorStarted() and startScreenTimePercentage < 1);
				else
					if startScreen.loadingBar ~= nil then
						self:setBarScale(startScreen.loadingBar, 0, startScreen.scaleDirection);
					end;

					setVisibility(startScreen.node, false);
				end;

				allowShowScreens = startScreen.currentTime >= startScreen.endTime;
			end;
		end;

		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.seatCam ~= nil and camera.seatCam ~= 0 then 
						if g_gameStateManager:getGameState() == GameState.PLAY 
							and self:getControllerName() == g_currentMission.player.visualInformation.playerName
							and specEnterable.camIndex == specEnterable.seatCamIndex
						then
							local newFov = math.deg(getFovY(camera.seatCam));
							local allowUpdateFov = false;
	
							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));
							local allowUpdateFov = false;
	
							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;
	
			for _, input in pairs({"INDOOR_CAMERA_ZOOM_BUTTON", "INDOOR_CAMERA_RESET_BUTTON"}) do
				local inputButton = specAnimatedIndoorParts.actionEvents[InputAction[input]];
	
				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 _, input in pairs({"SWITCH_SCREEN_UP_BUTTON", "SWITCH_SCREEN_DOWN_BUTTON"}) do	
					local actionEvent = specAnimatedIndoorParts.actionEvents[InputAction[input]];
				
					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;
					specAnimatedIndoorParts.currentSeedFillLevel = self:getFillLevelFillType("SEED");
				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;
				end;
			
				self: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, toolFrontIsAttached1, toolBackIsAttached1 = self:getIsSelected(), false, false, false, false;
		local toolFrontIsSelected2, toolBackIsSelected2, toolFrontIsAttached2, toolBackIsAttached2 = false, false, false, false;
		local frontloaderIsSelected, frontloaderToolIsSelected, frontloaderIsAttached, frontloaderToolIsAttached = false, false, false, false;
		local currentShutoffModeIsLeft, currentShutoffModeIsRight, currentModeIsAuto, currentModeIsSemi, currentModeIsManual, createTramLines = false, false, false, false, false, false;
		
		if specAnimatedIndoorParts.gpsModName ~= "" then
			local specGlobalPositioningSystem = JohnDeerePackUtil.getSpecByName(self, "globalPositioningSystem", specAnimatedIndoorParts.gpsModName);
			
			if specGlobalPositioningSystem.guidanceData ~= nil then
				gpsIsActive = specGlobalPositioningSystem.guidanceIsActive;
				gpsSteeringIsActive = specGlobalPositioningSystem.guidanceSteeringIsActive;
				gpsCurrentLane = specGlobalPositioningSystem.guidanceData.currentLane;
			end;
		end;
	
		if self.vcaDiffLockFront == nil then
			if self.vData ~= nil then
				diffLockFront, diffLockBack, handbrakeIsActive, fourWheel = self.vData.is[1], self.vData.is[2], self.vData.is[5] and self.vData.is[6], self.vData.want[3] == 1;
			end;
		else
			diffLockFront, diffLockBack, handbrakeIsActive, fourWheel = self.vcaDiffLockFront and self.vcaDiffLockAWD, self.vcaDiffLockBack, self.vcaNewHandbrake, self.vcaDiffLockAWD;
		end;

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			--## tool 1
			
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local object_specAttachable = JohnDeerePackUtil.getSpecByName(object, "attachable");
			local object_specTrailer = JohnDeerePackUtil.getSpecByName(object, "trailer");
			local object_specLockSteeringAxles = JohnDeerePackUtil.getSpecByName(object, "lockSteeringAxles", specAnimatedIndoorParts.lsaModName);
			local object_specBuyableSteeringAxle = JohnDeerePackUtil.getSpecByName(object, "buyableSteeringAxle", specAnimatedIndoorParts.bsaModName);
			local object_specRidgeMarker = JohnDeerePackUtil.getSpecByName(object, "ridgeMarker");
			local object_specFoldable = JohnDeerePackUtil.getSpecByName(object, "foldable");
			local object_specCutter = JohnDeerePackUtil.getSpecByName(object, "cutter");
			local object_specLights = JohnDeerePackUtil.getSpecByName(object, "lights");
			local object_specFillUnit = JohnDeerePackUtil.getSpecByName(object, "fillUnit");
			local object_specSowingMachine = JohnDeerePackUtil.getSpecByName(object, "sowingMachine");
			local attacherVehicle_zScale = nil;

			if specAnimatedIndoorParts.psModName ~= "" then
				local specProSeedTramLines = JohnDeerePackUtil.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;

						JohnDeerePackUtil.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_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 object_specLights.hasTurnLights or #object_specLights.shaderLeftTurnLights > 0 and #object_specLights.shaderRightTurnLights > 0 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_specAttachable.attacherVehicle ~= nil then
				local attacherJointVehicleSpec = JohnDeerePackUtil.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 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();
						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;
					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_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 = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(object, "lockSteeringAxles", specAnimatedIndoorParts.lsaModName);
					local object_specBuyableSteeringAxle = JohnDeerePackUtil.getSpecByName(object, "buyableSteeringAxle", specAnimatedIndoorParts.bsaModName);
					local object_specAttachable = JohnDeerePackUtil.getSpecByName(object, "attachable");
					local object_specTrailer = JohnDeerePackUtil.getSpecByName(object, "trailer");
					local object_specLights = JohnDeerePackUtil.getSpecByName(object, "lights");
					local object_specRidgeMarker = JohnDeerePackUtil.getSpecByName(object, "ridgeMarker");
					local object_specFoldable = JohnDeerePackUtil.getSpecByName(object, "foldable");
					local object_specFillUnit = JohnDeerePackUtil.getSpecByName(object, "fillUnit");
					local object_specSowingMachine = JohnDeerePackUtil.getSpecByName(object, "sowingMachine");
					
					local attacherJointVehicleSpec = JohnDeerePackUtil.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 = JohnDeerePackUtil.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;
					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 object_specLights.hasTurnLights or #object_specLights.shaderLeftTurnLights > 0 and #object_specLights.shaderRightTurnLights > 0 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 = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(object, "ridgeMarker");
							local object_specFoldable = JohnDeerePackUtil.getSpecByName(object, "foldable");
							local object_specSowingMachine = JohnDeerePackUtil.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" then
							setShaderParameter(driveSymbol.node, "lightControl", 0, 0, 0, 0, false);
							setVisibility(driveSymbol.node, true);
						end;

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

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

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

						elseif driveSymbol.name == "TIPPING_INACTIVE" then
							if numberOfAttachedTrailers > 0 and not trailerIsTippingActive then
								setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							end;

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

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

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

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

						elseif driveSymbol.name == "HANDBRAKE" then
							if handbrakeIsActive then
								setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							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 == "GPS_STEERING_ACTIVE" then
							if gpsSteeringIsActive then
								setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							end;
						
						elseif driveSymbol.name == "GPS_ACTIVE" then
							if gpsIsActive then
								setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							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 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;

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

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

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

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

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

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

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

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

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

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

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

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

						elseif driveSymbol.name == "STOP_IMMINENTLY" then
							if stopImminently then
								setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							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_SELECTED" then
							setVisibility(driveSymbol.node, drivableIsSelected);

							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);

							if vehicleSchemaIsTurnedOn[1] then
								setShaderParameter(driveSymbol.node, "emitColor", 0, 0.1, 0, 1, false);
							end;

						elseif driveSymbol.name == "TOOL_FRONT_SELECTED_1" then
							setVisibility(driveSymbol.node, toolFrontIsSelected1);

							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);

							if vehicleSchemaIsTurnedOn[2] then
								setShaderParameter(driveSymbol.node, "emitColor", 0, 0.1, 0, 1, false);
							end;

						elseif driveSymbol.name == "TOOL_FRONT_SELECTED_2" then
							setVisibility(driveSymbol.node, toolFrontIsSelected2);

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

						elseif driveSymbol.name == "TOOL_BACK_SELECTED_1" then
							setVisibility(driveSymbol.node, toolBackIsSelected1);

							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);

							if vehicleSchemaIsTurnedOn[3] then
								setShaderParameter(driveSymbol.node, "emitColor", 0, 0.1, 0, 1, false);
							end;

						elseif driveSymbol.name == "TOOL_BACK_SELECTED_2" then
							setVisibility(driveSymbol.node, toolBackIsSelected2);

							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);

							if vehicleSchemaIsTurnedOn[4] then
								setShaderParameter(driveSymbol.node, "emitColor", 0, 0.1, 0, 1, false);
							end;

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

							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);

							if vehicleSchemaIsTurnedOn[1] then
								setShaderParameter(driveSymbol.node, "emitColor", 0, 0.25, 0, 1, false);
							end;

						elseif driveSymbol.name == "TOOL_FRONT_UNSELECTED_1" then
							setVisibility(driveSymbol.node, toolFrontIsAttached1);

							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);

							if vehicleSchemaIsTurnedOn[2] then
								setShaderParameter(driveSymbol.node, "emitColor", 0, 0.25, 0, 1, false);
							end;

						elseif driveSymbol.name == "TOOL_FRONT_UNSELECTED_2" then
							setVisibility(driveSymbol.node, toolFrontIsAttached2);

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

						elseif driveSymbol.name == "TOOL_BACK_UNSELECTED_1" then
							setVisibility(driveSymbol.node, toolBackIsAttached1);

							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);

							if vehicleSchemaIsTurnedOn[3] then
								setShaderParameter(driveSymbol.node, "emitColor", 0, 0.25, 0, 1, false);
							end;

						elseif driveSymbol.name == "TOOL_BACK_UNSELECTED_2" then
							setVisibility(driveSymbol.node, toolBackIsAttached2);

							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);

							if vehicleSchemaIsTurnedOn[4] then
								setShaderParameter(driveSymbol.node, "emitColor", 0, 0.25, 0, 1, false);
							end;

						elseif driveSymbol.name == "FRONTLOADER_SELECTED" then
							setVisibility(driveSymbol.node, frontloaderIsSelected);

							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);

						elseif driveSymbol.name == "FRONTLOADER_UNSELECTED" then
							setVisibility(driveSymbol.node, frontloaderIsAttached);

							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);

						elseif driveSymbol.name == "FRONTLOADER_TOOL_SELECTED" then
							setVisibility(driveSymbol.node, frontloaderToolIsSelected);

							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);

						elseif driveSymbol.name == "FRONTLOADER_TOOL_UNSELECTED" then
							setVisibility(driveSymbol.node, frontloaderToolIsAttached);

							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);

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

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

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

						elseif driveSymbol.name == "PIPE_UNFOLDED" then
							if isPipeUnfolded then
								setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
							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
								if numberOfAttachedTrailers >= trailerNumber and trailerIsTipping[trailerNumber] then
									setShaderParameter(driveSymbol.node, "lightControl", driveSymbol.intensity, 0, 0, 0, false);
								end;
							end;
						end;
					else
						if driveSymbol.name ~= "DISABLE_ON_MOTOR_OFF" then	
							setVisibility(driveSymbol.node, false);
						end;

						if driveSymbol.name == "CONTROL_LAMP" then
							driveSymbol.currentTime = driveSymbol.intensity;
						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 getVisibility(driveSymbol.node) then
						setVisibility(driveSymbol.node, false);
					end;
				end;
			end;
		end;
	end;
end;

function AnimatedIndoorParts:getFillLevelPercentSowingMachine()
	local specAttacherJoints = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(object, "fillUnit");
		local specSowingMachine = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			--## tool 2
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = JohnDeerePackUtil.getSpecByName(object, "fillUnit");
			local specSowingMachine = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(object, "attacherJoints");

			for attachedImplement = 1, #specAttacherJoints.attachedImplements do
				--## tool 3
				local object = specAttacherJoints.attachedImplements[attachedImplement].object;
				local specFillUnit = JohnDeerePackUtil.getSpecByName(object, "fillUnit");
				local specSowingMachine = JohnDeerePackUtil.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:getFillLevelSowingMachine()
	local specAttacherJoints = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		--## tool 1
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = JohnDeerePackUtil.getSpecByName(object, "fillUnit");
		local specSowingMachine = JohnDeerePackUtil.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;
				end;
			end;
		end;

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

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			--## tool 2
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = JohnDeerePackUtil.getSpecByName(object, "fillUnit");
			local specSowingMachine = JohnDeerePackUtil.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;
					end;
				end;
			end;

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

			for attachedImplement = 1, #specAttacherJoints.attachedImplements do
				--## tool 3
				local object = specAttacherJoints.attachedImplements[attachedImplement].object;
				local specFillUnit = JohnDeerePackUtil.getSpecByName(object, "fillUnit");
				local specSowingMachine = JohnDeerePackUtil.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;
						end;
					end;
				end;
			end;
		end;
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getFillLevelPercentFillType(fillTypeName)
	local specAttacherJoints = JohnDeerePackUtil.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 = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;
	
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = JohnDeerePackUtil.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 = JohnDeerePackUtil.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 = JohnDeerePackUtil.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 isStrawHarvestMachine then
						isUnSupportetFillType = JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name);
					end;

					if not isUnSupportetFillType then	
						fillLevel = fillLevel + fillUnit.fillLevel;
						capacity = capacity + fillUnit.capacity;
					end;
				end;
			end;
		end;

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

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = JohnDeerePackUtil.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 isStrawHarvestMachine then
							isUnSupportetFillType = JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name);
						end;

						if not isUnSupportetFillType 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 = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = JohnDeerePackUtil.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 isStrawHarvestMachine then
						isUnSupportetFillType = JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name);
					end;

					if not isUnSupportetFillType then	
						fillLevel = fillLevel + fillUnit.fillLevel;
					end;
				end;
			end;
		end;

		local specAttacherJoints = JohnDeerePackUtil.getSpecByName(object, "attacherJoints");
			
		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = JohnDeerePackUtil.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 isStrawHarvestMachine then
							isUnSupportetFillType = JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name);
						end;

						if not isUnSupportetFillType then	
							fillLevel = fillLevel + fillUnit.fillLevel;
						end;
					end;
				end;
			end;
		end;
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getFillLevelPercentTrailer1()
	local specAttacherJoints = JohnDeerePackUtil.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 = JohnDeerePackUtil.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 isStrawHarvestMachine then
						isUnSupportetFillType = JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name);
					end;

					if not isUnSupportetFillType 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 = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = JohnDeerePackUtil.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 isStrawHarvestMachine then
						isUnSupportetFillType = JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name);
					end;

					if not isUnSupportetFillType then	
						fillLevel = fillLevel + fillUnit.fillLevel;
					end;
				end;
			end;
		end;
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getCurrentFilledTrailerFillLevelPercent()
	local specPipe = JohnDeerePackUtil.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 = JohnDeerePackUtil.getCurrentFilledTrailer(self, dischargeNode, specPipe);
		
			if currentFilledTrailer ~= nil then
				local specFillUnit = JohnDeerePackUtil.getSpecByName(currentFilledTrailer, "fillUnit");
				local specAttacherJoints = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(object, "fillUnit");

					for _, fillUnit in ipairs(specFillUnit.fillUnits) do
						if fillUnit.showOnHud 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;
		end;
	end;

	return percentage;
end;

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

				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 = JohnDeerePackUtil.getSpecByName(object, "fillUnit");

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

	return fillLevel;
end;

function AnimatedIndoorParts:getFillLevelPercentTrailer2()
	local specAttacherJoints = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(object, "attacherJoints");

		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = JohnDeerePackUtil.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 isStrawHarvestMachine then
							isUnSupportetFillType = JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name);
						end;

						if not isUnSupportetFillType 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 = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local fillLevel = 0;
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specAttacherJoints = JohnDeerePackUtil.getSpecByName(object, "attacherJoints");
			
		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = JohnDeerePackUtil.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 isStrawHarvestMachine then
							isUnSupportetFillType = JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name);
						end;

						if not isUnSupportetFillType then	
							fillLevel = fillLevel + fillUnit.fillLevel;
						end;
					end;
				end;
			end;
		end;
	end;

	return fillLevel;
end;

function AnimatedIndoorParts:getFillWeightTrailer1()
	local specAttacherJoints = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local fillWeight = {0, 0};
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specFillUnit = JohnDeerePackUtil.getSpecByName(object, "fillUnit");
		local isStrawHarvestMachine = string.find(object.configFileName, "strawHarvest/vehicles/krone");
		local isUnSupportetFillType = false;

		if specFillUnit ~= nil then
			for fillUnitNumber, fillUnit in pairs(specFillUnit.fillUnits) do
				if fillUnit.showOnHud then
					if isStrawHarvestMachine then
						isUnSupportetFillType = JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name);
					end;

					if not isUnSupportetFillType then	
						fillWeight[fillUnitNumber] = fillUnit.fillLevel * g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).massPerLiter * 1000;
					end;
				end;
			end;
		end;
	end;

	return fillWeight[1] + fillWeight[2];
end;

function AnimatedIndoorParts:getFillWeightTrailer2()
	local specAttacherJoints = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local fillWeight = {0, 0};
		
	for attachedImplement = 1, #specAttacherJoints.attachedImplements do
		local object = specAttacherJoints.attachedImplements[attachedImplement].object;
		local specAttacherJoints = JohnDeerePackUtil.getSpecByName(object, "attacherJoints");
			
		for attachedImplement = 1, #specAttacherJoints.attachedImplements do
			local object = specAttacherJoints.attachedImplements[attachedImplement].object;
			local specFillUnit = JohnDeerePackUtil.getSpecByName(object, "fillUnit");
			local isStrawHarvestMachine = string.find(object.configFileName, "strawHarvest/vehicles/krone");
			local isUnSupportetFillType = false;
	
			if specFillUnit ~= nil then
				for fillUnitNumber, fillUnit in pairs(specFillUnit.fillUnits) do
					if fillUnit.showOnHud then
						if isStrawHarvestMachine then
							isUnSupportetFillType = JohnDeerePackUtil.getIsSupportetValue(JohnDeerePackUtil.unSupportetFillTypes, g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).name);
						end;

						if not isUnSupportetFillType then	
							fillWeight[fillUnitNumber] = fillUnit.fillLevel * g_fillTypeManager:getFillTypeByIndex(fillUnit.fillType).massPerLiter * 1000;
						end;
					end;
				end;
			end;
		end;
	end;

	return fillWeight[1] + fillWeight[2];
end;

function AnimatedIndoorParts:getGpsWorkingWidth()
	local specAnimatedIndoorParts = JohnDeerePackUtil.getSpecByName(self, "animatedIndoorParts");
	local specGlobalPositioningSystem = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(self, "animatedIndoorParts");
	local specGlobalPositioningSystem = JohnDeerePackUtil.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;

	return currentLane;
end;

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

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

	return currentBaleCount;
end;

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

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

	return totalBaleCount;
end;

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

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

	return currentWrappedBaleCount;
end;

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

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

	return totalWrappedBaleCount;
end;

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

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

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

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

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

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

					for attachedImplement = 1, #specAttacherJoints.attachedImplements do
						local object = specAttacherJoints.attachedImplements[attachedImplement].object;
						local specProSeedTramLines = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local maxLanes = 1;

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

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

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

					for attachedImplement = 1, #specAttacherJoints.attachedImplements do
						local object = specAttacherJoints.attachedImplements[attachedImplement].object;
						local specProSeedTramLines = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local currentWorkingWidth = 0;

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

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

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

					for attachedImplement = 1, #specAttacherJoints.attachedImplements do
						local object = specAttacherJoints.attachedImplements[attachedImplement].object;
						local specProSeedTramLines = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(self, "animatedIndoorParts");
	local specAttacherJoints = JohnDeerePackUtil.getSpecByName(self, "attacherJoints");
	local currentTramlineDistance = 0;

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

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

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

					for attachedImplement = 1, #specAttacherJoints.attachedImplements do
						local object = specAttacherJoints.attachedImplements[attachedImplement].object;
						local specProSeedTramLines = JohnDeerePackUtil.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 = JohnDeerePackUtil.getSpecByName(self, "animatedIndoorParts");
	local currentFieldNumber = 0;
	local posX, posZ = nil, nil;

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

	if posX ~= nil and posZ ~= nil then
		local farmland = g_farmlandManager:getFarmlandAtWorldPosition(posX, posZ);

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

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

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

	return currentFieldNumber;
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;