--
-- Fortschritt
-- This is the specialization for combines
--
-- @author  Stefan Geiger
-- @date  30/11/08
--
-- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.

Fortschritt = {};

function Fortschritt.prerequisitesPresent(specializations)
    Vehicle.registerJointType("cutter");
    Vehicle.registerJointType("trailerCombine");
    return SpecializationUtil.hasSpecialization(Steerable, specializations);
end;

function Fortschritt:load(xmlFile)
	 local rotationPartNodeleftDoor = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.leftDoor#index"));
    if rotationPartNodeleftDoor ~= nil then
        self.rotationPartleftDoor = {};
        self.rotationPartleftDoor.node = rotationPartNodeleftDoor;

        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationPartleftDoor#minRot"));
        self.rotationPartleftDoor.minRot = {};
        self.rotationPartleftDoor.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationPartleftDoor.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationPartleftDoor.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.leftDoor#maxRot"));
        self.rotationPartleftDoor.maxRot = {};
        self.rotationPartleftDoor.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationPartleftDoor.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationPartleftDoor.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        self.rotationPartleftDoor.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.leftDoor#rotTime"), 2)*1000;
        self.rotationPartleftDoor.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.leftDoor#touchRotLimit"), 10));
    end;
	local rotationPartNodeteto = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.teto#index"));
    if rotationPartNodeteto ~= nil then
        self.rotationPartteto = {};
        self.rotationPartteto.node = rotationPartNodeteto;

        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationPartteto#minRot"));
        self.rotationPartteto.minRot = {};
        self.rotationPartteto.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationPartteto.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationPartteto.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.teto#maxRot"));
        self.rotationPartteto.maxRot = {};
        self.rotationPartteto.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationPartteto.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationPartteto.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        self.rotationPartteto.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.teto#rotTime"), 2)*1000;
        self.rotationPartteto.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.teto#touchRotLimit"), 10));
    end;
	local rotationPartNodeabl1 = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.abl1#index"));
    if rotationPartNodeabl1 ~= nil then
        self.rotationPartabl1 = {};
        self.rotationPartabl1.node = rotationPartNodeabl1;

        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationPartabl1#minRot"));
        self.rotationPartabl1.minRot = {};
        self.rotationPartabl1.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationPartabl1.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationPartabl1.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.abl1#maxRot"));
        self.rotationPartabl1.maxRot = {};
        self.rotationPartabl1.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationPartabl1.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationPartabl1.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        self.rotationPartabl1.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.abl1#rotTime"), 2)*1000;
        self.rotationPartabl1.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.abl1#touchRotLimit"), 10));
    end;
    local rotationPartNodeabl2 = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.abl2#index"));
    if rotationPartNodeabl2 ~= nil then
        self.rotationPartabl2 = {};
        self.rotationPartabl2.node = rotationPartNodeabl2;

        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationPartabl2#minRot"));
        self.rotationPartabl2.minRot = {};
        self.rotationPartabl2.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationPartabl2.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationPartabl2.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.abl2#maxRot"));
        self.rotationPartabl2.maxRot = {};
        self.rotationPartabl2.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationPartabl2.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationPartabl2.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        self.rotationPartabl2.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.abl2#rotTime"), 2)*1000;
        self.rotationPartabl2.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.abl2#touchRotLimit"), 10));
    end;
    local rotationPartNodehidra1 = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.hidra1#index"));
    if rotationPartNodehidra1 ~= nil then
        self.rotationParthidra1 = {};
        self.rotationParthidra1.node = rotationPartNodehidra1;

        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationParthidra1#minRot"));
        self.rotationParthidra1.minRot = {};
        self.rotationParthidra1.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationParthidra1.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationParthidra1.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.hidra1#maxRot"));
        self.rotationParthidra1.maxRot = {};
        self.rotationParthidra1.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationParthidra1.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationParthidra1.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        self.rotationParthidra1.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.hidra1#rotTime"), 2)*1000;
        self.rotationParthidra1.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.hidra1#touchRotLimit"), 10));
    end;
        local rotationPartNodehidra2 = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.hidra2#index"));
    if rotationPartNodehidra2 ~= nil then
        self.rotationParthidra2 = {};
        self.rotationParthidra2.node = rotationPartNodehidra2;

        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationParthidra2#minRot"));
        self.rotationParthidra2.minRot = {};
        self.rotationParthidra2.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationParthidra2.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationParthidra2.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.hidra2#maxRot"));
        self.rotationParthidra2.maxRot = {};
        self.rotationParthidra2.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationParthidra2.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationParthidra2.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        self.rotationParthidra2.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.hidra2#rotTime"), 2)*1000;
        self.rotationParthidra2.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.hidra2#touchRotLimit"), 10));
    end;
      local rotationPartNodehidra3 = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.hidra3#index"));
    if rotationPartNodehidra3 ~= nil then
        self.rotationParthidra3 = {};
        self.rotationParthidra3.node = rotationPartNodehidra3;

        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationParthidra3#minRot"));
        self.rotationParthidra3.minRot = {};
        self.rotationParthidra3.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationParthidra3.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationParthidra3.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.hidra3#maxRot"));
        self.rotationParthidra3.maxRot = {};
        self.rotationParthidra3.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotationParthidra3.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotationParthidra3.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        self.rotationParthidra3.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.hidra3#rotTime"), 2)*1000;
        self.rotationParthidra3.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.hidra3#touchRotLimit"), 10));
    end;

    local attachNode = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.attach#index"));
    if attachNode ~= nil then
        self.attach = {};
        self.attach.node = attachNode;
        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.attach#minRot"));
        self.attach.minRot = {};
        self.attach.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.attach.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.attach.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.attach#maxRot"));
        self.attach.maxRot = {};
        self.attach.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.attach.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.attach.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

        self.attach.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.attach#rotTime"), 2)*1000;
        self.attach.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.attach#touchRotLimit"), 10));
    end;
    self.allowGrainTankFruitType = Fortschritt.allowGrainTankFruitType;
    self.emptyGrainTankIfLowFillLevel = Fortschritt.emptyGrainTankIfLowFillLevel;
    self.setGrainTankFillLevel = SpecializationUtil.callSpecializationsFunction("setGrainTankFillLevel");
    self.startThreshing = SpecializationUtil.callSpecializationsFunction("startThreshing");
    self.stopThreshing = SpecializationUtil.callSpecializationsFunction("stopThreshing");
    self.openPipe = SpecializationUtil.callSpecializationsFunction("openPipe");
    self.closePipe = SpecializationUtil.callSpecializationsFunction("closePipe");
    self.findTrailerRaycastCallback = Fortschritt.findTrailerRaycastCallback;

    local threshingStartSound = getXMLString(xmlFile, "vehicle.threshingStartSound#file");
    if threshingStartSound ~= nil and threshingStartSound ~= "" then
        threshingStartSound = Utils.getFilename(threshingStartSound, self.baseDirectory); 
        self.threshingStartSound = createSample("threshingStartSound");
        loadSample(self.threshingStartSound, threshingStartSound, false);
        self.threshingStartSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStartSound#pitchOffset"), 1);
        self.threshingStartSoundPitchScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStartSound#pitchScale"), 0);
        self.threshingStartSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStartSound#pitchMax"), 2.0);
    end;

    local threshingSound = getXMLString(xmlFile, "vehicle.threshingSound#file");
    if threshingSound ~= nil and threshingSound ~= "" then
        threshingSound = Utils.getFilename(threshingSound, self.baseDirectory); 
        self.threshingSound = createSample("threshingSound");
        loadSample(self.threshingSound, threshingSound, false);
        self.threshingSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingSound#pitchOffset"), 1);
        self.threshingSoundPitchScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingSound#pitchScale"), 0);
        self.threshingSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingSound#pitchMax"), 2.0);
    end;

    local threshingStopSound = getXMLString(xmlFile, "vehicle.threshingStopSound#file");
    if threshingStopSound ~= nil and threshingStopSound ~= "" then
        threshingStopSound = Utils.getFilename(threshingStopSound, self.baseDirectory); 
        self.threshingStopSound = createSample("threshingStopSound");
        loadSample(self.threshingStopSound, threshingStopSound, false);
        self.threshingStopSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStopSound#pitchOffset"), 1);
        self.threshingStopSoundPitchScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStopSound#pitchScale"), 0);
        self.threshingStopSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStopSound#pitchMax"), 2.0);
    end;

    local pipeSound = getXMLString(xmlFile, "vehicle.pipeSound#file");
    if pipeSound ~= nil and pipeSound ~= "" then
        pipeSound = Utils.getFilename(pipeSound, self.baseDirectory); 
        self.pipeSound = createSample("pipeSound");
        loadSample(self.pipeSound, pipeSound, false);
        self.pipeSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.pipeSound#pitchOffset"), 1);
        self.pipeSoundPitchScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.pipeSound#pitchScale"), 0);
        self.pipeSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.pipeSound#pitchMax"), 2.0);
    end;

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

    self.pipeParticleSystems = {};
    self.pipe = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.pipe#index"));
    if self.pipe ~= nil then

        self.pipeRaycastNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.pipe#raycastNodeIndex"));

        -- load the pipe particle system for each fruit type
        local i = 0;
        while true do
            local key = string.format("vehicle.pipeParticleSystems.pipeParticleSystem(%d)", i);
            local t = getXMLString(xmlFile, key .. "#type");
            if t == nil then
                break;
            end;

            local desc = FruitUtil.fruitTypes[t];
            if desc ~= nil then
                local currentPS = {};

                local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, key, self.pipe, false, "$data/vehicles/particleSystems/wheatParticleSystem.i3d", self.baseDirectory);
                self.pipeParticleSystems[desc.index] = currentPS;
                if self.defaultPipeParticleSystem == nil then
                    self.defaultPipeParticleSystem = currentPS;
                end;

                if self.pipeRaycastNode == nil then
                    self.pipeRaycastNode = particleNode;
                end;
            end;
            i = i + 1;
        end;

        if self.pipeRaycastNode == nil then
            self.pipeRaycastNode = self.components[1].node;
        end;

    end;

    self.allowsThreshing = true;

    self.isThreshingStarted = false;

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

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

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

    self.grainTankCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.grainTankCapacity"), 200);
    self.grainTankUnloadingCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.grainTankUnloadingCapacity"), 10);
    self.grainTankCrowded = false;

    self.grainTankPlanes = {};
    local i = 0;
    while true do
        local key = string.format("vehicle.grainTankPlane.node(%d)", i);
        local t = getXMLString(xmlFile, key.."#type");
        local index = getXMLString(xmlFile, key.."#index");
        if t==nil or index==nil then
            break;
        end;

        local node = Utils.indexToObject(self.components, index);
        if node ~= nil then
            setVisibility(node, false);

            local entry = {};
            entry.node = node;
            local windowNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#windowIndex"));
            if windowNode ~= nil then
                entry.windowNode = windowNode;
                setVisibility(windowNode, false);
            end;

            if self.defaultGrainTankPlane == nil then
                self.defaultGrainTankPlane = entry;
            end;
            self.grainTankPlanes[t] = entry;
        end;
        i = i +1;
    end;
    if self.defaultGrainTankPlane==nil then
        self.grainTankPlanes = nil;
    end;

    --self.grainPlane = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.grainPlane#index"));
    --self.grainPlaneWindow = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.grainTankPlane#windowIndex"));

    self.grainTankPlaneMinY, self.grainTankPlaneMaxY = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.grainTankPlane#minMaxY"));
    if self.grainTankPlaneMinY == nil or self.grainTankPlaneMaxY == nil then
        local animCurve = AnimCurve:new(linearInterpolator4);
        local i = 0;
        while true do
            local key = string.format("vehicle.grainTankPlane.key(%d)", i);
            local t = getXMLFloat(xmlFile, key.."#time");
            local yValue = getXMLFloat(xmlFile, key.."#y");
            local scaleX,scaleY,scaleZ = Utils.getVectorFromString(getXMLString(xmlFile, key.."#scale"));
            if y == nil or scaleX == nil or scaleY == nil or scaleZ == nil then
                break;
            end;
            animCurve:addKeyframe({x=scaleX, y=scaleY, z=scaleZ, w=yValue, time = t});
            i = i +1;
        end;
        if i > 0 then
            self.grainTankPlaneAnimCurve = animCurve;
        end;
        self.grainTankPlaneMinY = 0;
        self.grainTankPlaneMaxY = 0;
    end;


    self.grainTankPlaneWindowMinY, self.grainTankPlaneWindowMaxY = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.grainTankPlane#windowMinMaxY"));
    if self.grainTankPlaneWindowMinY == nil or self.grainTankPlaneWindowMaxY == nil then
        local animCurve = AnimCurve:new(linearInterpolatorN);
        local i = 0;
        while true do
            local key = string.format("vehicle.grainTankPlane.windowKey(%d)", i);
            local t = getXMLFloat(xmlFile, key.."#time");
            local yValue = getXMLFloat(xmlFile, key.."#y");
            local visibility = getXMLBool(xmlFile, key.."#visibility");
            local scaleX,scaleY,scaleZ = Utils.getVectorFromString(getXMLString(xmlFile, key.."#scale"));
            if y == nil or scaleX == nil or scaleY == nil or scaleZ == nil or visibility == nil then
                break;
            end;
            animCurve:addKeyframe({v={scaleX, scaleY, scaleZ, yValue, }, time = t});
            i = i +1;
        end;
        if i > 0 then
            self.grainTankPlaneWindowAnimCurve = animCurve;
        end;
        self.grainTankPlaneWindowMinY = 0;
        self.grainTankPlaneWindowMaxY = 0;
    end;
    self.grainTankPlaneWindowStartY = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.grainTankPlane#windowStartY"), 0.0);


    self.chopperParticleSystems = {};
    --Utils.loadParticleSystem(xmlFile, self.chopperParticleSystems, "vehicle.chopperParticleSystem", self.components, false, "$data/vehicles/particleSystems/threshingChopperParticleSystem.i3d", self.baseDirectory);
    local i = 0;
    while true do
        local key = string.format("vehicle.chopperParticleSystems.chopperParticleSystem(%d)", i);
        local t = getXMLString(xmlFile, key .. "#type");
        if t == nil then
            break;
        end;
        local desc = FruitUtil.fruitTypes[t];
        if desc ~= nil then
            local currentPS = {};

            local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, key, self.components, false, "$data/vehicles/particleSystems/threshingChopperParticleSystem.i3d", self.baseDirectory);
            self.chopperParticleSystems[desc.index] = currentPS;
            if self.defaultChopperParticleSystem == nil then
                self.defaultChopperParticleSystem = currentPS;
            end;
        end;
        i = i + 1;
    end;

    self.chopperToggleTime = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.chopperParticleSystems#toggleTime"), 2500);
    self.chopperEnableTime = nil;
    self.chopperDisableTime = nil;


    self.strawParticleSystems = {};
    --Utils.loadParticleSystem(xmlFile, self.strawParticleSystems, "vehicle.strawParticleSystem", self.components, false, "$data/vehicles/particleSystems/threshingStrawParticleSystem.i3d", self.baseDirectory);
    local i = 0;
    while true do
        local key = string.format("vehicle.strawParticleSystems.strawParticleSystem(%d)", i);
        local t = getXMLString(xmlFile, key .. "#type");
        if t == nil then
            break;
        end;
        local desc = FruitUtil.fruitTypes[t];
        if desc ~= nil then
            local currentPS = {};

            local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, key, self.components, false, "$data/vehicles/particleSystems/threshingStrawParticleSystem.i3d", self.baseDirectory);
            self.strawParticleSystems[desc.index] = currentPS;
            if self.defaultStrawParticleSystem == nil then
                self.defaultStrawParticleSystem = currentPS;
            end;
        end;
        i = i + 1;
    end;

    self.strawToggleTime = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.strawParticleSystems#toggleTime"), 2500);
    self.strawEnableTime = nil;
    self.strawDisableTime = nil;

    self.strawEmitState = false;


    self.combineSize = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.combineSize"), 1);

    local numStrawAreas = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.strawAreas#count"), 0);
    self.strawAreas = {}
    for i=1, numStrawAreas do
        local area = {};
        local areanamei = string.format("vehicle.strawAreas.strawArea%d", i);
        area.start = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#startIndex"));
        area.width = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#widthIndex"));
        area.height = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#heightIndex"));
        table.insert(self.strawAreas, area);
    end;

    self.isThreshing = false;
    self.chopperActivated = false;
    self.defaultChopperState = false;
    self.pipeOpening = false;
    self.pipeOpen = false;
    self.pipeClose = true;
    self.pipeParticleActivated = false;

    self.threshingScale = 1;

    self.grainTankFruitTypes = {};
    self.grainTankFruitTypes[FruitUtil.FRUITTYPE_UNKNOWN] = true;

    local fruitTypes = getXMLString(xmlFile, "vehicle.grainTankFruitTypes#fruitTypes");
    if fruitTypes ~= nil then
        local types = Utils.splitString(" ", fruitTypes);
        for k,v in pairs(types) do
            local desc = FruitUtil.fruitTypes[v];
            if desc ~= nil then
                self.grainTankFruitTypes[desc.index] = true;
            end;
        end;
    end;

    self.currentGrainTankFruitType = FruitUtil.FRUITTYPE_UNKNOWN;
    self.grainTankFillLevel = 0;
    self:setGrainTankFillLevel(0.0, FruitUtil.FRUITTYPE_UNKNOWN);

    self.minThreshold = 0.05;

    self.speedDisplayScale = 1.0;
    self.drawFillLevel = true;

    self.attachedCutters = {};
    self.numAttachedCutters = 0;

    self.lastLastArea = 0;
    self.lastArea = 0;
