-- Skrypt: Z211
-- Wersja: 1.0
-- 07.01.2010
-- Autor: hannibal5
------------------------
-- Ls2013 Script edit and bugfix
-- Script version: 1.1
-- Game version: Patch 2.0 - Public Beta 3
-- Date: 25.08.2013
-- Edit autor: Farok

Z211 = {};
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Z211.MODE_TRANSPORT = 1;
Z211.MODE_ZGRABIANIE = 2;
Z211.MODE_PRZETRZASANIE = 3;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211.prerequisitesPresent(specializations)

    return true;
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:load(xmlFile)

	self.farmerInRange = SpecializationUtil.callSpecializationsFunction("farmerInRange"); --Funkcja sprawdzajaca czy farmer jest w poblizu maszyny
	
	self.toTransport = SpecializationUtil.callSpecializationsFunction("toTransport"); --Funkcja przelacza maszyne w tryb transportu
	self.toZgrabianie = SpecializationUtil.callSpecializationsFunction("toZgrabianie"); --Funkcja przelacza maszyne w tryb zgrabianie
	self.toPrzetrzasanie = SpecializationUtil.callSpecializationsFunction("toPrzetrzasanie"); --Funkcja przelacza maszyne w tryb przetrzasania
	
	self.createCompJoint = SpecializationUtil.callSpecializationsFunction("createCompJoint"); --Funkcja laczy elementy, z jednym punktem polaczenia
	self.createComp2Joint = SpecializationUtil.callSpecializationsFunction("createComp2Joint");	--Funkcja laczy elementy, z dwoma punktami polaczenia
	
	self.playAnimation = SpecializationUtil.callSpecializationsFunction("playAnimation"); --Funkcja odpowiedzialna za odtwarzanie animacji
	
	self.groundContactReport = SpecializationUtil.callSpecializationsFunction("groundContactReport"); --Funkcja sprawdzajaca kontak z ziemia
	
	-----Dane dla czesci obracajacych sie (gwiazdy)-----
    self.speedRotatingParts = {};
    local i = 0;
    while true do
        local baseName = string.format("vehicle.speedRotatingParts.speedRotatingPart(%d)", i);
        local index = getXMLString(xmlFile, baseName.. "#index");
        if index == nil then
            break;
        end;
        local node = Utils.indexToObject(self.components, index);
        if node ~= nil then
            local entry = {};
            entry.node = node;
            entry.rotationSpeedScale = getXMLFloat(xmlFile, baseName.."#rotationSpeedScale");
            if entry.rotationSpeedScale == nil then
                entry.rotationSpeedScale = 1.0/Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#radius"), 1);
            end;
            table.insert(self.speedRotatingParts, entry);
        end;
        i = i + 1;
    end;	
	-----Dane dla czesci obracajacych sie (gwiazdy)-----

	-----Dane dla odtwarzania animacji-----
    self.animParts = {};
    local i = 0;
    while true do
        local baseName = string.format("vehicle.animationParts.animPart(%d)", i);
        local rootNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.."#rootNode"));
        if rootNode == nil then
            break;
        else
            local entry = {};
			entry.moveDirection = 0;
            entry.animCharSet = 0;
            entry.animCharSet = getAnimCharacterSet(rootNode);
            if entry.animCharSet ~= 0 then
                local clip = getAnimClipIndex(entry.animCharSet, getXMLString(xmlFile, baseName.."#animationClip"));
                if clip >= 0 then
                    assignAnimTrackClip(entry.animCharSet, 0, clip);
                    setAnimTrackLoopState(entry.animCharSet, 0, false);
                    entry.speedScale = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#speedScale"), 1);
                    entry.animDuration = getAnimClipDuration(entry.animCharSet, clip);

                    table.insert(self.animParts, entry);
                end;
            end;

        end;
        i = i + 1;
    end;	
	-----Dane dla odtwarzania animacji-----

	-----Dane dla elementow pobierajacych informacje o kontakcie z ziemia-----	
	self.contactReportNodes = {};
    local contactReportNodeFound = false;
    local i = 0;
    while true do
        local baseName = string.format("vehicle.contactReportNodes.contactReportNode(%d)", i);
        local index = getXMLString(xmlFile, baseName.. "#index");
        if index == nil then
            break;
        end;
        local node = Utils.indexToObject(self.components, index);
        if node ~= nil then
            local entry = {};
            entry.node = node;
            entry.hasGroundContact = false;

            self.contactReportNodes[node] = entry;
            contactReportNodeFound = true;
        end;
        i = i + 1;
    end;
    if not contactReportNodeFound then
        local entry = {};
        entry.node = self.components[1].node;
        entry.hasGroundContact = false;
        self.contactReportNodes[entry.node] = entry;
    end;

    self.groundReferenceThreshold = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.groundReferenceNode#threshold"), 0.2);
    self.groundReferenceNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.groundReferenceNode#index"));
	-----Dane dla elementow pobierajacych informacje o kontakcie z ziemia-----

	-----Dane dla aktualizacji obszaru po przejechaniu maszyny-----	
	local numDropAreas = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.dropAreas#count"), 0);
	self.dropAreas = {}
    for i=1, numDropAreas do
        self.dropAreas[i] = {};
        local areanamei = string.format("vehicle.dropAreas.dropArea%d", i);
        self.dropAreas[i].start = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#startIndex"));
        self.dropAreas[i].width = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#widthIndex"));
        self.dropAreas[i].height = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#heightIndex"));
    end;
	-----Dane dla aktualizacji obszaru po przejechaniu maszyny-----	
	
	-----Dane dla ustawienia trybu pracy maszyny-----
	self.ustawMaszyne = {};
	local i = 0;
	while true do
		local baseName = string.format("vehicle.ustawienieMaszyny.ustawienie(%d)", i);
		local joint = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. "#joint"));
		local wheel01 = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. "#wheel01"));
		local wheel02 = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. "#wheel02"));
		local wheel03 = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. "#wheel03"));
		if joint == nil or wheel01 == nil or wheel02 == nil or wheel03 == nil then
			break;
		else
			local entry = {};
			entry.joint = joint;
			entry.wheel01 = wheel01;
			entry.wheel02 = wheel02;
			entry.wheel03 = wheel03;

			local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile,  baseName.. "#rotWheel01"));
			entry.rotWheel01 = {};
			entry.rotWheel01[1] = math.rad(Utils.getNoNil(x, 0));
			entry.rotWheel01[2] = math.rad(Utils.getNoNil(y, 0));
			entry.rotWheel01[3] = math.rad(Utils.getNoNil(z, 0));

			local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile,  baseName.. "#rotWheel02"));
			entry.rotWheel02 = {};
			entry.rotWheel02[1] = math.rad(Utils.getNoNil(x, 0));
			entry.rotWheel02[2] = math.rad(Utils.getNoNil(y, 0));
			entry.rotWheel02[3] = math.rad(Utils.getNoNil(z, 0));

			local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile,  baseName.. "#rotWheel03"));
			entry.rotWheel03 = {};
			entry.rotWheel03[1] = math.rad(Utils.getNoNil(x, 0));
			entry.rotWheel03[2] = math.rad(Utils.getNoNil(y, 0));
			entry.rotWheel03[3] = math.rad(Utils.getNoNil(z, 0));
			
			table.insert(self.ustawMaszyne, entry);
		end;
		i = i + 1
	end;
	-----Dane dla ustawienia trybu pracy maszyny-----	
	
	-----Dane dla belki-----
	self.belka = {};
	local posIndex = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.belka#posIndex"));
	if posIndex ~= nil and posIndex ~= 0 then
		local entry = self.belka;
		entry.posIndex = posIndex;
		entry.comp1joint1 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.belka#comp1joint1"));
		entry.comp1joint2 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.belka#comp1joint2"));
		entry.comp2joint1 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.belka#comp2joint1"));
		entry.comp2joint2 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.belka#comp2joint2"));

	end;
	-----Dane dla belki-----

	-----Dane dla czasteczek-----
    self.grassParticleSystems = {};
    local i = 0;
    while true do
        local baseName = string.format("vehicle.grassParticleSystems.grassParticleSystem(%d)", i);

        local particleSystem = {};
        particleSystem.ps = {};
        local ps = Utils.loadParticleSystem(xmlFile, particleSystem.ps, baseName, self.components, false, nil, self.baseDirectory)
        if ps == nil then
            break;
        end;
        particleSystem.disableTime = 0;
        table.insert(self.grassParticleSystems, particleSystem);
        i = i + 1;
		self.psEnabled = false;
    end;
	-----Dane dla czasteczek-----
	
	-----Dane dla dzwięku-----
	local zgSound = getXMLString(xmlFile, "vehicle.zgSound#file");
    if zgSound ~= nil and zgSound ~= "" then
        zgSound = Utils.getFilename(zgSound, self.baseDirectory);
        self.zgSound = createSample("zgSound");
        self.zgSoundEnabled = false;
        loadSample(self.zgSound, zgSound, false);
        self.zgSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.zgSound#pitchOffset"), 1);
        self.zgSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.zgSound#volume"), 1.0);
    end;
	-----Dane dla dzwięku-----	
	
	self.components2Joints = {}; --Tabela dla danych, dla polaczen dwupunktowych
	
	self.mode = Z211.MODE_TRANSPORT; --Maszyna w trybie transportu
	
	self.Z211contactReportsActive = false; --Sprawdzenie czy gwiazdy maja kontakt z podlozem
	
	self.farmerNear = false; --Wskaznik, czy farmer jest przy maszynie	
	
	self.changeMode = false; --Wskaznik, czy ma nastapic zmiana trybu pracy maszyny
	self.updateRotLimits = false; --Sprawdza czy ma zostac dokonana aktualizacja limitow obrotu
	self.rotLimitTime = 0; --Podaje opoznienie, zanim zostana zaktualizowane limity obrotow (aby maszyna sie ustawila)
	
	self.belkaVis = true; --Wskaznik, czy belka jest widoczna
	
	self.playAnim = false; --Sprawdza czy animacja zostala zalaczona
	self.animNumber = 0; --Pobiera informacje o liczbie animacji od odtworzenia
	self.animPrzetrzasanie = false; --Wskaznik, sprawdza czy maszyna byla ustawiona w pozycji przetrzasania (dotyczy animacji ustawiajacej kola grabiace)
	
	self.wasToFast = false; --Dla wyswietlenia informacji o limicie predkosci
	self.speedMaxTimer = 1500; --Czas jaki ma farmer na zwolnienie po wyswietleniu komunikaru o przekroczeniu predkosci
	self.speedTimer = self.speedMaxTimer; --Odlicza czas po pokazeniu komunikatu o przekroczeniu predkosci

	self.checkColTransLimit = false; --Czy ma zostac sprawdzone, czy wlaczyc lub wylaczyc przemieszczanie kolizji kol grabiacych (zalaczyc lub nie  limit przemieszczania kolizji)
	self.wheelColTransLimit = false; --Za wlaczenie lub wylaczenie przemieszczania kolizji kol grabiacych, kolizja dopasowuje sie do uksztaltowania terenu
	self.wheelAnimRoz = false; --Czy zalaczona jest animacja skladania lub rozkladania kol grabiacych
	self.wheelDown = false; --Sprawdza czy kola grabiace sa opuszczone, przy opuszczonych nie mozna zmieniac trybu pracy maszyny

	self.firstTimeStart = true; --Ustawia maszyne przy pierwszym jej wczytaniu
	self.animWheelMode = false; --Sprawdza czy ma zaktualizowac animacje kol grabiacych przy pierwszym wczytaniu
	
	-----Ustawienie masy komponentow-----
	setMass(self.components[1].node, 0.02);
	setMass(self.components[2].node, 0.01);	

	setMass(self.components[3].node, 0.09);
	
	setMass(self.components[4].node, 0.075);
	setMass(self.components[5].node, 0.075);
	setMass(self.components[6].node, 0.050);
	
	setMass(self.components[7].node, 0.01);	
	
	setMass(self.components[8].node, 0.01);
	setMass(self.components[9].node, 0.01);
	setMass(self.components[10].node, 0.01);
	setMass(self.components[11].node, 0.01);
	setMass(self.components[12].node, 0.01);

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:loadFromAttributesAndNodes(xmlFile, key, resetVehicles) --Wczytanie zapisanych ustawien maszyny

	-----Pobieranie informacji o trybie pracy maszyny-----
    local modeType = getXMLFloat(xmlFile, key.."#modeType");
	local animWheel = getXMLFloat(xmlFile, key.."#animWheel");
	if modeType ~= nil then --Tryb pracy maszyny
		if modeType == Z211.MODE_TRANSPORT then
			self.mode = Z211.MODE_TRANSPORT;
		elseif modeType == Z211.MODE_ZGRABIANIE then
			self.mode = Z211.MODE_ZGRABIANIE;
		elseif modeType == Z211.MODE_PRZETRZASANIE then
			self.mode = Z211.MODE_PRZETRZASANIE;
		end;
	end;
	if animWheel ~= nil then --Animacja opuszczania kol grabiacych
		if animWheel == 1 then
			self.animWheelMode = true;
		else
			self.animWheelMode = false;
		end;
	end;
	-----Pobieranie informacji o trybie pracy maszyny-----
	
    return BaseMission.VEHICLE_LOAD_OK;
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:getSaveAttributesAndNodes(nodeIdent) --Zapis aktualnych ustawien maszyny, przy zapisie gry

	-----Zapisanie aktualnego trybu pracy maszyny-----
    local modeType = self.mode; --Tryb pracy
	local wheelColTransLimit = self.wheelColTransLimit;
	local animWheel = 0;
	if wheelColTransLimit then
		animWheel = 1;  --Animacja opuszczania kol grabiacych
	else
		animWheel = 0;
	end;
	
    local attributes = 'modeType="'..modeType..'" animWheel="'..animWheel..'"';
	-----Zapisanie aktualnego trybu pracy maszyny-----
	
    return attributes, nil;
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:delete()

    Z211.removeContactReports(self);
	
	for k,v in pairs(self.grassParticleSystems) do
        Utils.deleteParticleSystem(v.ps);
    end;
	
	if self.zgSound ~= nil then
        delete(self.zgSound);
    end;
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:mouseEvent(posX, posY, isDown, isUp, button)

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:keyEvent(unicode, sym, modifier, isDown)
	
	if isDown and  sym == Input.KEY_z then
		self.wheelColInvalid = true;
	end;
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:update(dt)

