--
-- BalerVariableChamber
-- Class for all BalerVariableChambers
--
-- @author  Stefan Geiger
-- @date  10/09/08
--
-- Updated for variable chamber
-- @author  JoXXer
-- @date  18/06/11
--

-- Updated for variable chamber
-- @author  @lex (FraBel LS-Modding)
-- @date  05/04/15
--

source("dataS/scripts/vehicles/specializations/BalerSetIsUnloadingBaleEvent.lua");
source("dataS/scripts/vehicles/specializations/BalerSetBaleTimeEvent.lua");
source("dataS/scripts/vehicles/specializations/BalerCreateBaleEvent.lua");
source("dataS/scripts/vehicles/specializations/BalerAreaEvent.lua");
  
BalerVariableChamber = {};

BalerVariableChamber.UNLOADING_CLOSED = 1;
BalerVariableChamber.UNLOADING_OPENING = 2;
BalerVariableChamber.UNLOADING_OPEN = 3;
BalerVariableChamber.UNLOADING_CLOSING = 4;

function BalerVariableChamber.prerequisitesPresent(specializations)
	return SpecializationUtil.hasSpecialization(Fillable, specializations);
end;

function BalerVariableChamber:load(xmlFile)

	self.getIsAreaActive = Utils.overwrittenFunction(self.getIsAreaActive, BalerVariableChamber.getIsAreaActive);
	self.setPickup= SpecializationUtil.callSpecializationsFunction("setPickup");
	self.setTransport= SpecializationUtil.callSpecializationsFunction("setTransport");
	self.setVehicleIncreaseRpm = SpecializationUtil.callSpecializationsFunction("setVehicleIncreaseRpm");

	-- Animations --
	self.PickupAnimation = getXMLString(xmlFile, "vehicle.Pickup#animationName");
	self.Pickup = false;

	-- Animated objects --
	self.numRotParts = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.RotParts#count"), 0);
	self.RotParts = {};
	for i=1, self.numRotParts do
		local RotPartsnamei = string.format("vehicle.RotParts.RotPart" .. "%d", i);
		self.RotParts[i] = Utils.indexToObject(self.components, getXMLString(xmlFile, RotPartsnamei .. "#index"));
	end;
	self.PickupWheel1 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.PickupWheel1#index"));
	self.PickupWheel2 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.PickupWheel2#index"));
	self.FeederRoll = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.FeederRoll#index"));

	-- Transport --
	self.Transport = false;

	-- Transport Objects --

	self.wheelsInTransportPosition = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.wheelsInTransportPosition#index"));
	self.wheelsInFieldPosition = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.wheelsInFieldPosition#index"));

	-- Particles --
	self.PickUpParticleSystems = {};
	local i = 0;
	while true do
		local namei = string.format("vehicle.PickUpParticleSystems.PickUpParticleSystems(%d)", i);
		local nodei = Utils.indexToObject(self.components, getXMLString(xmlFile, namei .. "#index"));
		if nodei == nil then
			break;
		end;
		Utils.loadParticleSystem(xmlFile, self.PickUpParticleSystems, namei, nodei, false, nil, self.baseDirectory)
		i = i +1;
	end;

	self.inrange = false;

	self.setIsTurnedOn = SpecializationUtil.callSpecializationsFunction("setIsTurnedOn");
	self.setIncreaseBaleSize = SpecializationUtil.callSpecializationsFunction("setIncreaseBaleSize");
	self.setDecreaseBaleSize = SpecializationUtil.callSpecializationsFunction("setDecreaseBaleSize");
	self.setNetBale = SpecializationUtil.callSpecializationsFunction("setNetBale");
	self.setNetBaleSize = SpecializationUtil.callSpecializationsFunction("setNetBaleSize");

	self.getTimeFromLevel = BalerVariableChamber.getTimeFromLevel;
	self.moveBales = SpecializationUtil.callSpecializationsFunction("moveBales");
	self.moveBale = SpecializationUtil.callSpecializationsFunction("moveBale");
	self.allowFillType = BalerVariableChamber.allowFillType;
	self.allowPickingUp = BalerVariableChamber.allowPickingUp;
	self.setIsUnloadingBale = BalerVariableChamber.setIsUnloadingBale;

	self.BALE_60CM = 60;
	self.BALE_80CM = 80;
	self.BALE_100CM = 100;
	self.BALE_120CM = 120;
	self.BALE_140CM = 140;
	self.BALE_160CM = 160;
	self.BALE_ORIGINAL = 180;

	self.currentBaleSize = self.BALE_ORIGINAL;

	self.originalBaleCapacity = self.capacity;

	self.baleCapacities = {}
	self.baleCapacities[1] = (self.originalBaleCapacity*0.33);
	self.baleCapacities[2] = (self.originalBaleCapacity*0.44);
	self.baleCapacities[3] = (self.originalBaleCapacity*0.56);
	self.baleCapacities[4] = (self.originalBaleCapacity*0.67);
	self.baleCapacities[5] = (self.originalBaleCapacity*0.78);
	self.baleCapacities[6] = (self.originalBaleCapacity*0.89);
	self.baleCapacities[7] = self.originalBaleCapacity;


	self.isUnloadingPlaying = false;
	self.unloadingAnimTime = 0;

	self.netBale = false;
	self.currentBaleSizeSaved = nil;
	self.capacitySaved = nil;

	self.lastUsedFruitType = nil;

	self.fillScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fillScale#value"), 1);

	local firstBaleMarker = getXMLFloat(xmlFile, "vehicle.baleAnimation#firstBaleMarker");
	if firstBaleMarker ~= nil then
		local baleAnimCurve = AnimCurve:new(linearInterpolatorN);
		local keyI = 0;
		while true do
			local key = string.format("vehicle.baleAnimation.key(%d)", keyI);
			local t = getXMLFloat(xmlFile, key.."#time");
			local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#pos"));
			if x == nil or y == nil or z == nil then
				break;
			end;
			local rx, ry, rz = Utils.getVectorFromString(getXMLString(xmlFile, key.."#rot"));
			rx = math.rad(Utils.getNoNil(rx, 0));
			ry = math.rad(Utils.getNoNil(ry, 0));
			rz = math.rad(Utils.getNoNil(rz, 0));
			baleAnimCurve:addKeyframe({ v={x, y, z, rx, ry, rz}, time = t});
			keyI = keyI +1;
		end;
		if keyI > 0 then
			self.baleAnimCurve = baleAnimCurve;
			self.firstBaleMarker = firstBaleMarker;
			--self.baleFilename = Utils.getNoNil(getXMLString(xmlFile, "vehicle.baleAnimation#filename"), "data/maps/models/objects/strawbale/strawbaleNewBaler.i3d");
		end;
	end;
	self.baleAnimRoot = Utils.getNoNil(Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.baleAnimation#node")), self.components[1].node);

	-- there is no standard bale animation, load the unload animation (for round baler)
	if self.firstBaleMarker == nil then
		local unloadAnimationNameOriginal = getXMLString(xmlFile, "vehicle.baleAnimation#unloadAnimationNameOriginal");

		--Variable Chamber Animations--
		local unloadAnimationName60cm = getXMLString(xmlFile, "vehicle.baleAnimation#unloadAnimationName60cm");
		local unloadAnimationName80cm = getXMLString(xmlFile, "vehicle.baleAnimation#unloadAnimationName80cm");
		local unloadAnimationName100cm = getXMLString(xmlFile, "vehicle.baleAnimation#unloadAnimationName100cm");
		local unloadAnimationName120cm = getXMLString(xmlFile, "vehicle.baleAnimation#unloadAnimationName120cm");
		local unloadAnimationName140cm = getXMLString(xmlFile, "vehicle.baleAnimation#unloadAnimationName140cm");
		local unloadAnimationName160cm = getXMLString(xmlFile, "vehicle.baleAnimation#unloadAnimationName160cm");

		local closeAnimationName = getXMLString(xmlFile, "vehicle.baleAnimation#closeAnimationName");
		local unloadAnimationSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.baleAnimation#unloadAnimationSpeed"), 1);
		local closeAnimationSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.baleAnimation#closeAnimationSpeed"), 1);
		if unloadAnimationNameOriginal ~= nil and closeAnimationName ~= nil and unloadAnimationName60cm ~= nil then
			if self.playAnimation ~= nil and self.animations ~= nil then
				if self.animations[unloadAnimationNameOriginal] ~= nil and self.animations[closeAnimationName] ~= nil and self.animations[unloadAnimationName60cm] ~= nil and self.animations[unloadAnimationName80cm] ~= nil and self.animations[unloadAnimationName100cm] ~= nil and self.animations[unloadAnimationName120cm] ~= nil and self.animations[unloadAnimationName140cm] ~= nil and self.animations[unloadAnimationName160cm] ~= nil then
					--print("has unload animation");
					self.baleUnloadAnimationNameOriginal = unloadAnimationNameOriginal;

					--Variable Chamber Animations--
					self.baleUnloadAnimationName60cm = unloadAnimationName60cm;
					self.baleUnloadAnimationName80cm = unloadAnimationName80cm;
					self.baleUnloadAnimationName100cm = unloadAnimationName100cm;
					self.baleUnloadAnimationName120cm = unloadAnimationName120cm;
					self.baleUnloadAnimationName140cm = unloadAnimationName140cm;
					self.baleUnloadAnimationName160cm = unloadAnimationName160cm;

					self.baleUnloadAnimationSpeed = unloadAnimationSpeed;

					self.baleCloseAnimationName = closeAnimationName;
					self.baleCloseAnimationSpeed = closeAnimationSpeed;

					self.baleDropAnimTime = getXMLFloat(xmlFile, "vehicle.baleAnimation#baleDropAnimTime");
					if self.baleDropAnimTime == nil then
						self.baleDropAnimTime = self:getAnimationDuration(self.baleUnloadAnimationNameOriginal);
					else
						self.baleDropAnimTime = self.baleDropAnimTime * 1000;
					end;
				else
					print("Error: Failed to find unload animations '"..baleUnloadAnimationNameOriginal.."' and '"..closeAnimationName.."' in '"..self.configFileName.."'.");
				end;
			else
				print("Error: There is an unload animation in '"..self.configFileName.."' but it is not a AnimatedVehicle. Change to a vehicle type which has the AnimatedVehicle specialization.");
			end;
		end;
	end;

	--Original Bales--
	self.baleTypes = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.baleTypes.baleType(%d)", i);
		local t = getXMLString(xmlFile, key.."#fruitType");
		local filename = getXMLString(xmlFile, key.."#filename");
		if t==nil or filename==nil then
			break;
		end;
		local entry = {};
		entry.filename = filename;
		local desc = FruitUtil.fruitTypes[t];
		if desc ~= nil then
			self.baleTypes[desc.index] = entry;
			if self.defaultBaleType == nil then
				self.defaultBaleType = entry;
			end;
		end;
		i = i +1;
	end;
	if self.defaultBaleType == nil then
		self.baleTypes = nil;
	end;

	--60cm Bales--
	self.baleTypes60cm = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.baleTypes60cm.baleType60cm(%d)", i);
		local t = getXMLString(xmlFile, key.."#fruitType");
		local filename = getXMLString(xmlFile, key.."#filename");
		if t==nil or filename==nil then
			break;
		end;
		local entry = {};
		entry.filename = filename;
		local desc = FruitUtil.fruitTypes[t];
		if desc ~= nil then
			self.baleTypes60cm[desc.index] = entry;
		end;
		i = i +1;
	end;

	--80cm Bales--
	self.baleTypes80cm = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.baleTypes80cm.baleType80cm(%d)", i);
		local t = getXMLString(xmlFile, key.."#fruitType");
		local filename = getXMLString(xmlFile, key.."#filename");
		if t==nil or filename==nil then
			break;
		end;
		local entry = {};
		entry.filename = filename;
		local desc = FruitUtil.fruitTypes[t];
		if desc ~= nil then
			self.baleTypes80cm[desc.index] = entry;
		end;
		i = i +1;
	end;

	--100cm Bales--
	self.baleTypes100cm = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.baleTypes100cm.baleType100cm(%d)", i);
		local t = getXMLString(xmlFile, key.."#fruitType");
		local filename = getXMLString(xmlFile, key.."#filename");
		if t==nil or filename==nil then
			break;
		end;
		local entry = {};
		entry.filename = filename;
		local desc = FruitUtil.fruitTypes[t];
		if desc ~= nil then
			self.baleTypes100cm[desc.index] = entry;
		end;
		i = i +1;
	end;

	--120cm Bales--
	self.baleTypes120cm = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.baleTypes120cm.baleType120cm(%d)", i);
		local t = getXMLString(xmlFile, key.."#fruitType");
		local filename = getXMLString(xmlFile, key.."#filename");
		if t==nil or filename==nil then
			break;
		end;
		local entry = {};
		entry.filename = filename;
		local desc = FruitUtil.fruitTypes[t];
		if desc ~= nil then
			self.baleTypes120cm[desc.index] = entry;
		end;
		i = i +1;
	end;

	--140cm Bales--
	self.baleTypes140cm = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.baleTypes140cm.baleType140cm(%d)", i);
		local t = getXMLString(xmlFile, key.."#fruitType");
		local filename = getXMLString(xmlFile, key.."#filename");
		if t==nil or filename==nil then
			break;
		end;
		local entry = {};
		entry.filename = filename;
		local desc = FruitUtil.fruitTypes[t];
		if desc ~= nil then
			self.baleTypes140cm[desc.index] = entry;
		end;
		i = i +1;
	end;

	--160cm Bales--
	self.baleTypes160cm = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.baleTypes160cm.baleType160cm(%d)", i);
		local t = getXMLString(xmlFile, key.."#fruitType");
		local filename = getXMLString(xmlFile, key.."#filename");
		if t==nil or filename==nil then
			break;
		end;
		local entry = {};
		entry.filename = filename;
		local desc = FruitUtil.fruitTypes[t];
		if desc ~= nil then
			self.baleTypes160cm[desc.index] = entry;
		end;
		i = i +1;
	end;

	local balerSound = getXMLString(xmlFile, "vehicle.balerSound#file");
	if balerSound ~= nil and balerSound ~= "" then
		balerSound = Utils.getFilename(balerSound, self.baseDirectory);
		self.balerSound = createSample("balerSound");
		self.balerSoundEnabled = false;
		loadSample(self.balerSound, balerSound, false);
		self.balerSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerSound#pitchOffset"), 1);
		self.balerSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerSound#volume"), 1);
	end;

	local balerAlarm = getXMLString(xmlFile, "vehicle.balerAlarm#file");
	if balerAlarm ~= nil and balerAlarm ~= "" then
		balerAlarm = Utils.getFilename(balerAlarm, self.baseDirectory);
		self.balerAlarm = createSample("balerAlarm");
		self.balerAlarmEnabled = false;
		loadSample(self.balerAlarm, balerAlarm, false);
		self.balerAlarmPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerAlarm#pitchOffset"), 1);
		self.balerAlarmVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerAlarm#volume"), 1);
	end;

	local balerBaleEject = getXMLString(xmlFile, "vehicle.balerBaleEject#file");
	if balerBaleEject ~= nil and balerBaleEject ~= "" then
		balerBaleEject = Utils.getFilename(balerBaleEject, self.baseDirectory);
		self.balerBaleEject = createSample("balerBaleEject");
		self.balerBaleEjectEnabled = false;
		loadSample(self.balerBaleEject, balerBaleEject, false);
		self.balerBaleEjectPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerBaleEject#pitchOffset"), 1);
		self.balerBaleEjectVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerBaleEject#volume"), 1);
	end;

	local balerDoor = getXMLString(xmlFile, "vehicle.balerDoor#file");
	if balerDoor ~= nil and balerDoor ~= "" then
		balerDoor = Utils.getFilename(balerDoor, self.baseDirectory);
		self.balerDoor = createSample("balerDoor");
		self.balerDoorEnabled = false;
		loadSample(self.balerDoor, balerDoor, false);
		self.balerDoorPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerDoor#pitchOffset"), 1);
		self.balerDoorVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerDoor#volume"), 1);
	end;

	local balerKnotCleaning = getXMLString(xmlFile, "vehicle.balerKnotCleaning#file");
	if balerKnotCleaning ~= nil and balerKnotCleaning ~= "" then
		balerKnotCleaning = Utils.getFilename(balerKnotCleaning, self.baseDirectory);
		self.balerKnotCleaning = createSample("balerKnotCleaning");
		self.balerKnotCleaningEnabled = false;
		self.balerKnotCleaningTime = 100000;
		loadSample(self.balerKnotCleaning, balerKnotCleaning, false);
		self.balerKnotCleaningPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerKnotCleaning#pitchOffset"), 1);
		self.balerKnotCleaningVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerKnotCleaning#volume"), 1);
	end;

	self.balerUVScrollParts = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.balerUVScrollParts.balerUVScrollPart(%d)", i);
		if not hasXMLProperty(xmlFile, key) then
			break;
		end;
		local node = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
		local speed = Utils.getVectorNFromString(getXMLString(xmlFile, key.."#speed"), 2);
		if node ~= nil and speed then
			table.insert(self.balerUVScrollParts, {node=node, speed=speed});
		end;
		i = i +1;
	end;

	self.pickupAnimationName = Utils.getNoNil(getXMLString(xmlFile, "vehicle.pickupAnimation#name"), "");
	if self.playAnimation == nil or self.getIsAnimationPlaying == nil then
		self.pickupAnimationName = "";
	end;
	self.pickupAnimationLowerSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.pickupAnimation#lowerSpeed"), 1);
	self.pickupAnimationLiftSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.pickupAnimation#liftSpeed"), -self.pickupAnimationLowerSpeed);

	self.baleLastPositionTime = 0;

	self.balerUnloadingState = BalerVariableChamber.UNLOADING_CLOSED;
	self.bales = {};
	self.wasToFast = false;
	self.isTurnedOn = false;
	self.increaseBaleSize = false;
	self.decreaseBaleSize = false;
	self.canNet = false;
	self.saveMinimumRpm = 0;
end;

function BalerVariableChamber:delete()
	Utils.deleteParticleSystem(self.PickUpParticleSystems);

	if self.balerSound ~= nil then
		delete(self.balerSound);
		self.balerSoundEnabled = false;
	end;
	if self.balerAlarm ~= nil then
		delete(self.balerAlarm);
		self.balerAlarmEnabled = false;
	end;
	if self.balerBaleEject ~= nil then
		delete(self.balerBaleEject);
		self.balerBaleEjectEnabled = false;
	end;
	if self.balerDoor ~= nil then
		delete(self.balerDoor);
		self.balerDoorEnabled = false;
	end;
	if self.balerKnotCleaning ~= nil then
		delete(self.balerKnotCleaning);
		self.balerKnotCleaningEnabled = false;
	end;
end;

function BalerVariableChamber:readStream(streamId, connection)
	self:setPickup(streamReadBool(streamId), true);
	self:setTransport(streamReadBool(streamId), true);
	self.canNet = streamReadBool(streamId);
	local turnedOn = streamReadBool(streamId);
	self:setIsTurnedOn(turnedOn, true);
	local increaseBaleSize = streamReadBool(streamId);
	self:setIncreaseBaleSize(increaseBaleSize, true);
	local decreaseBaleSize = streamReadBool(streamId);
	self:setDecreaseBaleSize(decreaseBaleSize, true);
	local netBale = streamReadBool(streamId);
	self:setNetBale(netBale, true);
	self.currentBaleSize = streamReadInt16(streamId);
	local numBales = streamReadInt16(streamId);
	for i=1, numBales do
		local fruitType = streamReadInt8(streamId);
		BalerVariableChamber.createBale(self, fruitType);
		if self.baleAnimCurve ~= nil then
			local baleTime = streamReadFloat32(streamId);
			BalerVariableChamber.setBaleTime(self, i, baleTime);
		end;
	end;
end;

function BalerVariableChamber:writeStream(streamId, connection)
	streamWriteBool(streamId, self.Pickup);
	streamWriteBool(streamId, self.Transport);
	streamWriteBool(streamId, self.canNet);
	streamWriteBool(streamId, self.isTurnedOn);
	streamWriteBool(streamId, self.increaseBaleSize);
	streamWriteBool(streamId, self.decreaseBaleSize);
	streamWriteBool(streamId, self.netBale);
	streamWriteInt16(streamId, self.currentBaleSize);
	streamWriteInt16(streamId, table.getn(self.bales));
	for i=1, table.getn(self.bales) do
		local bale = self.bales[i];
		streamWriteInt8(streamId, bale.fruitType);
		if self.baleAnimCurve ~= nil then
			streamWriteFloat32(streamId, bale.time);
		end;
	end;
end;

function BalerVariableChamber:readUpdateStream(streamId, timestamp, connection)

end;

function BalerVariableChamber:writeUpdateStream(streamId, connection, dirtyMask)

end;

function BalerVariableChamber:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	local numBales = getXMLInt(xmlFile, key.."#numBales");
	if numBales ~= nil then
		for i=1, numBales do
			local baleKey = key..string.format(".bale(%d)", i-1);

			local fruitType = getXMLString(xmlFile, baleKey.."#fruitType");
			local baleTime = getXMLFloat(xmlFile, baleKey.."#baleTime");
			if fruitType ~= nil and (baleTime ~= nil or self.baleAnimCurve == nil) then
				local fruitTypeDesc = FruitUtil.fruitTypes[fruitType];
				if fruitTypeDesc ~= nil then
					BalerVariableChamber.createBale(self, fruitTypeDesc.index);
					if self.baleAnimCurve ~= nil then
						BalerVariableChamber.setBaleTime(self, table.getn(self.bales), baleTime);
					end;
				end;
			end;
		end;
	end;

	return BaseMission.VEHICLE_LOAD_OK;
end

function BalerVariableChamber:getSaveAttributesAndNodes(nodeIdent)

	local attributes = 'numBales="'..table.getn(self.bales)..'"';
	local nodes = "";
	local baleNum = 0;

	for i=1, table.getn(self.bales) do
		local bale = self.bales[i];
		local fruitType = "unknown";
		if bale.fruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then
			fruitType = FruitUtil.fruitIndexToDesc[bale.fruitType].name;
		end;

		if baleNum>0 then
			nodes = nodes.."\n";
		end;
		nodes = nodes..nodeIdent..'<bale fruitType="'..fruitType..'"';
		if self.baleAnimCurve ~= nil then
			nodes = nodes..' baleTime="'..bale.time..'"';
		end;
		nodes = nodes..' />';
		baleNum = baleNum+1;
	end;

	return attributes,nodes;
end

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

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

function BalerVariableChamber:update(dt)

	if self:getIsActiveForInput() then
		if InputBinding.hasEvent(InputBinding.LOWER_IMPLEMENT) then
			if self.Transport == true then
				self:setPickup(not self.Pickup);
			end;
		end;
		if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA) then
			self:setIsTurnedOn(not self.isTurnedOn);
		end;

		if InputBinding.hasEvent(InputBinding.INCREASE_BALESIZE) then
			if self.currentBaleSize < self.BALE_ORIGINAL then
				if table.getn(self.bales) == 0 then
					self:setIncreaseBaleSize(not self.increaseBaleSize);
				end;
			end;
		end;

		if InputBinding.hasEvent(InputBinding.DECREASE_BALESIZE) then
			if self.currentBaleSize > self.BALE_60CM then
				if table.getn(self.bales) == 0 then
					self:setDecreaseBaleSize(not decreaseBaleSize);
				end;
			end;
		end;

		if InputBinding.hasEvent(InputBinding.NET_BALE) then
			if self.canNet == true and table.getn(self.bales) == 0 then
				self:setNetBale(not self.netBale);
			end;
		end;

		if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
			if self.baleUnloadAnimationNameOriginal ~= nil then
				if self.balerUnloadingState == BalerVariableChamber.UNLOADING_CLOSED then
					if table.getn(self.bales) > 0 then
						self:setIsUnloadingBale(true);
					end;
				elseif self.balerUnloadingState == BalerVariableChamber.UNLOADING_OPEN then
					self:setIsUnloadingBale(false);
				end;
			end;
		end;
	end;
	-- Manage key events for inrange --
	if self.inrange then
		if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
			self:setTransport(not self.Transport);
		end;
	end;
	-- Display key when in range --
	if self.inrange then
		if not self.PickupDown then
			if self.fieldMode then
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("TRANSPORT_MODE"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA2);
			else
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("FIELD_MODE"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA2);
			end;
		end;
	end;