end;

function Fortschritt:delete()

    for k,v in pairs(self.pipeParticleSystems) do
        Utils.deleteParticleSystem(v);
    end;
    for k,v in pairs(self.chopperParticleSystems) do
        Utils.deleteParticleSystem(v);
    end;
    for k,v in pairs(self.strawParticleSystems) do
        Utils.deleteParticleSystem(v);
    end;

    if self.threshingStartSound ~= nil then
        delete(self.threshingStartSound);
    end;
    if self.threshingSound ~= nil then
        delete(self.threshingSound);
    end;
    if self.threshingStopSound ~= nil then
        delete(self.threshingStopSound);
    end;
    if self.pipeSound ~= nil then
        delete(self.pipeSound);
    end;

end;

function Fortschritt:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
    local fillLevel = getXMLFloat(xmlFile, key.."#grainTankFillLevel");
    local fruitType = getXMLString(xmlFile, key.."#grainTankFruitType");
    if fillLevel ~= nil and fruitType ~= nil then
        local fruitTypeDesc = FruitUtil.fruitTypes[fruitType];
        if fruitTypeDesc ~= nil then
            self:setGrainTankFillLevel(fillLevel, fruitTypeDesc.index);
        end;
    end;
    return BaseMission.VEHICLE_LOAD_OK;