--------------------------------------------------------------------------------------------------------------------------------------------	
----------------------------USTAWIENIE MASZYNY PRZY PIERWSZYM URUCHOMIENIU----------------------------	
	if self.firstTimeStart then --Nastepuje odpowiednie ustawienie elementow maszyny, belka i dyszel nie sa automatycznie podczepianie przy wczytaniu maszyny
		--Ustawienie trybu pracy maszyny
		if self.mode == Z211.MODE_TRANSPORT then
			self:toTransport();
		elseif self.mode == Z211.MODE_ZGRABIANIE then
			self:toZgrabianie();
		elseif self.mode == Z211.MODE_PRZETRZASANIE then
			self:toPrzetrzasanie();
		end;
		
		--Ustawienie pozycji animacji kol grabiacych
		if self.animWheelMode then
			self.wheelAnimRoz = false;
			self:playAnimation(2, 2, 1);
			for i = 5, 9 do
				setJointTranslationLimit(self.componentJoints[i].jointIndex, 1, true, 0, 3);
			end;
			self.wheelDown = true;
			self.wheelColTransLimit = true;
		end;
		
		self.firstTimeStart = false;
	end;	
----------------------------USTAWIENIE MASZYNY PRZY PIERWSZYM URUCHOMIENIU----------------------------
---------------------------------------------------------------------------------------------------------------------------------------------	
	