end;

function BalerVariableChamber:updateTick(dt)
	self.wasToFast = false;
	if self:getIsActive() then
		-- Animating the pickup elements --
		if self.isTurnedOn then
			for i=1, 4 do
				rotate(self.RotParts[i], -0.01 * dt, 0, 0);
			end;
			if self.PickupDown then
				rotate(self.FeederRoll, 2.5 * self.lastSpeedReal * self.movingDirection * dt ,0,0);
			end;
		end;
		if self.PickupDown then
			rotate(self.PickupWheel1, 2.5 * self.lastSpeedReal * self.movingDirection * dt ,0,0);
			rotate(self.PickupWheel2, 2.5 * self.lastSpeedReal * self.movingDirection * dt ,0,0);
		end;
		-- Activate particles --
		if self.isTurnedOn and self.movingDirection ~= 0 and self.PickupDown then
			Utils.setEmittingState(self.PickUpParticleSystems, true);
		else
			Utils.setEmittingState(self.PickUpParticleSystems, false);
		end;
	end;
	if self.attacherVehicle and g_currentMission.player ~= nil then
		-- Getting the distance between the player and the implement
		local nearestDistance = 4.0; --max distance allowed
		local px, py, pz = getWorldTranslation(self.rootNode);
		local vx, vy, vz = getWorldTranslation(g_currentMission.player.rootNode);
		local distance = Utils.vector3Length(px-vx, py-vy, pz-vz);
		if distance < nearestDistance then
			self.inrange = true;
		else
			self.inrange = false;
		end;
	end;
	if self:getIsActive() then
		if self.attacherVehicle  ~= nil then
			if self.attacherVehicle.motor ~= nil then
				self:setVehicleIncreaseRpm(dt, self.isTurnedOn);
			end;
		end;
		if self.isTurnedOn then

			if self.fillLevel > (self.originalBaleCapacity*0.2) then
				self.canNet = true;
			else
				self.canNet = false;
			end;

			local toFast = self:doCheckSpeedLimit() and self.lastSpeed*3600 > 30;
			if not toFast then
				if self.isServer and self:allowPickingUp() then
					local totalArea =0;
					local usedFruitType = FruitUtil.FRUITTYPE_UNKNOWN;

					local fruitTypes = {};
					for fillType,enabled in pairs(self.fillTypes) do
						if enabled then
							local fruitType = FruitUtil.fillTypeToFruitType[fillType];
							if fruitType ~= nil and fruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then
								table.insert(fruitTypes, fruitType);
							end;
						end;
					end;
					if table.getn(fruitTypes) > 0 then

						local cuttingAreasSend = {};
						for k, workArea in pairs(self.workAreas) do
							if self:getIsAreaActive(workArea) then
								local x,y,z = getWorldTranslation(workArea.start);
								local x1,y1,z1 = getWorldTranslation(workArea.width);
								local x2,y2,z2 = getWorldTranslation(workArea.height);

								table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2});

								--[[for fillType,v in pairs(self.fillTypes) do
									local fruitType = FruitUtil.fillTypeToFruitType[fillType];
									if fruitType ~= nil and fruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then

										local desc = FruitUtil.fruitIndexToDesc[fruitType];

										local fruitTypeToCut = fruitType;
										if desc.autoSeedFruitType ~= nil then
											fruitTypeToCut = desc.autoSeedFruitType;
											Utils.setGrowthStateAtCutLongArea(fruitType, x, z, x1, z1, x2, z2, desc.autoSeedFruitType, 1)
											Utils.setGrowthStateAtWindrowArea(fruitType, x, z, x1, z1, x2, z2, desc.autoSeedFruitType, 1)
										end;

										local area = Utils.updateFruitWindrowArea(fruitTypeToCut, x, z, x1, z1, x2, z2, 0)*g_currentMission.windrowCutLongRatio;
										area = area + Utils.updateFruitCutLongArea(fruitTypeToCut, x, z, x1, z1, x2, z2, 0);
										if area > 0 then
											totalArea = totalArea+area;
											usedFruitType = fruitType;
											table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2,fruitType});
										end;
									end;
								end;]]
							end;
						end;

						if table.getn(cuttingAreasSend) > 0 then

							totalArea, usedFruitType = BalerAreaEvent.runLocally(cuttingAreasSend, fruitTypes);
							if totalArea > 0 then
								if (table.getn(cuttingAreasSend) > 0) then
									g_server:broadcastEvent(BalerAreaEvent:new(cuttingAreasSend, fruitTypes));
								end;
							end;
						end;
					end;
					if self.netBale == true then
						self.currentBaleSizeSaved = self.currentBaleSize;
						self.capacitySaved = self.capacity;

						if self.fillLevel < self.baleCapacities[1] then
							self:setNetBaleSize(self.BALE_60CM)
							self.capacity = self.fillLevel;
						elseif self.fillLevel > self.baleCapacities[1] and self.fillLevel < self.baleCapacities[2] then
							self:setNetBaleSize(self.BALE_80CM)
							self.capacity = self.fillLevel;
						elseif self.fillLevel > self.baleCapacities[2] and self.fillLevel < self.baleCapacities[3] then
							self:setNetBaleSize(self.BALE_100CM)
							self.capacity = self.fillLevel;
						elseif self.fillLevel > self.baleCapacities[3] and self.fillLevel < self.baleCapacities[4] then
							self:setNetBaleSize(self.BALE_120CM)
							self.capacity = self.fillLevel;
						elseif self.fillLevel > self.baleCapacities[4] and self.fillLevel < self.baleCapacities[5] then
							self:setNetBaleSize(self.BALE_140CM)
							self.capacity = self.fillLevel;
						elseif self.fillLevel > self.baleCapacities[5] and self.fillLevel < self.baleCapacities[6] then
							self:setNetBaleSize(self.BALE_160CM)
							self.capacity = self.fillLevel;
						elseif self.fillLevel > self.baleCapacities[6] and self.fillLevel < self.baleCapacities[7] then
							self:setNetBaleSize(self.BALE_ORIGINALCM)
							self.capacity = self.fillLevel;
						end;
						self:setNetBale(not self.netBale);

						if self.baleTypes ~= nil then
							-- create bale
							if self.baleAnimCurve ~= nil then
								local restDeltaFillLevel = deltaLevel - (self.fillLevel-oldFillLevel)
								self:setFillLevel(restDeltaFillLevel, usedFillType);

								BalerVariableChamber.createBale(self, self.lastUsedFruitType);

								local numBales = table.getn(self.bales);
								local bale = self.bales[numBales]

								self:moveBale(numBales, self:getTimeFromLevel(restDeltaFillLevel), true);
								-- note: self.bales[numBales] can not be accessed anymore since the bale might be dropped already
								g_server:broadcastEvent(BalerCreateBaleEvent:new(self, self.lastUsedFruitType, bale.time), nil, nil, self);
							elseif self.baleUnloadAnimationNameOriginal ~= nil then
								BalerVariableChamber.createBale(self, self.lastUsedFruitType);
								g_server:broadcastEvent(BalerCreateBaleEvent:new(self, self.lastUsedFruitType, 0), nil, nil, self);
							end;
						end;
					end;

					if totalArea > 0 then
						local literPerPixel = g_currentMission:getFruitPixelsToSqm();  --8000/1200 / 6 / (2*2) *12/4 / 4;

						local deltaLevel = totalArea * literPerPixel * self.fillScale;

						if self.baleUnloadAnimationNameOriginal == nil then
							-- move all bales
							local deltaTime = self:getTimeFromLevel(deltaLevel);
							self:moveBales(deltaTime);
						end;

						local usedFillType = FruitUtil.fruitTypeToFillType[usedFruitType];

						local oldFillLevel = self.fillLevel;
						self:setFillLevel(self.fillLevel+deltaLevel, usedFillType);

						self.lastUsedFruitType = usedFruitType;

						if self.fillLevel == self.capacity then
							if self.baleTypes ~= nil then
								-- create bale
								if self.baleAnimCurve ~= nil then
									local restDeltaFillLevel = deltaLevel - (self.fillLevel-oldFillLevel)
									self:setFillLevel(restDeltaFillLevel, usedFillType);

									BalerVariableChamber.createBale(self, usedFruitType);

									local numBales = table.getn(self.bales);
									local bale = self.bales[numBales]

									self:moveBale(numBales, self:getTimeFromLevel(restDeltaFillLevel), true);
									-- note: self.bales[numBales] can not be accessed anymore since the bale might be dropped already
									g_server:broadcastEvent(BalerCreateBaleEvent:new(self, usedFruitType, bale.time), nil, nil, self);
								elseif self.baleUnloadAnimationNameOriginal ~= nil then
									BalerVariableChamber.createBale(self, usedFruitType);
									g_server:broadcastEvent(BalerCreateBaleEvent:new(self, usedFruitType, 0), nil, nil, self);
								end;
							end;
						end;
					end;

				end;
			end;

			if self.isClient then
				if self.balerKnotCleaning ~= nil and self.balerKnotCleaningTime <= self.time and self:getIsActiveForSound() then
					playSample(self.balerKnotCleaning, 1, self.balerKnotCleaningVolume, 0);
					setSamplePitch(self.balerKnotCleaning, self.balerKnotCleaningPitchOffset);
					self.balerKnotCleaningTime = self.time + 120000;
					self.balerKnotCleaningEnabled = true;
				end;

				if not self.balerSoundEnabled and self:getIsActiveForSound() then
					setSamplePitch(self.balerSound, self.balerSoundPitchOffset);
					playSample(self.balerSound, 0, self.balerSoundVolume, 0);
					self.balerSoundEnabled = true;
				end;
			end;

			self.wasToFast = toFast;
		end;
		if self.isClient then
			if not self.isTurnedOn and self.balerSoundEnabled then
				stopSample(self.balerSound);
				self.balerSoundEnabled = false;
			end;
		end;


		if self.isTurnedOn and self.fillLevel > (self.capacity * 0.93) and self.fillLevel < self.capacity then
			-- start alarm sound
			if not self.balerAlarmEnabled and self:getIsActiveForSound() then
				setSamplePitch(self.balerAlarm, self.balerAlarmPitchOffset);
				playSample(self.balerAlarm, 0, self.balerAlarmVolume, 0);
				self.balerAlarmEnabled = true;
			end;
		else
			-- stop alarm sound
			if self.balerAlarmEnabled then
				stopSample(self.balerAlarm);
				self.balerAlarmEnabled = false;
			end;
		end;

		if self.balerUnloadingState == BalerVariableChamber.UNLOADING_OPENING then

			if not self.balerBaleEjectEnabled and self:getIsActiveForSound() then
				setSamplePitch(self.balerBaleEject, self.balerBaleEjectPitchOffset);
				playSample(self.balerBaleEject, 1, self.balerBaleEjectVolume, 0);
				self.balerBaleEjectEnabled = true;
			end;

			if not self.balerDoorEnabled and self:getIsActiveForSound() then
				setSamplePitch(self.balerDoor, self.balerDoorPitchOffset);
				playSample(self.balerDoor, 1, self.balerDoorVolume, 0);
				self.balerDoorEnabled = true;
			end;

			if self.currentBaleSize == self.BALE_ORIGINAL then
				local isPlaying = self:getIsAnimationPlaying(self.baleUnloadAnimationNameOriginal);
				local animTime = self:getRealAnimationTime(self.baleUnloadAnimationNameOriginal);
				self.isUnloadingPlaying = isPlaying;
				self.unloadingAnimTime = animTime;
			elseif self.currentBaleSize == self.BALE_60CM then
				local isPlaying = self:getIsAnimationPlaying(self.baleUnloadAnimationName60cm);
				local animTime = self:getRealAnimationTime(self.baleUnloadAnimationName60cm);
				self.isUnloadingPlaying = isPlaying;
				self.unloadingAnimTime = animTime;
			elseif self.currentBaleSize == self.BALE_80CM then
				local isPlaying = self:getIsAnimationPlaying(self.baleUnloadAnimationName80cm);
				local animTime = self:getRealAnimationTime(self.baleUnloadAnimationName80cm);
				self.isUnloadingPlaying = isPlaying;
				self.unloadingAnimTime = animTime;
			elseif self.currentBaleSize == self.BALE_100CM then
				local isPlaying = self:getIsAnimationPlaying(self.baleUnloadAnimationName100cm);
				local animTime = self:getRealAnimationTime(self.baleUnloadAnimationName100cm);
				self.isUnloadingPlaying = isPlaying;
				self.unloadingAnimTime = animTime;
			elseif self.currentBaleSize == self.BALE_120CM then
				local isPlaying = self:getIsAnimationPlaying(self.baleUnloadAnimationName120cm);
				local animTime = self:getRealAnimationTime(self.baleUnloadAnimationName120cm);
				self.isUnloadingPlaying = isPlaying;
				self.unloadingAnimTime = animTime;
			elseif self.currentBaleSize == self.BALE_140CM then
				local isPlaying = self:getIsAnimationPlaying(self.baleUnloadAnimationName140cm);
				local animTime = self:getRealAnimationTime(self.baleUnloadAnimationName140cm);
				self.isUnloadingPlaying = isPlaying;
				self.unloadingAnimTime = animTime;
			elseif self.currentBaleSize == self.BALE_160CM then
				local isPlaying = self:getIsAnimationPlaying(self.baleUnloadAnimationName160cm);
				local animTime = self:getRealAnimationTime(self.baleUnloadAnimationName160cm);
				self.isUnloadingPlaying = isPlaying;
				self.unloadingAnimTime = animTime;
			end;

			if not self.isUnloadingPlaying or self.unloadingAnimTime >= self.baleDropAnimTime then
				if table.getn(self.bales) > 0 then
					BalerVariableChamber.dropBale(self, 1);
					if self.isServer then
						self:setFillLevel(0, self.currentFillType);
					end;
				end;
				if not isPlaying then
					self.balerUnloadingState = BalerVariableChamber.UNLOADING_OPEN;

					if self.balerBaleEjectEnabled then
						stopSample(self.balerBaleEject);
						self.balerBaleEjectEnabled = false;
					end;
					if self.balerDoorEnabled then
						stopSample(self.balerDoor);
						self.balerDoorEnabled = false;
					end;

				end;
			end;

		elseif self.balerUnloadingState == BalerVariableChamber.UNLOADING_CLOSING then

			if not self.balerDoorEnabled and self:getIsActiveForSound() then
				setSamplePitch(self.balerDoor, self.balerDoorPitchOffset);
				playSample(self.balerDoor, 1, self.balerDoorVolume, 0);
				self.balerDoorEnabled = true;
			end;

			if not self:getIsAnimationPlaying(self.baleCloseAnimationName) then
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_CLOSED;
			end;

			if not self.balerDoorEnabled and self:getIsActiveForSound() then
				setSamplePitch(self.balerDoor, self.balerDoorPitchOffset);
				playSample(self.balerDoor, 1, self.balerDoorVolume, 0);
				self.balerDoorEnabled = true;
			end;

		elseif self.balerUnloadingState == BalerVariableChamber.UNLOADING_CLOSING then
			if self.balerDoorEnabled then
				stopSample(self.balerDoor);
				self.balerDoorEnabled = false;
			end;
		end;
		if self.isServer then
			if self.time > self.baleLastPositionTime+100 then
				for i=1, table.getn(self.bales) do
					local bale = self.bales[i];
					bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id);
				end;
				self.baleLastPositionTime = self.time;
			end;
		end;
	end;