end;

function Fortschritt:getSaveAttributesAndNodes(nodeIdent)
    local fruitType = "unknown";
    if self.currentGrainTankFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then
        fruitType = FruitUtil.fruitIndexToDesc[self.currentGrainTankFruitType].name;
    end;
    local attributes = 'grainTankFillLevel="'..self.grainTankFillLevel..'" grainTankFruitType="'..fruitType..'"';
    return attributes, nil;
end;

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

function Fortschritt:keyEvent(unicode, sym, modifier, isDown)
    if isDown and sym == Input.KEY_KP_0 then
		self.helpPanel = not self.helpPanel;
	end;
	 if isDown and sym == Input.KEY_k then 
		self.rotationMaxleftDoor = not self.rotationMaxleftDoor;
	end; 
	if isDown and sym == Input.KEY_m then 
		self.rotationMaxteto = not self.rotationMaxteto;
	end;
	if isDown and sym == Input.KEY_n then 
		self.rotationMaxabl1 = not self.rotationMaxabl1;
	end;
	if isDown and sym == Input.KEY_n then 
		self.rotationMaxabl2 = not self.rotationMaxabl2;
	end;
	if isDown and sym == Input.KEY_j then 
		self.rotationMaxhidra1 = not self.rotationMaxhidra1;
	end;
	if isDown and sym == Input.KEY_n then 
		self.rotationMaxhidra2 = not self.rotationMaxhidra2;
	end;
        	if isDown and sym == Input.KEY_u then 
		self.rotationMaxhidra3 = not self.rotationMaxhidra3;
        end;
	if sym == Input.KEY_pagedown then
		self.attachMax = isDown;
	end;
	if sym == Input.KEY_pageup then
		self.attachMin = isDown;
	end;
	if sym == Input.KEY_8 then
		self.alsokarMax = isDown;
	end;
	if sym == Input.KEY_7 then
		self.alsokarMin = isDown;
	end;