--------------------------------------------------------------------------------------------------------------------------------------------	
----------------------------------------STEROWANIE USTAWIENIAMI MASZYNY----------------------------------------	
	-----Aktualizacja limitow obrotu-----
	if self.updateRotLimits then
		self.rotLimitTime = self.rotLimitTime + dt;
		if self.rotLimitTime > 300 then --Opoznienie  aktualizacji
			if self.mode == Z211.MODE_TRANSPORT then
				setJointRotationLimit(self.componentJoints[1].jointIndex, 1, true, math.rad(-80), math.rad(80)); --Limit obrotu dla zaczepu		
				setJointRotationLimit(self.componentJoints[2].jointIndex, 1, true, math.rad(-180), math.rad(180)); --Limit obrotu dla pierwszego kola
				setJointRotationLimit(self.componentJoints[3].jointIndex, 1, true, math.rad(-180), math.rad(180)); --Limit obrotu dla drugiego kola
				self.updateRotLimits = false;
				self.rotLimitTime = 0;
			end;
			if self.mode == Z211.MODE_ZGRABIANIE then
				setJointRotationLimit(self.componentJoints[2].jointIndex, 1, true, math.rad(-180), math.rad(180)); --Limit obrotu dla pierwszego kola
				setJointRotationLimit(self.componentJoints[3].jointIndex, 1, true, math.rad(-180), math.rad(180)); --Limit obrotu dla drugiego kola			
				self.updateRotLimits = false;
				self.rotLimitTime = 0;			
			end;
			if self.mode == Z211.MODE_PRZETRZASANIE then	
				setJointRotationLimit(self.componentJoints[3].jointIndex, 1, true, math.rad(-180), math.rad(180)); --Limit obrotu dla drugiego kola
				self.updateRotLimits = false;
				self.rotLimitTime = 0;		
			end;
		end;
	end;
	-----Aktualizacja limitow obrotu-----
	
	-----Aktualizowanie pozycji belki kiedy jest odlaczona od maszyny-----
	if self.mode ~= Z211.MODE_TRANSPORT then
		local tx, ty, tz = getWorldTranslation(self.belka.posIndex);
		local rx, ry, rz = getWorldRotation(self.belka.posIndex);
		setTranslation(self.components[7].node, tx, ty, tz);
		setRotation(self.components[7].node, rx, ry, rz);
	end;
	-----Aktualizowanie pozycji belki kiedy jest odlaczona od maszyny-----
	
	-----Sterowanie zalaczona animacja i aktualizacja pozycji kolizji-----
	if self.playAnim then
        for i = 1, self.animNumber do
            local isInvalid = false;
			local animPart = self.animParts[i];
            local charSet = animPart.animCharSet;
            if animPart.moveDirection > 0.1 then
                local trackTime = getAnimTrackTime(charSet, 0);
                if trackTime < animPart.animDuration then
                    isInvalid = true;
				else
					self.playAnim = false;
					disableAnimTrack(charSet, 0);
                end;
            elseif animPart.moveDirection < -0.1 then
                local trackTime = getAnimTrackTime(charSet, 0);
                if trackTime > 0 then
                    isInvalid = true;
				else
					self.playAnim = false;
					disableAnimTrack(charSet, 0);
                end;
            end;

            if isInvalid then
				if self.wheelAnimRoz then --Aktualizowanie polozenia kolizji dla kol grabiacych przy rozkladaniu i skladaniu
					for i = 5, 9 do
						setJointFrame(self.componentJoints[i].jointIndex, 0, self.componentJoints[i].jointNode);
					end;
				end;
            end;
        end;
	end;
	-----Sterowanie zalaczona animacja i aktualizacja pozycji kolizji-----	

	self:farmerInRange(); --Sprawdzenie czy farmer stoi przy maszynie
	
	-----Farmer stoi przy maszynie----
	if self.farmerNear then
		-----Zmiana trybu pracy maszyny-----
		if self.mode == Z211.MODE_TRANSPORT then
			if self.changeMode then
				self:toTransport(); --Zmiana trybu pracy na transport
				self.changeMode = false;
			end;			
		end;
		
		if self.mode == Z211.MODE_ZGRABIANIE then
			if self.changeMode then
				self:toZgrabianie(); --Zmiana trybu pracy na zgrabianie
				self.changeMode = false;
			end;			
		end;

		if self.mode == Z211.MODE_PRZETRZASANIE then
			if self.changeMode then
				self:toPrzetrzasanie(); --Zmiana trybu pracy na przetrzasanie
				self.changeMode = false;
			end;			
		end;
		-----Zmiana trybu pracy maszyny-----
		
		-----Opuszczanie i podnoszenie kol grabiacych-----	
		if InputBinding.hasEvent(InputBinding.Z211KolaOpusc) then
			self.wheelAnimRoz = false; --Przy animacji nie aktualizowac pozycji kolizji
			self.wheelDown = true; --Nie mozna zmienic trybu pracy maszyny przy opuszczonych kolach grabiacych
			self:playAnimation(2, 2, 1); --Animacja, opuszczenie kol grabiacych
			self.wheelColTransLimit = true;
			self.checkColTransLimit = true;
		end;
			
		if InputBinding.hasEvent(InputBinding.Z211KolaPodnies) then
			self.wheelAnimRoz = false; --Przy animacji nie aktualizowac pozycji kolizji
			self.wheelDown = false; --Mozna zmienic tryb pracy maszyny przy podniesionych koach grabiacych
			self:playAnimation(2, 2, -1); --Animacja, podnoszenie kol grabiacych
			self.wheelColTransLimit = false;
			self.checkColTransLimit = true;
		end;
		-----Opuszczanie i podnoszenie kol grabiacych-----
	end;
	-----Farmer stoi przy maszynie----
	
	-----Aktywuje lub deaktywuje limity ruchu dla kolizji kol grabiacych-----
	if self.checkColTransLimit then
		if self.wheelColTransLimit then
			for i = 5, 9 do
				setJointTranslationLimit(self.componentJoints[i].jointIndex, 1, true, 0, 3);
			end;
		else
			for i = 5, 9 do
				setJointTranslationLimit(self.componentJoints[i].jointIndex, 1, true, 0, 0);
			end;
		end;
		self.checkColTransLimit = false;
	end;
	-----Aktywuje lub deaktywuje limity ruchu dla kolizji kol grabiacych-----