end;

function BalerVariableChamber:draw()
	if self.PickupDown then
		g_currentMission:addHelpButtonText(string.format(g_i18n:getText("PICKUP_LIFT"), self.typeDesc), InputBinding.LOWER_IMPLEMENT);
	else
		g_currentMission:addHelpButtonText(string.format(g_i18n:getText("PICKUP_LOWER"), self.typeDesc), InputBinding.LOWER_IMPLEMENT);
	end;
	if self.isClient then
		if self.isTurnedOn and not self.fieldMode then
			g_currentMission:addWarning(g_i18n:getText("SWITCH_TO_FIELD_MODE"), 0.07+0.022, 0.019+0.029);
		end;
	end;
	if self.isClient then
		if self.wasToFast then
			g_currentMission:addWarning(g_i18n:getText("Dont_drive_to_fast") .. "\n" .. string.format(g_i18n:getText("Cruise_control_levelN"), "2", InputBinding.getKeyNamesOfDigitalAction(InputBinding.SPEED_LEVEL2)), 0.07+0.022, 0.019+0.029);
		end;

		if self.isTurnedOn then
			g_currentMission:addHelpButtonText(string.format(g_i18n:getText("turn_off_OBJECT"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA);
			if table.getn(self.bales) == 0 and self.canNet == true then
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("NET_BALE"), self.typeDesc), InputBinding.NET_BALE);
			end;
		else
			g_currentMission:addHelpButtonText(string.format(g_i18n:getText("turn_on_OBJECT"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA);
		end;

		if self.baleUnloadAnimationNameOriginal ~= nil then
			if self.balerUnloadingState == BalerVariableChamber.UNLOADING_CLOSED then
				if table.getn(self.bales) > 0 then
					g_currentMission:addHelpButtonText(g_i18n:getText("baler_unload"), InputBinding.IMPLEMENT_EXTRA2);
				end;
			elseif self.balerUnloadingState == BalerVariableChamber.UNLOADING_OPEN then
				g_currentMission:addHelpButtonText(g_i18n:getText("baler_unload_stop"), InputBinding.IMPLEMENT_EXTRA2);
			end;
		end;

		if table.getn(self.bales) == 0 then
			if self.currentBaleSize ~= nil then
				local decrease = InputBinding.getKeyNamesOfDigitalAction(InputBinding.DECREASE_BALESIZE);
				local increase = InputBinding.getKeyNamesOfDigitalAction(InputBinding.INCREASE_BALESIZE);
				g_currentMission:addExtraPrintText(string.format(g_i18n:getText("CURRENT_BALESIZE"), self.currentBaleSize, decrease, increase));
			end;
		end;
	end;
end;

function BalerVariableChamber:onAttach(attacherVehicle)
	self.attacherVehicle = attacherVehicle;
	if self.attacherVehicleCopy1 == nil then
		self.attacherVehicleCopy1 = self.attacherVehicle;
	end;
	if self.attacherVehicle.motor ~= nil then
		self.saveMinimumRpm = self.attacherVehicle.motor.minRpm;
	else
		if self.attacherVehicle.saveMinRpm ~= nil then
			self.saveMinimumRpm = self.attacherVehicle.saveMinimumRpm;
		else
			self.attacherVehicle.saveMinimumRpm  = 100;
		end;
	end;
end;

function BalerVariableChamber:onDetach()
	if self.deactivateOnDetach then
		BalerVariableChamber.onDeactivate(self);
	else
		BalerVariableChamber.onDeactivateSounds(self);
	end;
	if self.PickUpParticleSystems ~= nil then
		Utils.setEmittingState(self.PickUpParticleSystems, false);
	end;
	for k, steerable in pairs(g_currentMission.steerables) do
		if self.attacherVehicleCopy1 == steerable then
			steerable.motor.minRpm = self.saveMinimumRpm;
			self.attacherVehicleCopy1 = nil;
		end;
	end;
end;

function BalerVariableChamber:onLeave()
	if self.deactivateOnLeave then
		BalerVariableChamber.onDeactivate(self);
	else
		BalerVariableChamber.onDeactivateSounds(self);
	end;
end;

function BalerVariableChamber:onDeactivate()
	for _, part in pairs(self.balerUVScrollParts) do
		setShaderParameter(part.node, "uvScrollSpeed", 0, 0, 0, 0, false);
	end;
	self.wasToFast = false;
	self.isTurnedOn = false;
	BalerVariableChamber.onDeactivateSounds(self)
	self:setVehicleIncreaseRpm(nil, false);
end;

function BalerVariableChamber:onDeactivateSounds()
	if self.balerSoundEnabled then
		stopSample(self.balerSound);
		self.balerSoundEnabled = false;
	end;
	if self.balerAlarmEnabled then
		stopSample(self.balerAlarm);
		self.balerAlarmEnabled = false;
	end;
	if self.balerBaleEjectEnabled then
		stopSample(self.balerBaleEject);
		self.balerBaleEjectEnabled = false;
	end;
	if self.balerDoorEnabled then
		stopSample(self.balerDoor);
		self.balerDoorEnabled = false;
	end;
	if self.balerKnotCleaningEnabled then
		stopSample(self.balerKnotCleaning);
		self.balerKnotCleaningEnabled = false;
	end;
end;

function BalerVariableChamber:setIsUnloadingBale(isUnloadingBale, noEventSend)
	if self.baleUnloadAnimationNameOriginal ~= nil and self.currentBaleSize == self.BALE_ORIGINAL then
		if isUnloadingBale then
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_OPENING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_OPENING;
				self:playAnimation(self.baleUnloadAnimationNameOriginal, self.baleUnloadAnimationSpeed, nil, true);
			end;
		else
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_CLOSING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_CLOSING;
				self:playAnimation(self.baleCloseAnimationName, self.baleCloseAnimationSpeed, nil, true);
			end;
		end;
	elseif self.baleUnloadAnimationName60cm ~= nil and self.currentBaleSize == self.BALE_60CM then
		if isUnloadingBale then
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_OPENING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_OPENING;
				self:playAnimation(self.baleUnloadAnimationName60cm, self.baleUnloadAnimationSpeed, nil, true);
			end;
		else
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_CLOSING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_CLOSING;
				self:playAnimation(self.baleCloseAnimationName, self.baleCloseAnimationSpeed, nil, true);
			end;
		end;
	elseif self.baleUnloadAnimationName80cm ~= nil and self.currentBaleSize == self.BALE_80CM then
		if isUnloadingBale then
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_OPENING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_OPENING;
				self:playAnimation(self.baleUnloadAnimationName80cm, self.baleUnloadAnimationSpeed, nil, true);
			end;
		else
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_CLOSING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_CLOSING;
				self:playAnimation(self.baleCloseAnimationName, self.baleCloseAnimationSpeed, nil, true);
			end;
		end;
	elseif self.baleUnloadAnimationName100cm ~= nil and self.currentBaleSize == self.BALE_100CM then
		if isUnloadingBale then
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_OPENING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_OPENING;
				self:playAnimation(self.baleUnloadAnimationName100cm, self.baleUnloadAnimationSpeed, nil, true);
			end;
		else
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_CLOSING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_CLOSING;
				self:playAnimation(self.baleCloseAnimationName, self.baleCloseAnimationSpeed, nil, true);
			end;
		end;
	elseif self.baleUnloadAnimationName120cm ~= nil and self.currentBaleSize == self.BALE_120CM then
		if isUnloadingBale then
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_OPENING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_OPENING;
				self:playAnimation(self.baleUnloadAnimationName120cm, self.baleUnloadAnimationSpeed, nil, true);
			end;
		else
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_CLOSING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_CLOSING;
				self:playAnimation(self.baleCloseAnimationName, self.baleCloseAnimationSpeed, nil, true);
			end;
		end;
	elseif self.baleUnloadAnimationName140cm ~= nil and self.currentBaleSize == self.BALE_140CM then
		if isUnloadingBale then
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_OPENING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_OPENING;
				self:playAnimation(self.baleUnloadAnimationName140cm, self.baleUnloadAnimationSpeed, nil, true);
			end;
		else
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_CLOSING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_CLOSING;
				self:playAnimation(self.baleCloseAnimationName, self.baleCloseAnimationSpeed, nil, true);
			end;
		end;
	elseif self.baleUnloadAnimationName160cm ~= nil and self.currentBaleSize == self.BALE_160CM then
		if isUnloadingBale then
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_OPENING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_OPENING;
				self:playAnimation(self.baleUnloadAnimationName160cm, self.baleUnloadAnimationSpeed, nil, true);
			end;
		else
			if self.balerUnloadingState ~= BalerVariableChamber.UNLOADING_CLOSING then
				BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend)
				self.balerUnloadingState = BalerVariableChamber.UNLOADING_CLOSING;
				self:playAnimation(self.baleCloseAnimationName, self.baleCloseAnimationSpeed, nil, true);
			end;
		end;
	end;
end;

function BalerVariableChamber:getTimeFromLevel(level)
	-- level = capacity -> time = firstBaleMarker
	-- level = 0           -> time = 0
	if self.firstBaleMarker ~= nil then
		return level / self.capacity * self.firstBaleMarker;
	end;
	return 0;
end;

function BalerVariableChamber:moveBales(dt)
	for i=table.getn(self.bales), 1, -1 do
		self:moveBale(i, dt);
	end;
end;

function BalerVariableChamber:moveBale(i, dt, noEventSend)
	local bale = self.bales[i];
	BalerVariableChamber.setBaleTime(self, i, bale.time + dt, noEventSend)
end;

function BalerVariableChamber.setBaleTime(self, i, baleTime, noEventSend)
	if self.baleAnimCurve ~= nil then
		local bale = self.bales[i];
		bale.time = baleTime;
		local v = self.baleAnimCurve:get(bale.time);
		setTranslation(bale.id, v[1], v[2], v[3]);
		setRotation(bale.id, v[4], v[5], v[6]);
		if bale.time >= 1 then
			BalerVariableChamber.dropBale(self, i);
		end;
		if self.isServer then
			if noEventSend == nil or not noEventSend then
				g_server:broadcastEvent(BalerSetBaleTimeEvent:new(self, i, bale.time), nil, nil, self);
			end;
		end;
	end;
end;

-- overwrite Fillable.allowFillType
function BalerVariableChamber:allowFillType(fillType)
	return self.fillTypes[fillType] == true;
end;

function BalerVariableChamber:allowPickingUp()
	if self.baleUnloadAnimationNameOriginal == nil then
		return true;
	end;
	return table.getn(self.bales) == 0 and self.balerUnloadingState == BalerVariableChamber.UNLOADING_CLOSED;
end;

function BalerVariableChamber.createBale(self, usedFruitType)
	if self.currentBaleSize == self.BALE_ORIGINAL then
		local baleType = self.baleTypes[usedFruitType];
		if baleType == nil then
			baleType = self.defaultBaleType;
		end;
		local baleRoot = Utils.loadSharedI3DFile(baleType.filename, self.baseDirectory);

		local baleId = getChildAt(baleRoot, 0);
		setRigidBodyType(baleId, "None");
		link(self.baleAnimRoot, baleId);
		delete(baleRoot);

		local bale = {};
		bale.id = baleId;
		bale.time = 0;
		bale.fruitType = usedFruitType;
		bale.filename = Utils.getFilename(baleType.filename, self.baseDirectory);
		bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id);
		table.insert(self.bales, bale);
		local i = table.getn(self.bales);
	elseif self.currentBaleSize == self.BALE_60CM then
		local baleType = self.baleTypes60cm[usedFruitType];
		if baleType == nil then
			baleType = self.defaultBaleType;
		end;
		local baleRoot = Utils.loadSharedI3DFile(baleType.filename, self.baseDirectory);

		local baleId = getChildAt(baleRoot, 0);
		setRigidBodyType(baleId, "None");
		link(self.baleAnimRoot, baleId);
		delete(baleRoot);

		local bale = {};
		bale.id = baleId;
		bale.time = 0;
		bale.fruitType = usedFruitType;
		bale.filename = Utils.getFilename(baleType.filename, self.baseDirectory);
		bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id);
		table.insert(self.bales, bale);
		local i = table.getn(self.bales);
	elseif self.currentBaleSize == self.BALE_80CM then
		local baleType = self.baleTypes80cm[usedFruitType];
		if baleType == nil then
			baleType = self.defaultBaleType;
		end;
		local baleRoot = Utils.loadSharedI3DFile(baleType.filename, self.baseDirectory);

		local baleId = getChildAt(baleRoot, 0);
		setRigidBodyType(baleId, "None");
		link(self.baleAnimRoot, baleId);
		delete(baleRoot);

		local bale = {};
		bale.id = baleId;
		bale.time = 0;
		bale.fruitType = usedFruitType;
		bale.filename = Utils.getFilename(baleType.filename, self.baseDirectory);
		bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id);
		table.insert(self.bales, bale);
		local i = table.getn(self.bales);
	elseif self.currentBaleSize == self.BALE_100CM then
		local baleType = self.baleTypes100cm[usedFruitType];
		if baleType == nil then
			baleType = self.defaultBaleType;
		end;
		local baleRoot = Utils.loadSharedI3DFile(baleType.filename, self.baseDirectory);

		local baleId = getChildAt(baleRoot, 0);
		setRigidBodyType(baleId, "None");
		link(self.baleAnimRoot, baleId);
		delete(baleRoot);

		local bale = {};
		bale.id = baleId;
		bale.time = 0;
		bale.fruitType = usedFruitType;
		bale.filename = Utils.getFilename(baleType.filename, self.baseDirectory);
		bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id);
		table.insert(self.bales, bale);
		local i = table.getn(self.bales);
	elseif self.currentBaleSize == self.BALE_120CM then
		local baleType = self.baleTypes120cm[usedFruitType];
		if baleType == nil then
			baleType = self.defaultBaleType;
		end;
		local baleRoot = Utils.loadSharedI3DFile(baleType.filename, self.baseDirectory);

		local baleId = getChildAt(baleRoot, 0);
		setRigidBodyType(baleId, "None");
		link(self.baleAnimRoot, baleId);
		delete(baleRoot);

		local bale = {};
		bale.id = baleId;
		bale.time = 0;
		bale.fruitType = usedFruitType;
		bale.filename = Utils.getFilename(baleType.filename, self.baseDirectory);
		bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id);
		table.insert(self.bales, bale);
		local i = table.getn(self.bales);
	elseif self.currentBaleSize == self.BALE_140CM then
		local baleType = self.baleTypes140cm[usedFruitType];
		if baleType == nil then
			baleType = self.defaultBaleType;
		end;
		local baleRoot = Utils.loadSharedI3DFile(baleType.filename, self.baseDirectory);

		local baleId = getChildAt(baleRoot, 0);
		setRigidBodyType(baleId, "None");
		link(self.baleAnimRoot, baleId);
		delete(baleRoot);

		local bale = {};
		bale.id = baleId;
		bale.time = 0;
		bale.fruitType = usedFruitType;
		bale.filename = Utils.getFilename(baleType.filename, self.baseDirectory);
		bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id);
		table.insert(self.bales, bale);
		local i = table.getn(self.bales);
	elseif self.currentBaleSize == self.BALE_160CM then
		local baleType = self.baleTypes160cm[usedFruitType];
		if baleType == nil then
			baleType = self.defaultBaleType;
		end;
		local baleRoot = Utils.loadSharedI3DFile(baleType.filename, self.baseDirectory);

		local baleId = getChildAt(baleRoot, 0);
		setRigidBodyType(baleId, "None");
		link(self.baleAnimRoot, baleId);
		delete(baleRoot);

		local bale = {};
		bale.id = baleId;
		bale.time = 0;
		bale.fruitType = usedFruitType;
		bale.filename = Utils.getFilename(baleType.filename, self.baseDirectory);
		bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id);
		table.insert(self.bales, bale);
		local i = table.getn(self.bales);
	end;