end;

function Fortschritt:update(dt)
        if self.rotationPartleftDoor ~= nil then
		   local x, y, z = getRotation(self.rotationPartleftDoor.node);
		   local rot = {x,y,z};
		   local newRot = Utils.getMovedLimitedValues(rot, self.rotationPartleftDoor.maxRot, self.rotationPartleftDoor.minRot, 3, self.rotationPartleftDoor.rotTime, dt, not self.rotationMaxleftDoor);
		   setRotation(self.rotationPartleftDoor.node, unpack(newRot));
        end;
       if self.rotationPartteto ~= nil then
		   local x, y, z = getRotation(self.rotationPartteto.node);
		   local rot = {x,y,z};
		   local newRot = Utils.getMovedLimitedValues(rot, self.rotationPartteto.maxRot, self.rotationPartteto.minRot, 3, self.rotationPartteto.rotTime, dt, not self.rotationMaxteto);
		   setRotation(self.rotationPartteto.node, unpack(newRot));
        end;
		if self.rotationPartabl1 ~= nil then
		   local x, y, z = getRotation(self.rotationPartabl1.node);
		   local rot = {x,y,z};
		   local newRot = Utils.getMovedLimitedValues(rot, self.rotationPartabl1.maxRot, self.rotationPartabl1.minRot, 3, self.rotationPartabl1.rotTime, dt, not self.rotationMaxabl1);
		   setRotation(self.rotationPartabl1.node, unpack(newRot));
        end;
        if self.rotationPartabl2 ~= nil then
		   local x, y, z = getRotation(self.rotationPartabl2.node);
		   local rot = {x,y,z};
		   local newRot = Utils.getMovedLimitedValues(rot, self.rotationPartabl2.maxRot, self.rotationPartabl2.minRot, 3, self.rotationPartabl2.rotTime, dt, not self.rotationMaxabl2);
		   setRotation(self.rotationPartabl2.node, unpack(newRot));
        end;
        if self.rotationParthidra1 ~= nil then
		   local x, y, z = getRotation(self.rotationParthidra1.node);
		   local rot = {x,y,z};
		   local newRot = Utils.getMovedLimitedValues(rot, self.rotationParthidra1.maxRot, self.rotationParthidra1.minRot, 3, self.rotationParthidra1.rotTime, dt, not self.rotationMaxhidra1);
		   setRotation(self.rotationParthidra1.node, unpack(newRot));
        end;
        if self.rotationParthidra2 ~= nil then
		   local x, y, z = getRotation(self.rotationParthidra2.node);
		   local rot = {x,y,z};
		   local newRot = Utils.getMovedLimitedValues(rot, self.rotationParthidra2.maxRot, self.rotationParthidra2.minRot, 3, self.rotationParthidra2.rotTime, dt, not self.rotationMaxhidra2);
		   setRotation(self.rotationParthidra2.node, unpack(newRot));
        end;
        if self.rotationParthidra3 ~= nil then
		   local x, y, z = getRotation(self.rotationParthidra3.node);
		   local rot = {x,y,z};
		   local newRot = Utils.getMovedLimitedValues(rot, self.rotationParthidra3.maxRot, self.rotationParthidra3.minRot, 3, self.rotationParthidra3.rotTime, dt, not self.rotationMaxhidra3);
		   setRotation(self.rotationParthidra3.node, unpack(newRot));
        end;

	local doRotate = self.attachMax or self.attachMin 
	if self.attach ~= nil and doRotate then
        	local x, y, z = getRotation(self.attach.node);		
        	local rot = {x,y,z};
        	local newRot = Utils.getMovedLimitedValues(rot, self.attach.maxRot, self.attach.minRot, 3, self.attach.rotTime, dt, not self.attachMax);
        	setRotation(self.attach.node, unpack(newRot));
	end;

    if self:getIsActive() then

        if self.isThreshingStarted then
            if self.playThreshingSound then
                if self:getIsActiveForSound() then
                    if self.threshingSound ~= nil and self.playThreshingSoundTime <= self.time then
                        playSample(self.threshingSound, 0, 1, 0);
                        self.playThreshingSound = false;
                        self.threshingSoundActive = true;
                    end;
                end;
            end;
        end;

        if self:getIsActiveForInput() then

            if self.grainTankFillLevel < self.grainTankCapacity then
                if InputBinding.hasEvent(InputBinding.ACTIVATE_THRESHING) then
                    if self.isThreshing then
                        self:stopThreshing();
                    else
                        self:startThreshing();
                    end;
                end;
            end;

            if InputBinding.hasEvent(InputBinding.EMPTY_GRAIN) then
                if self.pipeOpening then
                    self:closePipe();
                else
                    self:openPipe();
                end;
            end;
        end;

        if self.grainTankFillLevel >= self.grainTankCapacity then
            self:stopThreshing();
        end;

		if self.isThreshing and self.rotorFan ~= nil then
			rotate(self.rotorFan, dt*0.005, 0, 0);
		end;

        local disableChopperEmit = true;
        local disableStrawEmit = true;

        if self.isThreshing then
            local lastArea = 0;
            local fruitType = FruitUtil.FRUITTYPE_UNKNOWN;
            for cutter,implement in pairs(self.attachedCutters) do
                if cutter.reelStarted then
                    if cutter.lastArea > 0 then
                        for cutter,implement in pairs(self.attachedCutters) do
                            cutter:setFruitType(cutter.currentFruitType);
                            self.currentGrainTankFruitType = cutter.currentFruitType;
                        end;
                        fruitType = cutter.currentFruitType;
                        lastArea = lastArea + cutter.lastArea;
                    end;
                end;
            end;
            self.lastArea = lastArea;
            if lastArea > 0 then
                local fruitDesc = FruitUtil.fruitIndexToDesc[fruitType];
                if fruitDesc.hasStraw then
                    self.chopperActivated = false;
                else
                    self.chopperActivated = true;
                end;

                if self.chopperActivated then
                    if self.chopperEnableTime == nil then
                        self.chopperEnableTime = self.time + self.chopperToggleTime;
                    else
                        self.chopperDisableTime = nil;
                    end;
                    disableChopperEmit = false;
                else
                    if self.strawEnableTime == nil then
                        self.strawEnableTime = self.time + self.strawToggleTime;
                    else
                        self.strawDisableTime = nil;
                    end;
                    disableStrawEmit = false;
                end;
                -- 8000/1200 = 6.56 liter/meter
                -- 8000/1200 / 6 = 1.111 liter/m^2
                -- 8000/1200 / 6 / 2^2 = 0.277777 liter / density pixel (density is 4096^2, on a area of 2048m^2
                local pixelToQm = 2048 / 4096 * 2048 / 4096  / g_currentMission.maxFruitValue; -- 4096px are mapped to 2048m
                local literPerQm = 1;
                if (fruitType ~= FruitUtil.FRUITTYPE_UNKNOWN) then
                    literPerQm = FruitUtil.fruitIndexToDesc[fruitType].literPerQm * (1 + 0.21 * (3 - g_currentMission.missionStats.difficulty));
                end;

                --local literPerPixel = 8000/1200 / 6 / (2*2);

                --literPerPixel = literPerPixel*1.5;
                local qm = lastArea*pixelToQm;

                local newFillLevel = self.grainTankFillLevel+qm*literPerQm*self.threshingScale;
                self:setGrainTankFillLevel(newFillLevel, fruitType);
            end;
        end;

        if disableChopperEmit and self.chopperDisableTime == nil then
            self.chopperDisableTime = self.time + self.chopperToggleTime;
        end;
        if disableStrawEmit and self.strawDisableTime == nil then
            self.strawDisableTime = self.time + self.strawToggleTime;
        end;

        if self.grainTankFillLevel <= 0 then
            for cutter,implement in pairs(self.attachedCutters) do
                cutter:resetFruitType();
            end;
            self.currentGrainTankFruitType = FruitUtil.FRUITTYPE_UNKNOWN;
        end;

        local chopperBlindRotationSpeed = 0.001;
        local minRotX = -83*3.1415/180.0;
        if self.chopperBlind ~= nil then
            local x,y,z = getRotation(self.chopperBlind);
            if self.chopperActivated then
                x = x-dt*chopperBlindRotationSpeed;
                if x < minRotX then
                    x = minRotX;
                end;
            else
                x = x+dt*chopperBlindRotationSpeed;
                if x > 0.0 then
                    x = 0.0;
                end;
            end;
            setRotation(self.chopperBlind, x, y, z);
        end;

        local pipeRotationSpeed = 0.0006;
        local pipeMinRotY = -90*3.1415/180.0;
        local pipeMaxRotX = 10*3.1415/180.0;
        local pipeXRotationSpeed = 0.00006;
        if self.pipe ~= nil then
            local x,y,z = getRotation(self.pipe);

            if self.pipeOpening then
                y = y-dt*pipeRotationSpeed;
                if y < pipeMinRotY then
                    y = pipeMinRotY;
                end;
                x = x+dt*pipeXRotationSpeed;
                if x > pipeMaxRotX then
                    x = pipeMaxRotX;
                end;
            else
                y = y+dt*pipeRotationSpeed;
                if y > 0.0 then
                    y = 0.0;
                end;
                x = x-dt*pipeXRotationSpeed;
                if x < 0.0 then
                    x = 0.0;
                end;
            end;
            setRotation(self.pipe, x, y, z);
            setRotation(self.pipeFlapLid, 0, y, 0);

            self.pipeOpen = (math.abs(pipeMinRotY-y) < 0.01);
            self.pipeClose =  ((x == 0) and (y == 0) );

        end;

        --[[if self.pipeLight ~= nil then
            local pipeLightActive = self.lightsActive and self.pipeOpening;
            setVisibility(self.pipeLight, pipeLightActive);
        end;]]

        if self.motor ~= nil then
            if self.motor.speedLevel == 1 then
                self.speedDisplayScale = 0.5;
            elseif self.motor.speedLevel == 2 then
                self.speedDisplayScale = 0.6;
            else
                self.speedDisplayScale = 1.0;
            end;
        end;

        if not self.pipeOpen or not self.pipeClose then
            if self.pipeSound ~= nil and not self.pipeSoundEnabled then
                if self:getIsActiveForSound() then
                    setSamplePitch(self.pipeSound, self.pipeSoundPitchOffset);
                    playSample(self.pipeSound, 0, 1, 0);
                    self.pipeSoundEnabled = true;
                end;
            end;
        end;

        if self.pipeOpen then
            if self.pipeSound ~= nil and self.pipeSoundEnabled then
                stopSample(self.pipeSound);
                self.pipeSoundEnabled = false;
            end;
        end;

        if self.pipeClose then
            if self.pipeSound ~= nil and self.pipeSoundEnabled then
                stopSample(self.pipeSound);
                self.pipeSoundEnabled = false;
            end;
        end;

        if self.chopperEnableTime ~= nil and self.chopperEnableTime <= self.time then
            if self.currentChopperParticleSystem ~= nil then
                Utils.setEmittingState(self.currentChopperParticleSystem, false);
            end;
            self.currentChopperParticleSystem = self.chopperParticleSystems[self.currentGrainTankFruitType];
            if self.currentChopperParticleSystem == nil then
                self.currentChopperParticleSystem = self.defaultChopperParticleSystem;
            end;
            Utils.setEmittingState(self.currentChopperParticleSystem, true);
            self.chopperEnableTime = nil;
        end;
        if self.strawEnableTime ~= nil and self.strawEnableTime <= self.time then
            if self.currentStrawParticleSystem ~= nil then
                Utils.setEmittingState(self.currentStrawParticleSystem, false);
            end;
            self.currentStrawParticleSystem = self.strawParticleSystems[self.currentGrainTankFruitType];
            if self.currentStrawParticleSystem == nil then
                self.currentStrawParticleSystem = self.defaultStrawParticleSystem;
            end;
            Utils.setEmittingState(self.currentStrawParticleSystem, true);
            self.strawEnableTime = nil;
            self.strawEmitState = true;
        end;
        if self.strawEmitState then
            for k, strawArea in pairs(self.strawAreas) do
                local x,y,z = getWorldTranslation(strawArea.start);
                local x1,y1,z1 = getWorldTranslation(strawArea.width);
                local x2,y2,z2 = getWorldTranslation(strawArea.height);
                local old, total = Utils.getFruitWindrowArea(self.currentGrainTankFruitType, x, z, x1, z1, x2, z2);
                local value = 1+math.floor(old / total + 0.7); -- round, biased to the bigger value
                value = math.min(value, g_currentMission.maxWindrowValue);
                Utils.updateFruitWindrowArea(self.currentGrainTankFruitType, x, z, x1, z1, x2, z2, value, true);
            end;
        end;

    end;

    if self.chopperDisableTime ~= nil and self.chopperDisableTime <= self.time then
        if self.currentChopperParticleSystem ~= nil then
            Utils.setEmittingState(self.currentChopperParticleSystem, false);
        end;
        self.currentChopperParticleSystem = self.chopperParticleSystems[self.currentGrainTankFruitType];
        if self.currentChopperParticleSystem == nil then
            self.currentChopperParticleSystem = self.defaultChopperParticleSystem;
        end;
        Utils.setEmittingState(self.currentChopperParticleSystem, false);
        self.chopperDisableTime = nil;
    end;
    if self.strawDisableTime ~= nil and self.strawDisableTime <= self.time then
        if self.currentStrawParticleSystem ~= nil then
            Utils.setEmittingState(self.currentStrawParticleSystem, false);
        end;
        self.currentStrawParticleSystem = self.strawParticleSystems[self.currentGrainTankFruitType];
        if self.currentStrawParticleSystem == nil then
            self.currentStrawParticleSystem = self.defaultStrawParticleSystem;
        end;
        Utils.setEmittingState(self.currentStrawParticleSystem, false);
        self.strawDisableTime = nil;
        self.strawEmitState = false;
    end;

    self.lastUnloadingTrailer = nil;
    self.pipeParticleActivated = false;
    if self.pipeOpen then
        if self.grainTankFillLevel > 0 then
            self.pipeParticleActivated = true;
            -- test if we should drain the grain tank
            self.trailerFound = 0;
            local x,y,z = getWorldTranslation(self.pipeRaycastNode);
            raycastAll(x, y, z, 0, -1, 0, "findTrailerRaycastCallback", 10, self);

            local trailer = g_currentMission.objectToTrailer[self.trailerFound];
            self.lastUnloadingTrailer = trailer;
            if self.trailerFound == 0 or trailer == nil or not trailer:allowFillType(self.currentGrainTankFruitType, true) or not trailer.allowFillFromAir then
                self.pipeParticleActivated = false;
            else
                local deltaLevel = self.grainTankUnloadingCapacity*dt/1000.0;
                deltaLevel = math.min(deltaLevel, trailer.capacity - trailer.fillLevel);

                self.grainTankFillLevel = self.grainTankFillLevel-deltaLevel;
                if self.grainTankFillLevel <= 0.0 then
                    deltaLevel = deltaLevel+self.grainTankFillLevel;
                    self.grainTankFillLevel = 0.0;
                    self.pipeParticleActivated = false;
                end;
                if deltaLevel == 0 then
                    self.pipeParticleActivated = false;
                end;
                self:setGrainTankFillLevel(self.grainTankFillLevel, self.currentGrainTankFruitType);
                trailer:setFillLevel(trailer.fillLevel+deltaLevel, self.currentGrainTankFruitType);
            end;
        end;
    end;

    if self.currentGrainTankFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then
        if self.currentPipeParticleSystem ~= nil then
            Utils.setEmittingState(self.currentPipeParticleSystem, false);
        end;
        if self.pipeParticleActivated then
            self.currentPipeParticleSystem = self.pipeParticleSystems[self.currentGrainTankFruitType];
            if self.currentPipeParticleSystem == nil then
                self.currentPipeParticleSystem = self.defaultPipeParticleSystem;
            end;
            Utils.setEmittingState(self.currentPipeParticleSystem, true);
        end;
    end;
end;

function Fortschritt:draw()
    if self.isEntered then
		if not self.helpPanel then
			renderText(0.8335, 0.15, 0.02, "Taste KP 0: Info ki");
		else
			renderText(0.8335, 0.15, 0.02, "Taste KP 0: Info be");
		end;
		if self.helpPanel then
			renderText(0.72, 0.84, 0.025, "-Claas Dominator 106 Info-");
			renderText(0.72, 0.815, 0.02, "Taste M: letra");	
			renderText(0.72, 0.8, 0.02, "Taste U: magtartaly");			
			renderText(0.72, 0.785, 0.02, "Taste K: ajto");
			renderText(0.72, 0.77, 0.02, "Taste J: szecskazo");
			renderText(0.72, 0.755, 0.02, "Taste T: auto pilot");			
			renderText(0.72, 0.74, 0.02, "Taste 0: ap console");
			renderText(0.72, 0.725, 0.02, "Taste PgUp: Ferde fel");
			renderText(0.72, 0.71, 0.02, "Taste PgDown: Ferde le");
                        renderText(0.72, 0.695, 0.02, "Taste R: Villogo");
                        renderText(0.72, 0.68, 0.02, "Taste N: Motorfedel");
			renderText(0.72, 0.675, 0.02, "by Torndao");
		end;
	end;
    local percent = self.grainTankFillLevel/self.grainTankCapacity*100;
    if self.drawFillLevel then
        self:drawGrainLevel(self.grainTankFillLevel, self.grainTankCapacity, 95);
    end;
    if self.pipeOpen and not self.pipeParticleActivated and self.grainTankFillLevel > 0 then
        g_currentMission:addExtraPrintText(g_i18n:getText("Move_the_pipe_over_a_trailer"));
    elseif self.grainTankFillLevel == self.grainTankCapacity then
        g_currentMission:addExtraPrintText(g_i18n:getText("Dump_corn_to_continue_threshing"));
    end;
    if self.numAttachedCutters > 0 then
        if self.isThreshing then
            g_currentMission:addHelpButtonText(g_i18n:getText("Turn_off_cutter"), InputBinding.ACTIVATE_THRESHING);
        else
            g_currentMission:addHelpButtonText(g_i18n:getText("Turn_on_cutter"), InputBinding.ACTIVATE_THRESHING);
        end;
    end;
    if self.pipeOpening then
        g_currentMission:addHelpButtonText(g_i18n:getText("Pipe_in"), InputBinding.EMPTY_GRAIN);
    else
        if percent > 80 then
            g_currentMission:addHelpButtonText(g_i18n:getText("Dump_corn"), InputBinding.EMPTY_GRAIN);
        end;
    end;

    if self.currentGrainTankFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then
        g_currentMission.fruitOverlays[self.currentGrainTankFruitType]:render();
    end;

end;

function Fortschritt:onEnter()
    if self.isThreshingStarted then
        self.playThreshingSound = true;
    end;
end;

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

function Fortschritt:onDeactivate()
    self:stopThreshing();
    for k,v in pairs(self.chopperParticleSystems) do
        Utils.setEmittingState(v, false);
    end;
    for k,v in pairs(self.strawParticleSystems) do
        Utils.setEmittingState(v, false);
    end;

    self.chopperEnableTime = nil;
    self.chopperDisableTime = nil;
    self.strawEnableTime = nil;
    self.strawDisableTime = nil;
    self.strawEmitState = false;
    Fortschritt.onDeactivateSounds(self)
end;

function Fortschritt:onDeactivateSounds()
    if self.pipeSound ~= nil and self.pipeSoundEnabled then
        stopSample(self.pipeSound);
        self.pipeSoundEnabled = false;
    end;
    if self.threshingSound ~= nil then
        stopSample(self.threshingSound);
    end;
end;

function Fortschritt:attachImplement(implement)
    local object = implement.object;
    if object.attacherJoint.jointType == Vehicle.JOINTTYPE_CUTTER then
        self.attachedCutters[object] = implement;
        self.numAttachedCutters = self.numAttachedCutters+1;
        -- cutter assumes the thresher's loaded fruit type
        object:setFruitType(self.currentGrainTankFruitType);
    end;
end;

function Fortschritt:detachImplement(implementIndex)
    local object = self.attachedImplements[implementIndex].object;
    if object.attacherJoint.jointType == Vehicle.JOINTTYPE_CUTTER then
        self.numAttachedCutters = self.numAttachedCutters-1;
        if self.numAttachedCutters == 0 then
            self:stopThreshing()
        end;
        self.attachedCutters[object] = nil;
    end;
end;

function Fortschritt:allowGrainTankFruitType(fruitType)
    local allowed = false;

    if self.grainTankFruitTypes[fruitType] then -- is fruit type accepted by combine?
        if self.currentGrainTankFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then -- is combine currently not empty?
            if self.currentGrainTankFruitType ~= fruitType then -- is there another fill type in the tank?
                if self.grainTankFillLevel / self.grainTankCapacity <= self.minThreshold then
                    allowed = true; -- fill level is low enough to be overridden
                end;
            else
                allowed = true; -- fill type is the same as the combine's current fill type
            end;
        else
            allowed = true; -- combine is empty --> FruitUtil.FRUITTYPE_UNKNOWN
        end;
    end;

    return allowed;
end;

function Fortschritt:emptyGrainTankIfLowFillLevel()
    if self.grainTankFillLevel / self.grainTankCapacity <= self.minThreshold then
        self.grainTankFillLevel = 0; -- empty the combine
        --return true;
    end;
    --return false;
end;

function Fortschritt:setGrainTankFillLevel(fillLevel, fruitType)
    if not self:allowGrainTankFruitType(fruitType) then
        return;
    end;

    --self:emptyGrainTankIfLowFillLevel();

    self.grainTankFillLevel = Utils.clamp(fillLevel, 0, self.grainTankCapacity);

    if self.currentGrainTankFruitType ~= fruitType then
        self.currentGrainTankFruitType = fruitType;
        if self.currentGrainTankPlane ~= nil then
            setVisibility(self.currentGrainTankPlane, false);
        end;
        if self.currentGrainTankPlaneWindow ~= nil then
            setVisibility(self.currentGrainTankPlaneWindow, false);
        end;
    end;

    if self.grainTankPlanes ~= nil and self.defaultGrainTankPlane ~= nil and fruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then
        local fruitTypeName = FruitUtil.fruitIndexToDesc[fruitType].name;
        local grainPlane = self.grainTankPlanes[fruitTypeName];
        if grainPlane == nil then
            grainPlane = self.defaultGrainTankPlane;
        end;
        if grainPlane.node ~= nil then
            local yTranslation = 0;
            if self.grainTankPlaneAnimCurve then
                local scaleX, scaleY, scaleZ , yTrans = self.grainTankPlaneAnimCurve:get(self.grainTankFillLevel/self.grainTankCapacity);
                yTranslation = yTrans;
                setScale(grainPlane.node, scaleX, scaleY, scaleZ);
            else
                local m = (self.grainTankPlaneMaxY - self.grainTankPlaneMinY) / self.grainTankCapacity;
                yTranslation = m*self.grainTankFillLevel + self.grainTankPlaneMinY;
            end;
            local xPos, yPos, zPos = getTranslation(grainPlane.node);
            setTranslation(grainPlane.node, xPos, yTranslation, zPos);
            setVisibility(grainPlane.node, self.grainTankFillLevel > 0);
            self.currentGrainTankPlane = grainPlane.node;
        end;
        if grainPlane.windowNode ~= nil then
            local yTranslation = 0;
            if self.grainTankPlaneWindowAnimCurve then
                local scaleX, scaleY, scaleZ , yTrans, visiblity = self.grainTankPlaneWindowAnimCurve:get(self.grainTankFillLevel/self.grainTankCapacity);
                yTranslation = yTrans;
                setScale(grainPlane.windowNode, scaleX, scaleY, scaleZ);
                setVisibility(self.grainPlaneWindow, visiblity);
            else
                local m = (self.grainTankPlaneMaxY - self.grainTankPlaneMinY) / self.grainTankCapacity;
                local startFillLevel = (self.grainTankPlaneWindowStartY-self.grainTankPlaneMinY)/m;
                local yTranslation = math.min(m*(self.grainTankFillLevel-startFillLevel)+self.grainTankPlaneWindowMinY, self.grainTankPlaneWindowMaxY);
                setVisibility(grainPlane.windowNode, self.grainTankFillLevel >= startFillLevel);
            end;
            local xPos, yPos, zPos = getTranslation(grainPlane.windowNode);
            setTranslation(grainPlane.windowNode, xPos, yTranslation, zPos);
            self.currentGrainTankPlaneWindow = grainPlane.windowNode;
        end;
    end;
end;

function Fortschritt:startThreshing()

    if not self.isThreshing then
        if self.numAttachedCutters > 0 then
            self.chopperActivated = self.defaultChopperState;
            self.isThreshing = true;
            for cutter,implement in pairs(self.attachedCutters) do
                local jointDesc = self.attacherJoints[implement.jointDescIndex];
                jointDesc.moveDown = true;

                cutter:setReelSpeed(0.003);
                cutter:onStartReel();
            end;
            --if self.threshingSound ~= nil then
            --    setSamplePitch(self.threshingSound, math.min(self.threshingSoundPitchOffset + self.threshingSoundPitchScale*math.abs(self.lastSoundSpeed), self.threshingSoundPitchMax));
            --   playSample(self.threshingSound, 0, 1, 0);
            --end;


            if not self.isThreshingStarted then

                self.isThreshingStarted = true;

                local threshingSoundOffset = 0;
                if self.threshingStartSound ~= nil and self:getIsActiveForSound() then
                    setSamplePitch(self.threshingStartSound, self.threshingStartSoundPitchOffset);
                    playSample(self.threshingStartSound, 1, 1, 0);
                    threshingSoundOffset = getSampleDuration(self.threshingStartSound);
                end;

                self.playThreshingSound = true;
                self.playThreshingSoundTime = self.time+threshingSoundOffset;

            end;
        end;
    end;
end;

function Fortschritt:stopThreshing()

    if self.isThreshing then
        self.isThreshingStarted = false;
        self.playThreshingSound = false;

        if self.threshingSound ~= nil then
            stopSample(self.threshingSound);
        end;

        if self.threshingStopSound ~= nil and self.threshingSoundActive and self:getIsActiveForSound() then
            setSamplePitch(self.threshingStopSound, self.threshingStopSoundPitchOffset);
            playSample(self.threshingStopSound, 1, 1, 0);
            self.threshingSoundActive = false;
        end;

        self.chopperActivated = false;
        self.isThreshing = false;
        for cutter,implement in pairs(self.attachedCutters) do
            local jointDesc = self.attacherJoints[implement.jointDescIndex];
            jointDesc.moveDown = false;

            cutter:onStopReel();
        end;
    end;

end;

function Fortschritt:findTrailerRaycastCallback(transformId, x, y, z, distance)

    if getUserAttribute(transformId, "vehicleType") == 2 then
        self.trailerFound = transformId;
    end;

    return false;

end;
function Fortschritt:validateAttacherJoint(implement, jointDesc, dt)
    return true;
end;

function Fortschritt:openPipe()
    self.pipeOpening = true;
end;

function Fortschritt:closePipe()
    self.pipeOpening = false;
end;