----------------------------------------STEROWANIE USTAWIENIAMI MASZYNY----------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------KONTAKT Z ZIEMIA DLA KOL GRABIACYCH----------------------------------------
	if self:getIsActive() then
		-----Sprawdzenie danych dla kontaktu z ziemia-----
        local hasGroundContact = false;
        for k, v in pairs(self.contactReportNodes) do
            if v.hasGroundContact then
                hasGroundContact = true;
                break;
            end;
        end;

        if not hasGroundContact then
            if self.groundReferenceNode ~= nil then
                local x,y,z = getWorldTranslation(self.groundReferenceNode);
                local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z);
                if terrainHeight + self.groundReferenceThreshold >= y then
                    hasGroundContact = true;
                end;
            end;
        end;
		
		if not hasGroundContact then
			if self.wheelColTransLimit then
				hasGroundContact = true;
			end;
		end;
		-----Sprawdzenie danych dla kontaktu z ziemia-----
----------------------------------------KONTAKT Z ZIEMIA DLA KOL GRABIACYCH----------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------		

---------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------KOLA GRABIACE MAJA KONTAKT Z ZIEMIA----------------------------------------
		if hasGroundContact then			
			-----Zgrabianie trawy-----
			if self.mode == Z211.MODE_ZGRABIANIE then --Zgrabianie
				local fruitTypeFix = false;
			
				local cuttingArea = self.cuttingAreas[1];
				local x,y,z = getWorldTranslation(cuttingArea.start);
                local x1,y1,z1 = getWorldTranslation(cuttingArea.width);
                local x2,y2,z2 = getWorldTranslation(cuttingArea.height);
				
				if not fruitTypeFix then
                    fruitType = FruitUtil.FRUITTYPE_GRASS;
                end;
				
				local area = Utils.updateFruitWindrowArea(fruitType, x, z, x1, z1, x2, z2, 0);
                area = area + Utils.updateFruitWindrowArea(fruitType, x, z, x1, z1, x2, z2, 0);
                if area == 0 and not fruitTypeFix then
                    fruitType = FruitUtil.FRUITTYPE_DRYGRASS;
                    area = Utils.updateFruitWindrowArea(fruitType, x, z, x1, z1, x2, z2, 0);
                    area = area + Utils.updateFruitWindrowArea(fruitType, x, z, x1, z1, x2, z2, 0);
                end;
				
				if area > 0 then
					fruitTypeFix = true;
					
                    local dropArea = self.dropAreas[1];
                    local x,y,z = getWorldTranslation(dropArea.start);
                    local x1,y1,z1 = getWorldTranslation(dropArea.width);
                    local x2,y2,z2 = getWorldTranslation(dropArea.height);
                    local old, total = Utils.getFruitWindrowArea(fruitType, x, z, x1, z1, x2, z2);
					
					area = area + old;
					
                    local value = area / total;
                    if value < 1 and value > 0.08 then
                        value = 1;
                    else
                        value = math.floor(value + 0.6);					
                    end;
                    if value >= 1 then
                        value = math.min(value, g_currentMission.maxWindrowValue);
                        Utils.updateFruitWindrowArea(fruitType, x, z, x1, z1, x2, z2, value, true);
						self.psEnabled = true; --Zalaczenie czasteczek
                    end;
				elseif area <= 0 then
					self.psEnabled = false; --Wylaczenie czasteczek
                end;
			end;
			-----Zgrabianie trawy-----	

			-----Przetrzasanie trawy-----
			if self.mode == Z211.MODE_PRZETRZASANIE then --Przetrzasanie
				local cuttingArea = self.cuttingAreas[2];
                local x,y,z = getWorldTranslation(cuttingArea.start);
                local x1,y1,z1 = getWorldTranslation(cuttingArea.width);
                local x2,y2,z2 = getWorldTranslation(cuttingArea.height);
				
				local area = Utils.updateFruitWindrowArea(FruitUtil.FRUITTYPE_GRASS, x, z, x1, z1, x2, z2, 1);
                area = area + Utils.updateFruitWindrowArea(FruitUtil.FRUITTYPE_DRYGRASS, x, z, x1, z1, x2, z2, 1);
                area = area + Utils.updateFruitWindrowArea(FruitUtil.FRUITTYPE_GRASS, x, z, x1, z1, x2, z2, 1);
                area = area + Utils.updateFruitWindrowArea(FruitUtil.FRUITTYPE_DRYGRASS, x, z, x1, z1, x2, z2, 1);
				
				if area > 0 then
                    local dropArea = self.dropAreas[2];
                    local x,y,z = getWorldTranslation(dropArea.start);
                    local x1,y1,z1 = getWorldTranslation(dropArea.width);
                    local x2,y2,z2 = getWorldTranslation(dropArea.height);
                    local old, total = Utils.getFruitCutLongArea(FruitUtil.FRUITTYPE_DRYGRASS, x, z, x1, z1, x2, z2);
					
                    area = area + old;
					
                    local value = area / total;
                    if value < 1 and value > 0.1 then
                        value = 1;
                    else
                        value = math.floor(value + 0.6);
                    end;
					if value >= 1 then
                        value = math.min(value, g_currentMission.maxWindrowValue);
                        Utils.updateFruitWindrowArea(FruitUtil.FRUITTYPE_DRYGRASS, x, z, x1, z1, x2, z2, value, true);
						self.psEnabled = true; --Zalaczenie czasteczek
                    end;
				elseif area <= 0 then
					self.psEnabled = false; --Wylaczenie czasteczek
				end;
			end;
			-----Przetrzasanie trawy-----			
		
			-----Obrot gwiazd-----
			for k,v in pairs(self.speedRotatingParts) do
				rotate(v.node, v.rotationSpeedScale * self.lastSpeedReal * self.movingDirection * dt, 0, 0);
			end;
			-----Obrot gwiazd-----
			
			-----Aktywacja dzwieku-----
			if not self.zgSoundEnabled and self.lastSpeed * 3600 > 5 then
				playSample(self.zgSound, 0, self.zgSoundVolume, 0);
				setSamplePitch(self.zgSound, self.zgSoundPitchOffset);
				self.zgSoundEnabled = true;
			end;
			-----Aktywacja dzwieku-----			
			
			-----Odlaczenie maszyny po przekroczeniu limitu predkosci-----
			local toFast = self:doCheckSpeedLimit() and self.lastSpeed * 3600 > 23;
			self.wasToFast = toFast;
			
			if self.wasToFast then
				self.speedTimer = self.speedTimer - dt;
				if self.speedTimer < 0 then				
					if self.attacherVehicle then
						-----Wylaczenie dzwieku i czasteczek-----
						for k,v in pairs(self.grassParticleSystems) do
							Utils.setEmittingState(v.ps, false);
						end;
						
						if self.zgSoundEnabled then
							stopSample(self.zgSound);
							self.zgSoundEnabled = false;
						end;
						-----Wylaczenie dzwieku i czasteczek-----
						self.attacherVehicle:detachImplementByObject(self);
					end;
				end;
			else
				self.speedTimer = self.speedMaxTimer;
			end;
			-----Odlaczenie maszyny po przekroczeniu limitu predkosci-----
			
			if self.mode ~= Z211.MODE_TRANSPORT then --Wszystkie ustawienia mszyny poza pozycja transportowa
				-----Aktywacja czasteczek-----
				if self.psEnabled and self.lastSpeed * 3600 > 5 then
					if self.mode == Z211.MODE_PRZETRZASANIE then
						for i = 1, 5 do
							Utils.setEmittingState(self.grassParticleSystems[i].ps, true);
						end;
					end;
					if self.mode == Z211.MODE_ZGRABIANIE then
						for i = 6, 10 do
							Utils.setEmittingState(self.grassParticleSystems[i].ps, true);
						end;					
					end;
				end;
				-----Aktywacja czasteczek-----			
			end;
		end;