end;

function BalerVariableChamber.dropBale(self, baleIndex)
	BaleCounter.dropBale(self, baleIndex);
	local bale = self.bales[baleIndex];
	local deltaRealTime = (self.time - self.baleLastPositionTime)/1000;
	local x,y,z = getWorldTranslation(bale.id);
	local rx,ry,rz = getWorldRotation(bale.id);
	--link(getRootNode(), bale.id);


	if self.isServer then
		local baleObject = Bale:new(self.isServer, self.isClient);
		baleObject:load(bale.filename, x,y,z,rx,ry,rz);
		baleObject:register();

		local lx, ly, lz = bale.lastX, bale.lastY, bale.lastZ;
		--setLinearVelocity(baleObject.nodeId);
	end;
	delete(bale.id);
	table.remove(self.bales, baleIndex);

	-- increase bale count if variable exists (baling mission)
	if g_currentMission.baleCount ~= nil then
		g_currentMission.baleCount = g_currentMission.baleCount + 1;
	end;

	if self.currentBaleSizeSaved ~= nil and self.capacitySaved ~= nil then
		self.currentBaleSize = self.currentBaleSizeSaved;
		self.capacity = self.capacitySaved;
		self:setNetBaleSize(self.currentBaleSizeSaved);
		self.currentBaleSizeSaved = nil;
		self.capacitySaved = nil;
	end;
end;

function BalerVariableChamber:setIsTurnedOn(isTurnedOn, noEventSend)
	SetTurnedOnEvent.sendEvent(self, isTurnedOn, noEventSend)
	self.isTurnedOn = isTurnedOn;
	for _, part in pairs(self.balerUVScrollParts) do
		if self.isTurnedOn then
			setShaderParameter(part.node, "uvScrollSpeed", part.speed[1], part.speed[2], 0, 0, false);
		else
			setShaderParameter(part.node, "uvScrollSpeed", 0, 0, 0, 0, false);
		end;
	end;

	if self.pickupAnimationName ~= "" then
		local animTime = nil;
		if self:getIsAnimationPlaying(self.pickupAnimationName) then
			animTime = self:getAnimationTime(self.pickupAnimationName);
		end;
		if isTurnedOn then
			self:playAnimation(self.pickupAnimationName, self.pickupAnimationLowerSpeed, animTime, true)
		else
			self:playAnimation(self.pickupAnimationName, self.pickupAnimationLiftSpeed, animTime, true)
		end;
	end;
end;

function BalerVariableChamber:setIncreaseBaleSize(increaseBaleSize, noEventSend)
	SetIncreaseBaleSizeEvent.sendEvent(self, increaseBaleSize, noEventSend);
	self.increaseBaleSize = increaseBaleSize;

	if self.increaseBaleSize == true then
		self.currentBaleSize = self.currentBaleSize+20;
		self.increaseBaleSize = false;
	end;
	if self.currentBaleSize == self.BALE_60CM then
		self.capacity = self.baleCapacities[1];
	elseif self.currentBaleSize == self.BALE_80CM then
		self.capacity = self.baleCapacities[2];
	elseif self.currentBaleSize == self.BALE_100CM then
		self.capacity = self.baleCapacities[3];
	elseif self.currentBaleSize == self.BALE_120CM then
		self.capacity = self.baleCapacities[4];
	elseif self.currentBaleSize == self.BALE_140CM then
		self.capacity = self.baleCapacities[5];
	elseif self.currentBaleSize == self.BALE_160CM then
		self.capacity = self.baleCapacities[6];
	elseif self.currentBaleSize == self.BALE_ORIGINAL then
		self.capacity = self.baleCapacities[7];
	else
		print("No baleSize aquired!");
	end;