----------------------------------------KOLA GRABIACE MAJA KONTAKT Z ZIEMIA----------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------KOLA GRABIACE NIE MAJA KONTAKTU Z ZIEMIA------------------------------------
		if not hasGroundContact then
			self.wasToFast = false;
		end;
		-----Deaktywacja czasteczek-----
		if not hasGroundContact or not self.psEnabled or self.lastSpeed * 3600 < 5 then
			for k,v in pairs(self.grassParticleSystems) do
				Utils.setEmittingState(v.ps, false);
			end;
		end;
		-----Deaktywacja czasteczek-----
		
		-----Deaktywacja dzwieku-----
		if not hasGroundContact or self.lastSpeed * 3600 < 5 then
			if self.zgSoundEnabled then
				stopSample(self.zgSound);
				self.zgSoundEnabled = false;
			end;
		end;
		-----Deaktywacja dzwieku-----	
	end;	
------------------------------------KOLA GRABIACE NIE MAJA KONTAKTU Z ZIEMIA------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:farmerInRange() --Sprawdzenie czy farmer jest w poblizu maszyny

    local nearestDistance = 4.0; --MAX odleglosc farmera od maszyny, aby funkcja zwrocila wartosc true
	
	local px, py, pz = getWorldTranslation(self.components[2].node); --Pozycja maszyny
	local vx, vy, vz = getWorldTranslation(getCamera());
	--local vx, vy, vz = getWorldTranslation(Player.rootNode); --Pozycja farmera
	local distance = Utils.vector3Length(px-vx, py-vy, pz-vz); --Odleglosc maszyny od farmera

	if distance < nearestDistance then
		self.farmerNear = true;
			
		if self.attacherVehicle == nil and not self.wheelDown then
		
			if self.mode == Z211.MODE_TRANSPORT then --Maszyna w pozycji transportowej
				g_currentMission:addExtraPrintText(g_i18n:getText("Z211Przycisk").. " " ..InputBinding.getKeyNamesOfDigitalAction(InputBinding.Z211Zgrabianie).. ": " ..g_i18n:getText("Z211Zgrabianie"));
				g_currentMission:addExtraPrintText(g_i18n:getText("Z211Przycisk").. " " ..InputBinding.getKeyNamesOfDigitalAction(InputBinding.Z211Przetrzasanie).. ": " ..g_i18n:getText("Z211Przetrzasanie"));
			
				if InputBinding.hasEvent(InputBinding.Z211Zgrabianie) then --Wybrano zgrabianie
					self.mode = Z211.MODE_ZGRABIANIE;
					self.changeMode = true;
				end;
				if InputBinding.hasEvent(InputBinding.Z211Przetrzasanie) then --Wybrano przetrzasnie
					self.mode = Z211.MODE_PRZETRZASANIE;
					self.changeMode = true;
				end;
			end;
	
			if self.mode == Z211.MODE_ZGRABIANIE then --Maszyna w pozycji do zgrabiania
				g_currentMission:addExtraPrintText(g_i18n:getText("Z211Przycisk").. " " ..InputBinding.getKeyNamesOfDigitalAction(InputBinding.Z211Transport).. ": " ..g_i18n:getText("Z211Transport"));
				g_currentMission:addExtraPrintText(g_i18n:getText("Z211Przycisk").. " " ..InputBinding.getKeyNamesOfDigitalAction(InputBinding.Z211Przetrzasanie).. ": " ..g_i18n:getText("Z211Przetrzasanie"));
			
				if InputBinding.hasEvent(InputBinding.Z211Transport) then --Wybrano transport
					self.mode = Z211.MODE_TRANSPORT;
					self.changeMode = true;
				end;
				if InputBinding.hasEvent(InputBinding.Z211Przetrzasanie) then --Wybrano przetrzasnie
					self.mode = Z211.MODE_PRZETRZASANIE;
					self.changeMode = true;
				end;
			end;
		
			if self.mode == Z211.MODE_PRZETRZASANIE then --Maszyna w pozycji do przetrzasania
				g_currentMission:addExtraPrintText(g_i18n:getText("Z211Przycisk").. " " ..InputBinding.getKeyNamesOfDigitalAction(InputBinding.Z211Transport).. ": " ..g_i18n:getText("Z211Transport"));
				g_currentMission:addExtraPrintText(g_i18n:getText("Z211Przycisk").. " " ..InputBinding.getKeyNamesOfDigitalAction(InputBinding.Z211Zgrabianie).. ": " ..g_i18n:getText("Z211Zgrabianie"));
			
				if InputBinding.hasEvent(InputBinding.Z211Transport) then --Wybrano transport
					self.mode = Z211.MODE_TRANSPORT;
					self.changeMode = true;
				end;
				if InputBinding.hasEvent(InputBinding.Z211Zgrabianie) then --Wybrano zgrabianie
					self.mode = Z211.MODE_ZGRABIANIE;
					self.changeMode = true;
				end;
			end;
		end;
		--Wyswietlenie informacji o klawiszach dla podnoszenia i opuszczania kol grabiacych
		g_currentMission:addExtraPrintText(g_i18n:getText("Z211Przycisk").. " " ..InputBinding.getKeyNamesOfDigitalAction(InputBinding.Z211KolaPodnies)..", " ..InputBinding.getKeyNamesOfDigitalAction(InputBinding.Z211KolaOpusc).. ": " ..g_i18n:getText("Z211KolaGrabiace"));
	else
		self.farmerNear = false;
	end;
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:toTransport() --Ustawienie w pozycji do transportu
	
	-----Odczepienie dyszla i wylaczenie dla niego kolizji-----
	if not self.firstTimeStart then
		removeJoint(self.componentJoints[10].jointIndex);
	end;
	setRigidBodyType(self.components[1].node, "NoRigidBody");
	-----Odczepienie dyszla i wylaczenie dla niego kolizji-----
	
	-----Usuniecie limitow obrotu dla polaczen-----
	setJointRotationLimit(self.componentJoints[1].jointIndex, 1, true, 0, 0); --Dla zaczepu	
	setJointRotationLimit(self.componentJoints[2].jointIndex, 1, true, 0, 0); --Dla pierwszego kola
	setJointRotationLimit(self.componentJoints[3].jointIndex, 1, true, 0, 0); --Dla drugiego kola
	-----Usuniecie limitow obrotu dla polaczen-----	
	
	-----Ustawienie kol-----
	setRotation(self.ustawMaszyne[1].wheel01, unpack(self.ustawMaszyne[1].rotWheel01)); --Kolo pierwsze
	setRotation(self.ustawMaszyne[1].wheel02, unpack(self.ustawMaszyne[1].rotWheel02)); --Kolo drugie
	setRotation(self.ustawMaszyne[1].wheel03, unpack(self.ustawMaszyne[1].rotWheel03)); --Kolo trzecie
	setJointFrame(self.componentJoints[2].jointIndex, 0, self.componentJoints[2].jointNode); --Aktualizacja polaczenia dla kola pierwszego
	setJointFrame(self.componentJoints[3].jointIndex, 0, self.componentJoints[3].jointNode); --Aktualizacja polaczenia dla kola drugiego
	setJointFrame(self.componentJoints[4].jointIndex, 0, self.componentJoints[4].jointNode); --Aktualizacja polaczenia dla kola trzeciego
	-----Ustawienie kol-----
	
	-----Ustawienie pozycji belki i wlaczenie dla niej kolizji-----
	if self.belka.posIndex ~= nil then
		local tx, ty, tz = getWorldTranslation(self.belka.posIndex);
		local rx, ry, rz = getWorldRotation(self.belka.posIndex);
		setTranslation(self.components[7].node, tx, ty, tz);
		setRotation(self.components[7].node, rx, ry, rz);
		setRigidBodyType(self.components[7].node, "Dynamic");
		setVisibility(self.components[7].node, true); --Wyswietlenie belki
		self.belkaVis = true; --Belka jest widoczna
	end;
	-----Ustawienie pozycji belki i wlaczenie dla niej kolizji-----
	
	-----Ustawienie zaczepu-----
	setRotation(self.componentJoints[1].jointNode, 0, 0, 0);
	setJointFrame(self.componentJoints[1].jointIndex, 0, self.componentJoints[1].jointNode);
	-----Ustawienie zaczepu-----	

	self:createComp2Joint(7, 2, self.belka.comp1joint1, self.belka.comp1joint2, '0 90 0', '0 0 0'); --Odwolanie do funkcji, podczepienie belki do zaczepu
	self:createComp2Joint(4, 7, self.belka.comp2joint1, self.belka.comp2joint2, '0 90 0', '0 0 0'); --Odwolanie do funkcji, podczepienie belki do kola
	
	-----Ustawienie pozycji dyszla----
	local tx, ty, tz = getWorldTranslation(self.ustawMaszyne[1].joint);
	local rx, ry, rz = getWorldRotation(self.ustawMaszyne[1].joint);
	setTranslation(self.components[1].node, tx, ty, tz);
	setRotation(self.components[1].node, rx, ry, rz);
	
	setRigidBodyType(self.components[1].node, "Dynamic"); --Wlaczenie kolizji dla dyszla	
	-----Ustawienie pozycji dyszla----
	
	self:createCompJoint(1, 2, self.ustawMaszyne[1].joint, '45 0 0', '0 0 0');	--Odwolanie do funkcji, podczepienie dyszla w nowym punkcie

	-----Animacja rozkladania kol grabiacych-----	
	if self.animPrzetrzasanie then
		self.wheelAnimRoz = true; --Aby zaktualizowac pozycje kolizji
		self:playAnimation(1, 1, -1);
		self.animPrzetrzasanie = false;
	end;
	-----Animacja rozkladania kol grabiacych-----	
	
	self.updateRotLimits = true; --Aktualizacja limitow polaczen

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:toZgrabianie() --Ustawienie w pozycji do zgrabiania
	
	-----Jezeli potrzeba, belka jest odlaczana-----
	if self.belkaVis then
		if not self.firstTimeStart then
			for i = 1, 2 do
				removeJoint(self.components2Joints[i].joint2Index); --Usuniecie polaczen z belka
			end;
		end;
		setRigidBodyType(self.components[7].node, "NoRigidBody"); --Wylaczenie kolizji dla belki
		
		setVisibility(self.components[7].node, false); --Ukrycie belki
		self.belkaVis = false; --Belka jest niewidoczna
	end;
	-----Jezeli potrzeba, belka jest odlaczana-----
	
	-----Odczepienie dyszla i wylaczenie dla niego kolizji-----
	if not self.firstTimeStart then
		removeJoint(self.componentJoints[10].jointIndex);
	end;
	setRigidBodyType(self.components[1].node, "NoRigidBody");
	-----Odczepienie dyszla i wylaczenie dla niego kolizji-----
	
	-----Usuniecie limitow obrotu dla polaczen-----
	setJointRotationLimit(self.componentJoints[2].jointIndex, 1, true, 0, 0); --Dla pierwszego kola
	setJointRotationLimit(self.componentJoints[3].jointIndex, 1, true, 0, 0); --Dla drugiego kola
	-----Usuniecie limitow obrotu dla polaczen-----
	
	-----Ustawienie kol-----
	setRotation(self.ustawMaszyne[2].wheel01, unpack(self.ustawMaszyne[2].rotWheel01)); --Kolo pierwsze
	setRotation(self.ustawMaszyne[2].wheel02, unpack(self.ustawMaszyne[2].rotWheel02)); --Kolo drugie
	setRotation(self.ustawMaszyne[2].wheel03, unpack(self.ustawMaszyne[2].rotWheel03)); --Kolo trzecie
	setJointFrame(self.componentJoints[2].jointIndex, 0, self.componentJoints[2].jointNode); --Aktualizacja polaczenia dla kola pierwszego
	setJointFrame(self.componentJoints[3].jointIndex, 0, self.componentJoints[3].jointNode); --Aktualizacja polaczenia dla kola drugiego
	setJointFrame(self.componentJoints[4].jointIndex, 0, self.componentJoints[4].jointNode); --Aktualizacja polaczenia dla kola trzeciego
	-----Ustawienie kol-----
	
	-----Ustawienie pozycji dyszla----
	local tx, ty, tz = getWorldTranslation(self.ustawMaszyne[2].joint);
	local rx, ry, rz = getWorldRotation(self.ustawMaszyne[2].joint);
	setTranslation(self.components[1].node, tx, ty, tz);
	setRotation(self.components[1].node, rx, ry, rz);
	
	setRigidBodyType(self.components[1].node, "Dynamic"); --Wlaczenie kolizji dla dyszla	
	-----Ustawienie pozycji dyszla----

	self:createCompJoint(1, 4, self.ustawMaszyne[2].joint, '45 0 0', '0 0 0'); --Odwolanie do funkcji, podczepienie dyszla w nowym punkcie

	-----Animacja rozkladania kol grabiacych-----	
	if self.animPrzetrzasanie then
		self.wheelAnimRoz = true; --Aby zaktualizowac pozycje kolizji
		self:playAnimation(1, 1, -1);
		self.animPrzetrzasanie = false;
	end;
	-----Animacja rozkladania kol grabiacych-----	
	
	self.updateRotLimits = true; --Aktualizacja limitow polaczen
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:toPrzetrzasanie() --Ustawienie w pozycji do przetrzasania
	
	-----Jezeli potrzeba, belka jest odlaczana-----
	if self.belkaVis then
		if not self.firstTimeStart then
			for i = 1, 2 do
				removeJoint(self.components2Joints[i].joint2Index); --Usuniecie polaczen z belka
			end;
		end;
		setRigidBodyType(self.components[7].node, "NoRigidBody"); --Wylaczenie kolizji dla belki
		
		setVisibility(self.components[7].node, false); --Ukrycie belki
		self.belkaVis = false; --Belka jest niewidoczna
	end;
	-----Jezeli potrzeba, belka jest odlaczana-----	

	-----Odczepienie dyszla i wylaczenie dla niego kolizji-----
	if not self.firstTimeStart then
		removeJoint(self.componentJoints[10].jointIndex);
	end;
	setRigidBodyType(self.components[1].node, "NoRigidBody");
	-----Odczepienie dyszla i wylaczenie dla niego kolizji-----
	
	-----Usuniecie limitow obrotu dla polaczen-----
	setJointRotationLimit(self.componentJoints[2].jointIndex, 1, true, 0, 0); --Dla pierwszego kola
	setJointRotationLimit(self.componentJoints[3].jointIndex, 1, true, 0, 0); --Dla drugiego kola
	-----Usuniecie limitow obrotu dla polaczen-----
	
	-----Ustawienie kol-----
	setRotation(self.ustawMaszyne[3].wheel01, unpack(self.ustawMaszyne[3].rotWheel01)); --Kolo pierwsze
	setRotation(self.ustawMaszyne[3].wheel02, unpack(self.ustawMaszyne[3].rotWheel02)); --Kolo drugie
	setRotation(self.ustawMaszyne[3].wheel03, unpack(self.ustawMaszyne[3].rotWheel03)); --Kolo trzecie
	setJointFrame(self.componentJoints[2].jointIndex, 0, self.componentJoints[2].jointNode); --Aktualizacja polaczenia dla kola pierwszego
	setJointFrame(self.componentJoints[3].jointIndex, 0, self.componentJoints[3].jointNode); --Aktualizacja polaczenia dla kola drugiego
	setJointFrame(self.componentJoints[4].jointIndex, 0, self.componentJoints[4].jointNode); --Aktualizacja polaczenia dla kola trzeciego
	-----Ustawienie kol-----
	
	-----Ustawienie pozycji dyszla----
	local tx, ty, tz = getWorldTranslation(self.ustawMaszyne[3].joint);
	local rx, ry, rz = getWorldRotation(self.ustawMaszyne[3].joint);
	setTranslation(self.components[1].node, tx, ty, tz);
	setRotation(self.components[1].node, rx, ry, rz);
	
	setRigidBodyType(self.components[1].node, "Dynamic"); --Wlaczenie kolizji dla dyszla
	-----Ustawienie pozycji dyszla----

	self:createCompJoint(1, 5, self.ustawMaszyne[3].joint, '45 0 0', '0 0 0');	--Odwolanie do funkcji, podczepienie dyszla w nowym punkcie

	-----Animacja rozkladania kol grabiacych-----
	self.wheelAnimRoz = true; --Aby zaktualizowac pozycje kolizji
	self:playAnimation(1, 1, 1);	
	self.animPrzetrzasanie = true;
	-----Animacja rozkladania kol grabiacych-----	
	
	self.updateRotLimits = true; --Aktualizacja limitow polaczen
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:createCompJoint(comp1, comp2, joint, rotString, transString) --Funkcja do tworzenia polaczen miedzy komponentami (jeden punkt polaczenia)
	
	--comp1 - indeks pierwszego komponentu, liczony od 1
	--comp2 - indeks drugiego komponentu, liczony od 1
	--joint - indeks do punktu polaczenia tych komponentow
	--rotString - zmienna tekstowa, przechowuje dane o zakresie obrotu polaczenia
	--transString - zmienna tekstowa, przechowuje dane o zakresie ruchu polaczenia
		
	local jointNode = joint;
	if jointNode ~= nil and jointNode ~= 0 then

		local constr = JointConstructor:new();
		constr:setActors(self.components[comp1].node, self.components[comp2].node);
		constr:setJointTransforms(jointNode, jointNode);

		local x, y, z = Utils.getVectorFromString(rotString);
		local rotLimits = {};
		rotLimits[1] = math.rad(Utils.getNoNil(x, 0));
		rotLimits[2] = math.rad(Utils.getNoNil(y, 0));
		rotLimits[3] = math.rad(Utils.getNoNil(z, 0));

		local x, y, z = Utils.getVectorFromString(transString);
		local transLimits = {};
		transLimits[1] = Utils.getNoNil(x, 0);
		transLimits[2] = Utils.getNoNil(y, 0);
		transLimits[3] = Utils.getNoNil(z, 0);

		for i=1, 3 do
			local rotLimit = rotLimits[i];
			if rotLimit >= 0 then
				constr:setRotationLimit(i-1, -rotLimit, rotLimit);
			end;

			local transLimit = transLimits[i];
			if transLimit >= 0 then
				constr:setTranslationLimit(i-1, true, -transLimit, transLimit);
			else
				constr:setTranslationLimit(i-1, false, 0, 0);
			end;
		end;
		
		local jointDesc = {};
		jointDesc.componentIndices = {comp1, comp2};
		jointDesc.jointNode = jointNode;
		jointDesc.jointIndex = constr:finalize();
		table.insert(self.componentJoints, jointDesc);	
	end;

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:createComp2Joint(comp1, comp2, joint1, joint2, rotString, transString) --Funkcja do tworzenia polaczen miedzy komponentami (dwa punkty polaczenia)
																					 --Polaczenie dla belki
	--comp1 - indeks pierwszego komponentu, liczony od 1
	--comp2 - indeks drugiego komponentu, liczony od 1
	--joint1 - indeks do pierwszego punktu polaczenia tych komponentow
	--joint2 - indeks do drugiego punktu polaczenia tych komponentow
	--rotString - zmienna tekstowa, przechowuje dane o zakresie obrotu polaczenia
	--transString - zmienna tekstowa, przechowuje dane o zakresie ruchu polaczenia

	local jointNode1 = joint1;
	local jointNode2 = joint2;
	
	if jointNode1 ~= nil and jointNode1 ~= 0 and jointNode2 ~= nil and jointNode2 ~= 0 then
		local constr = JointConstructor:new();
		constr:setActors(self.components[comp1].node, self.components[comp2].node);
		constr:setJointTransforms(jointNode1, jointNode2);

        local x, y, z = Utils.getVectorFromString(rotString);
        local rotLimits = {};
        rotLimits[1] = math.rad(Utils.getNoNil(x, 0));
        rotLimits[2] = math.rad(Utils.getNoNil(y, 0));
        rotLimits[3] = math.rad(Utils.getNoNil(z, 0));

        local x, y, z = Utils.getVectorFromString(transString);
        local transLimits = {};
        transLimits[1] = Utils.getNoNil(x, 0);
        transLimits[2] = Utils.getNoNil(y, 0);
        transLimits[3] = Utils.getNoNil(z, 0);

        for i = 1, 3 do
            local rotLimit = rotLimits[i];
            if rotLimit >= 0 then
                constr:setRotationLimit(i - 1, -rotLimit, rotLimit);
            end;

            local transLimit = transLimits[i];
            if transLimit >= 0 then
                constr:setTranslationLimit(i - 1, true, -transLimit, transLimit);
            else
                constr:setTranslationLimit(i - 1, false, 0, 0);
            end;
        end;

        local jointDesc = {};
        jointDesc.componentIndices = {comp1, comp2};
        jointDesc.jointNode1 = jointNode1;
		jointDesc.jointNode2 = jointNode2;
        jointDesc.joint2Index = constr:finalize();
		table.insert(self.components2Joints, jointDesc);
	end;		

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:playAnimation(firstAnimPart, lastAnimPart, direction)

	--firstAnimPart - pierwsza z listy animacji do oddtworzenia
	--lastAnimPart - ostatnia z listy animacji do odtworzenia
	--direction - kierunek odtwarzania animacji
	
	-----Przy wartosci zero dla firstAnimPart lub lastAnimPart odtwarzane sa wszystkie dostepne animacje-----
	if firstAnimPart == 0 or lastAnimPart == 0 then
		firstAnimPart = 1;
		lastAnimPart = table.getn(self.animParts);
	end;
	-----Przy wartosci zero dla firstAnimPart lub lastAnimPart odtwarzane sa wszystkie dostepne animacje-----
	
	self.animNumber = lastAnimPart; --Liczba aktualnych animacji do odtworzenia (zasosowana w update)

	for i = firstAnimPart, lastAnimPart do
		local animPart = self.animParts[i];
		local charSet = animPart.animCharSet;
		animPart.moveDirection = direction;
		if charSet ~= 0 then
			local speedScale = nil;
			if animPart.moveDirection > 0.1 then
				speedScale = animPart.speedScale;
			elseif animPart.moveDirection < -0.1 then
				speedScale = -animPart.speedScale;
			end;
				
			if speedScale ~= nil then
				local animTrackTime = getAnimTrackTime(charSet, 0);
				if speedScale > 0 then
					if animTrackTime < 0.0 then
						setAnimTrackTime(charSet, 0, 0.0);
					end;
				else
					if animTrackTime > animPart.animDuration then
						setAnimTrackTime(charSet, 0, animPart.animDuration);
					end;
				end;
					
				setAnimTrackSpeedScale(charSet, 0, speedScale);
				enableAnimTrack(charSet, 0);
				self.playAnim = true;
			else
				disableAnimTrack(charSet, 0);
			end;
		end;
	end;

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:draw()

    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;

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:onAttach()

	--[[setMass(self.components[1].node, 0.02);
	setMass(self.components[2].node, 0.01);	

	setMass(self.components[3].node, 0.09);
	
	setMass(self.components[4].node, 0.075);
	setMass(self.components[5].node, 0.075);
	setMass(self.components[6].node, 0.050);
	
	setMass(self.components[7].node, 0.01);	
	
	setMass(self.components[8].node, 0.01);
	setMass(self.components[9].node, 0.01);
	setMass(self.components[10].node, 0.01);
	setMass(self.components[11].node, 0.01);
	setMass(self.components[12].node, 0.01);]]

	Z211.onActivate(self);
	Z211.addContactReports(self);
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:onDetach()

	--[[setMass(self.components[1].node, 6.02);
	setMass(self.components[2].node, 6.01);	

	setMass(self.components[3].node, 6.09);
	
	setMass(self.components[4].node, 6.075);
	setMass(self.components[5].node, 6.075);
	setMass(self.components[6].node, 6.050);
	
	setMass(self.components[7].node, 6.01);	
	
	setMass(self.components[8].node, 6.01);
	setMass(self.components[9].node, 6.01);
	setMass(self.components[10].node, 6.01);
	setMass(self.components[11].node, 6.01);
	setMass(self.components[12].node, 6.01);]]

	Z211.onDeactivate(self);
	Z211.removeContactReports(self);

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:onLeave()

    if self.deactivateOnLeave then
        Z211.onDeactivate(self);
    end;
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:onActivate()

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:onDeactivate()

	self.speedTimer = self.speedMaxTimer;
	
	for k,v in pairs(self.grassParticleSystems) do
		Utils.setEmittingState(v.ps, false);
	end;
	
	self.psEnabled = false;

	if self.zgSoundEnabled then
		stopSample(self.zgSound);
		self.zgSoundEnabled = false;
	end;

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:addContactReports()

    if not self.Z211ContactReportsActive then
        for k, v in pairs(self.contactReportNodes) do
            addContactReport(v.node, 0.0001, "groundContactReport", self);
        end;
        self.Z211ContactReportsActive = true;
    end;
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:removeContactReports()

    if self.Z211ContactReportsActive then
        for k, v in pairs(self.contactReportNodes) do
            removeContactReport(v.node);
            v.hasGroundContact = false;
        end;
        self.Z211ContactReportsActive = false;
    end;
	
end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function Z211:groundContactReport(objectId, otherObjectId, isStart, normalForce, tangentialForce)

    if otherObjectId == g_currentMission.terrainRootNode then
        local entry = self.contactReportNodes[objectId];
        if entry ~= nil then
            entry.hasGroundContact = isStart or normalForce > 0 or tangentialForce > 0;
        end;
    end;

end;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------