end;

function BalerVariableChamber:setDecreaseBaleSize(decreaseBaleSize, noEventSend)
	SetDecreaseBaleSizeEvent.sendEvent(self, decreaseBaleSize, noEventSend);
	self.decreaseBaleSize = decreaseBaleSize;

	local i = 1;
	for i=1, 7 do
		if self.baleCapacities[i] == self.capacity then
			self.currentArrayPlace = i;
		end;
		i = i+1;
	end;

	if self.decreaseBaleSize == true then
		if self.fillLevel > self.baleCapacities[self.currentArrayPlace-1] then
			print("Cannot lower baleSize");
			--TO-DO: Implement warningtext
		else
			self.currentBaleSize = self.currentBaleSize-20;
		end;
		self.decreaseBaleSize = false;
	end;

	if self.currentBaleSize == self.BALE_60CM then
		self.capacity = self.baleCapacities[1];
	elseif self.currentBaleSize == self.BALE_80CM then
		self.capacity = self.baleCapacities[2];
	elseif self.currentBaleSize == self.BALE_100CM then
		self.capacity = self.baleCapacities[3];
	elseif self.currentBaleSize == self.BALE_120CM then
		self.capacity = self.baleCapacities[4];
	elseif self.currentBaleSize == self.BALE_140CM then
		self.capacity = self.baleCapacities[5];
	elseif self.currentBaleSize == self.BALE_160CM then
		self.capacity = self.baleCapacities[6];
	elseif self.currentBaleSize == self.BALE_ORIGINAL then
		self.capacity = self.baleCapacities[7];
	else
		print("No baleSize aquired!");
	end;
end;

function BalerVariableChamber:setNetBaleSize(netBaleSize, noEventSend)
	SetNetBaleSizeEvent.sendEvent(self, netBaleSize, noEventSend);
	self.currentBaleSize = netBaleSize;
end;

function BalerVariableChamber:setNetBale(netBale, noEventSend)
	SetNetBaleEvent.sendEvent(self, netBale, noEventSend);
	self.netBale = netBale;
end;

function BalerVariableChamber:setPickup(isPickupState,noEventSend)
	SetPickupEvent.sendEvent(self, isPickupState, noEventSend);
	-- Play pickup animation --
	self.PickupDown = isPickupState;
	if self.PickupDown then
		if self.PickupAnimation ~= nil and self.playAnimation ~= nil then
			self:playAnimation(self.PickupAnimation, -1, nil, true);
			self.Pickup = true;
		end;
	else
		if self.PickupAnimation ~= nil and self.playAnimation ~= nil then
			self:playAnimation(self.PickupAnimation, 1, nil, true);
			self.Pickup = false;
		end;
	end;
end;

function BalerVariableChamber:setTransport(isTransportState,noEventSend)
	SetTransportEvent.sendEvent(self, isTransportState, noEventSend);
	-- Transport/field mode --
	self.fieldMode = isTransportState;
	if self.fieldMode then
		setVisibility(self.wheelsInTransportPosition, false);
		setVisibility(self.wheelsInFieldPosition, true);
		self.Transport = true;
	else
		setVisibility(self.wheelsInTransportPosition, true);
		setVisibility(self.wheelsInFieldPosition, false);
		self.Transport = false;
	end;
end;

function BalerVariableChamber:setVehicleIncreaseRpm(dt, isActive)
	if self.attacherVehicle ~= nil and self.saveMinimumRpm ~= 0 and
		self.attacherVehicle.motor ~= nil then
		if dt ~= nil then
			if isActive == true then
				self.attacherVehicle.motor.minRpm = math.max(self.attacherVehicle.motor.minRpm-(dt*2), -1200);
			else
				self.attacherVehicle.motor.minRpm = math.min(self.attacherVehicle.motor.minRpm+(dt*5), self.saveMinimumRpm);
			end;
		else
			self.attacherVehicle.motor.minRpm = self.saveMinimumRpm;
		end;		
	end;
end;

function BalerVariableChamber:getIsAreaActive(superFunc, area)
	if superFunc ~= nil then
			return superFunc(self, area) and self.PickupDown;
	end;
	return self.PickupDown;
end;