  --
  -- NonReversPlough
  -- This is the specialization for ploughs
  --
  -- @author  Stefan Geiger
  -- @date  30/11/08
  --
  -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.
  
  NonReversPlough = {};  
  
  function NonReversPlough.prerequisitesPresent(specializations)
      return SpecializationUtil.hasSpecialization(Attachable, specializations);
  end
  
  function NonReversPlough:load(xmlFile)
  
      self.groundContactReport = SpecializationUtil.callSpecializationsFunction("groundContactReport");
      self.setRotationMax = SpecializationUtil.callSpecializationsFunction("setRotationMax");
      self.setPloughLimitToField = NonReversPlough.setPloughLimitToField;
      self.getIsPloughRotationAllowed = NonReversPlough.getIsPloughRotationAllowed;
      self.getIsFoldAllowed = Utils.overwrittenFunction(self.getIsFoldAllowed, NonReversPlough.getIsFoldAllowed);
      self.getIsFoldMiddleAllowed = Utils.overwrittenFunction(self.getIsFoldMiddleAllowed, NonReversPlough.getIsFoldMiddleAllowed);
  
      self.rotationPart = {};
      local rotationPartNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.rotationPart#index"));
      if rotationPartNode ~= nil then
          self.rotationPart.node = rotationPartNode;
          local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationPart#minRot"));
          self.rotationPart.minRot = {};
          self.rotationPart.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
          self.rotationPart.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
          self.rotationPart.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));
  
          x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationPart#maxRot"));
          self.rotationPart.maxRot = {};
          self.rotationPart.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
          self.rotationPart.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
          self.rotationPart.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));
  
          x,y,z = getRotation(rotationPartNode);
          self.rotationPart.curRot = {x,y,z};
  
          self.rotationPart.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotationPart#rotTime"), 2)*1000;
          self.rotationPart.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotationPart#touchRotLimit"), 10));
      end
      self.rotationPart.turnAnimation = getXMLString(xmlFile, "vehicle.rotationPart#turnAnimationName");
      self.rotationPart.touchAnimMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotationPart#touchAnimMinLimit"), 0.1);
      self.rotationPart.touchAnimMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotationPart#touchAnimMaxLimit"), 0.9);
      self.rotationPart.foldMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotationPart#foldMinLimit"), 0);
      self.rotationPart.foldMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotationPart#foldMaxLimit"), 1);
      self.rotationPart.limitFoldRotationMax = getXMLBool(xmlFile, "vehicle.rotationPart#limitFoldRotationMax");
      self.rotationPart.foldRotationMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotationPart#foldRotationMinLimit"), 0);
      self.rotationPart.foldRotationMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotationPart#foldRotationMaxLimit"), 1);
      self.rotationPart.rotationFoldMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotationPart#rotationFoldMinLimit"), 0);
      self.rotationPart.rotationFoldMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotationPart#rotationFoldMaxLimit"), 1);
  
      self.ploughDirectionNode = Utils.getNoNil(Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.ploughDirectionNode#index")), self.components[1].node);
  
      self.contactReportNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.contactReportNode#index"));
      if self.contactReportNode == nil then
          self.contactReportNode = self.components[1].node;
      end
  
      self.groundReferenceThreshold = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.groundReferenceNode#threshold"), 0.2);
      self.groundReferenceNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.groundReferenceNode#index"));
  
      local ploughTurnSound = getXMLString(xmlFile, "vehicle.ploughTurnSound#file");
      self.ploughTurnSoundEnabled = false;
      if ploughTurnSound ~= nil and ploughTurnSound ~= "" then
          ploughTurnSound = Utils.getFilename(ploughTurnSound, self.baseDirectory);
          self.ploughTurnSound = createSample("ploughTurnSound");
          loadSample(self.ploughTurnSound, ploughTurnSound, false);
          self.ploughTurnSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.ploughTurnSound#pitchOffset"), 0);
          self.ploughTurnSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.ploughTurnSound#volume"), 1.0);
      end
  
      local ploughSound = getXMLString(xmlFile, "vehicle.ploughSound#file");
      if ploughSound ~= nil and ploughSound ~= "" then
          ploughSound = Utils.getFilename(ploughSound, self.baseDirectory);
          self.ploughSound = createSample("ploughSound");
          loadSample(self.ploughSound, ploughSound, false);
          self.ploughSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.ploughSound#pitchOffset"), 0);
          self.ploughSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.ploughSound#volume"), 1.0);
          self.ploughSoundEnabled = false;
      end
  
      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
  
              entry.turnAnimLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#turnAnimLimit"), 0);
              entry.turnAnimLimitSide = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#turnAnimLimitSide"), 0);
              entry.foldMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#foldMinLimit"), 0);
              entry.foldMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#foldMaxLimit"), 1);
              entry.invertDirectionOnRotation = Utils.getNoNil(getXMLBool(xmlFile, baseName .. "#invertDirectionOnRotation"), true);
  
              table.insert(self.speedRotatingParts, entry);
          end
          i = i+1;
      end
  
  
      self.groundParticleSystems = {};
      local psName = "vehicle.groundParticleSystem";
      Utils.loadParticleSystem(xmlFile, self.groundParticleSystems, psName, self.components, false, nil, self.baseDirectory)
      self.groundParticleSystemActive = false;
  
      self.aiTerrainDetailChannel1 = g_currentMission.sowingChannel;
      self.aiTerrainDetailChannel2 = g_currentMission.cultivatorChannel;
      self.aiTerrainDetailChannel3 = g_currentMission.sowingWidthChannel;
  
      self.rotateLeftToMax = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.rotateLeftToMax#value"), true);
  
      self.onlyActiveWhenLowered = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.onlyActiveWhenLowered#value"), true);
  
      self.ploughHasGroundContact = false;
      self.ploughHasContactReport = false;
      self.rotationMax = false;
      self.speedViolationMaxTime = 2500;
      self.speedViolationTimer = self.speedViolationMaxTime;
      self.ploughContactReportsActive = false;
      self.startActivationTimeout = 2000;
      self.startActivationTime = 0;
  
      self.ploughLimitToField = true;
  
      self.showFieldNotOwnedWarning = false;
  
      self.ploughGroundContactFlag = self:getNextDirtyFlag();
  end
  
  function NonReversPlough:delete()
  
      Utils.deleteParticleSystem(self.groundParticleSystems);
  
      if self.ploughTurnSound ~= nil then
          delete(self.ploughTurnSound);
      end
  
      if self.ploughSound ~= nil then
          delete(self.ploughSound);
      end
  
      NonReversPlough.removeContactReports(self);
  end
  
  function NonReversPlough:readStream(streamId, connection)
      local rotationMax = streamReadBool(streamId);
      local rotationAllowed = streamReadBool(streamId);
      if rotationAllowed then
          self:setRotationMax(rotationMax, true);
          if self.rotationPart.turnAnimation ~= nil and self.playAnimation ~= nil then
              local turnAnimTime = streamReadFloat32(streamId);
              self:setAnimationTime(self.rotationPart.turnAnimation, turnAnimTime);
  
              if self.updateCylinderedInitial ~= nil then
                  self:updateCylinderedInitial();
              end
          elseif self.rotationPart.node ~= nil then
              local x = streamReadFloat32(streamId);
              local y = streamReadFloat32(streamId);
              local z = streamReadFloat32(streamId);
              setRotation(self.rotationPart.node, x, y, z);
          end
      else
          self.rotationMax = rotationMax;
      end
  end
  
  function NonReversPlough:writeStream(streamId, connection)
      streamWriteBool(streamId, self.rotationMax);
      if streamWriteBool(streamId, self:getIsPloughRotationAllowed()) then
  
          if self.rotationPart.turnAnimation ~= nil and self.playAnimation ~= nil then
              local turnAnimTime = self:getAnimationTime(self.rotationPart.turnAnimation);
              streamWriteFloat32(streamId, turnAnimTime);
          elseif self.rotationPart.node ~= nil then
              local x, y, z = getRotation(self.rotationPart.node);
              streamWriteFloat32(streamId, x);
              streamWriteFloat32(streamId, y);
              streamWriteFloat32(streamId, z);
          end
      end
  end
  
  function NonReversPlough:readUpdateStream(streamId, timestamp, connection)
      if connection:getIsServer() then
          self.ploughHasGroundContact = streamReadBool(streamId);
          self.showFieldNotOwnedWarning = streamReadBool(streamId);
      end
  end
  
  function NonReversPlough:writeUpdateStream(streamId, connection, dirtyMask)
      if not connection:getIsServer() then
          streamWriteBool(streamId, self.ploughHasGroundContact);
          streamWriteBool(streamId, self.showFieldNotOwnedWarning);
      end
  end
  
  function NonReversPlough:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
      if not resetVehicles then
          local rotationMax = getXMLBool(xmlFile, key.."#ploughRotationMax");
          if rotationMax ~= nil then
              if self:getIsPloughRotationAllowed() then
                  self:setRotationMax(rotationMax, true);
                  if self.rotationPart.turnAnimation ~= nil and self.playAnimation ~= nil then
                      local turnAnimTime = getXMLFloat(xmlFile, key.."#ploughTurnAnimTime");
                      if turnAnimTime ~= nil then
                          self:setAnimationTime(self.rotationPart.turnAnimation, turnAnimTime);
  
                          if self.updateCylinderedInitial ~= nil then
                              self:updateCylinderedInitial();
                          end
                      end
                  elseif self.rotationPart.node ~= nil then
                      local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#ploughRotation"));
                      if x ~= nil and y ~= nil and z ~= nil then
                          setRotation(self.rotationPart.node, x, y, z);
                      end
                  end
              end
          end
      end
  
      return BaseMission.VEHICLE_LOAD_OK;
  end;
  
  function NonReversPlough:getSaveAttributesAndNodes(nodeIdent)
      local attributes = 'ploughRotationMax="'..tostring(self.rotationMax)..'"';
      if self.rotationPart.turnAnimation ~= nil and self.playAnimation ~= nil then
          local turnAnimTime = self:getAnimationTime(self.rotationPart.turnAnimation);
          attributes = attributes..' ploughTurnAnimTime="'..turnAnimTime..'"';
      elseif self.rotationPart.node ~= nil then
          local x,y,z = getRotation(self.rotationPart.node);
          attributes = attributes..' ploughRotation="'..x..' '..y..' '..z..'"';
      end
      local nodes = "";
      return attributes, nodes;
  end;
  
  function NonReversPlough:mouseEvent(posX, posY, isDown, isUp, button)
  end
  
  function NonReversPlough:keyEvent(unicode, sym, modifier, isDown)
  end
  
  function NonReversPlough:update(dt)
  
      if self:getIsActiveForInput() then
          if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA) then
              if self:getIsPloughRotationAllowed() then
                --  self:setRotationMax(not self.rotationMax);
              end
          end
          if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA3) then
              self:setPloughLimitToField(not self.ploughLimitToField);
          end
      end
  end
  
  function NonReversPlough:updateTick(dt)
      if self:getIsActive() then
          local showFieldNotOwnedWarning = false;
          local updateDensity = true;
  
          if self.rotationPart.turnAnimation ~= nil then
              local turnAnimTime = self:getAnimationTime(self.rotationPart.turnAnimation);
              if turnAnimTime < self.rotationPart.touchAnimMaxLimit and turnAnimTime > self.rotationPart.touchAnimMinLimit then
                  updateDensity = false;
              end
  
              if self.isClient and self.ploughTurnSound ~= nil then
                  if self:getIsAnimationPlaying(self.rotationPart.turnAnimation) then
                      if not self.ploughTurnSoundEnabled and self:getIsActiveForSound() then
                          playSample(self.ploughTurnSound, 0, self.ploughTurnSoundVolume, 0);
                          setSamplePitch(self.ploughTurnSound, self.ploughTurnSoundPitchOffset);
                          self.ploughTurnSoundEnabled = true;
                      end
                  else
                      if self.ploughTurnSoundEnabled then
                          stopSample(self.ploughTurnSound);
                          self.ploughTurnSoundEnabled = false;
                      end
                  end
              end
          elseif self.rotationPart.node ~= nil then
  
              local x, y, z = getRotation(self.rotationPart.node);
              local maxRot = self.rotationPart.maxRot;
              local minRot = self.rotationPart.minRot;
              local eps = self.rotationPart.touchRotLimit;
  
              if (math.abs(x-maxRot[1]) > eps and math.abs(x-minRot[1]) > eps) or
                 (math.abs(y-maxRot[2]) > eps and math.abs(y-minRot[2]) > eps) or
                 (math.abs(z-maxRot[3]) > eps and math.abs(z-minRot[3]) > eps) then
                  updateDensity = false;
  
                  if self.isClient then
                      if self.ploughTurnSound ~= nil and not self.ploughTurnSoundEnabled and self:getIsActiveForSound() then
                          playSample(self.ploughTurnSound, 0, self.ploughTurnSoundVolume, 0);
                          setSamplePitch(self.ploughTurnSound, self.ploughTurnSoundPitchOffset);
                          self.ploughTurnSoundEnabled = true;
                      end
                  end
  
              else
                  if self.isClient then
                      if self.ploughTurnSoundEnabled then
                          stopSample(self.ploughTurnSound);
                          self.ploughTurnSoundEnabled = false;
                      end
                  end
              end
  
              if Utils.setMovedLimitedValues(self.rotationPart.curRot, self.rotationPart.maxRot, self.rotationPart.minRot, 3, self.rotationPart.rotTime, dt, not self.rotationMax) then
                  setRotation(self.rotationPart.node, unpack(self.rotationPart.curRot));
              end
          end
  
          local foldAnimTime = self.foldAnimTime;
          if foldAnimTime ~= nil and (foldAnimTime > self.rotationPart.foldMaxLimit or foldAnimTime < self.rotationPart.foldMinLimit) then
              updateDensity = false;
          end
  
          if self.isServer then
              local hasGroundContact = self.ploughHasContactReport;
              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 self.ploughHasGroundContact ~= hasGroundContact then
                  self:raiseDirtyFlags(self.ploughGroundContactFlag);
              end
              self.ploughHasGroundContact = hasGroundContact;
          end
          local hasGroundContact = self.ploughHasGroundContact;
  
          if hasGroundContact and updateDensity then
  
              if self.startActivationTime <= self.time then
                  if self.isServer then
                      local cuttingAreasSend = {};
                      if not self.onlyActiveWhenLowered or self:isLowered(false) then
                          for k, cuttingArea in pairs(self.cuttingAreas) do
                              if self:getIsAreaActive(cuttingArea) then
                                  local x,_,z = getWorldTranslation(cuttingArea.start);
                                  if g_currentMission:getIsFieldOwnedAtWorldPos(x,z) then
                                      local x1,_,z1 = getWorldTranslation(cuttingArea.width);
                                      local x2,_,z2 = getWorldTranslation(cuttingArea.height);
                                      table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2});
                                  else
                                      showFieldNotOwnedWarning = true;
                                  end
                              end
                          end
                          if (table.getn(cuttingAreasSend) > 0) then
                              local limitToField = self.ploughLimitToField;
                              local limitGrassDestructionToField = false;
                              if not g_currentMission:getHasPermission("createFields", self:getOwner()) then
                                  limitToField = true;
                                  limitGrassDestructionToField = true;
                              end;
  
                              local dx,_,dz = localDirectionToWorld(self.ploughDirectionNode, 0, 0, 1);
                              local angle = Utils.convertToDensityMapAngle(Utils.getYRotationFromDirection(dx, dz), g_currentMission.terrainDetailAngleMaxValue);
  
                              local realArea = PloughAreaEvent.runLocally(cuttingAreasSend, limitToField, limitGrassDestructionToField, angle);
                              g_server:broadcastEvent(PloughAreaEvent:new(cuttingAreasSend, limitToField, limitGrassDestructionToField, angle));
  
                              local pixelToSqm = g_currentMission:getFruitPixelsToSqm(); -- 4096px are mapped to 2048m
                              local sqm = realArea*pixelToSqm;
                              local ha = sqm/10000;
                              g_currentMission.missionStats.hectaresWorkedTotal = g_currentMission.missionStats.hectaresWorkedTotal + ha;
                              g_currentMission.missionStats.hectaresWorkedSession = g_currentMission.missionStats.hectaresWorkedSession + ha;
                          end
                      end
                  end
              end
  
              if self:doCheckSpeedLimit() and self.lastSpeed*3600 > 20 then
                  self.speedViolationTimer = self.speedViolationTimer - dt;
                  if self.isServer then
                      if self.speedViolationTimer < 0 then
                          if self.attacherVehicle ~= nil then
                              self.attacherVehicle:detachImplementByObject(self);
                          end
                      end
                  end
              else
                  self.speedViolationTimer = self.speedViolationMaxTime;
              end
  
              if self.isClient then
                  if self.ploughSound ~= nil and not self.ploughSoundEnabled then
                      if self.lastSpeed*3600 > 3 and self:getIsActiveForSound() then
                          playSample(self.ploughSound, 0, self.ploughSoundVolume, 0);
                          setSamplePitch(self.ploughSound, self.ploughSoundPitchOffset);
                          self.ploughSoundEnabled = true;
                      end
                  end
              end
  
              if self.lastSpeed*3600 > 5 and not self.groundParticleSystemActive then
                  self.groundParticleSystemActive = true;
                  Utils.setEmittingState(self.groundParticleSystems, true);
              end
  
              if self.lastSpeed*3600 < 5 and self.groundParticleSystemActive then
                  self.groundParticleSystemActive = false;
                  Utils.setEmittingState(self.groundParticleSystems, false);
              end
  
          else
              self.speedViolationTimer = self.speedViolationMaxTime;
              if self.ploughSoundEnabled then
                  stopSample(self.ploughSound);
                  self.ploughSoundEnabled = false;
              end
              if self.groundParticleSystemActive then
                  self.groundParticleSystemActive = false;
                  Utils.setEmittingState(self.groundParticleSystems, false);
              end
          end
  
          if hasGroundContact then
              local turnAnimTime;
              local turnSide = 1;
              if self.rotationPart.turnAnimation ~= nil then
                  turnAnimTime = self:getAnimationTime(self.rotationPart.turnAnimation);
                  if turnAnimTime > 0.5 then
                      turnSide = -1;
                  end;
              end;
              for _,v in pairs(self.speedRotatingParts) do
                  local enabled = true;
                  if turnAnimTime ~= nil then
                      if v.turnAnimLimitSide < 0 then
                          enabled = (turnAnimTime <= v.turnAnimLimit);
                      elseif v.turnAnimLimitSide > 0 then
                          enabled = (1-turnAnimTime <= v.turnAnimLimit);
                      else
                          enabled = (turnAnimTime <= v.turnAnimLimit or 1-turnAnimTime <= v.turnAnimLimit);
                      end;
                  end;
                  if enabled and (foldAnimTime == nil or (foldAnimTime <= v.foldMaxLimit and foldAnimTime >= v.foldMinLimit)) then
                      local scale = 1;
                      if v.invertDirectionOnRotation then
                          scale = turnSide;
                      end;
                      rotate(v.node, v.rotationSpeedScale * self.lastSpeedReal * self.movingDirection * dt * scale, 0, 0);
                  end;
              end;
          end;
  
          if self.isServer then
              if showFieldNotOwnedWarning ~= self.showFieldNotOwnedWarning then
                  self.showFieldNotOwnedWarning = showFieldNotOwnedWarning;
                  self:raiseDirtyFlags(self.ploughGroundContactFlag);
              end
          end
  
      else
          if self.groundParticleSystemActive then
              self.groundParticleSystemActive = false;
              Utils.setEmittingState(self.groundParticleSystems, false);
          end
      end
  
      if self.ploughSoundEnabled and self.isClient then
          if self.lastSpeed*3600 < 3 then
              stopSample(self.ploughSound);
              self.ploughSoundEnabled = false;
          end
      end
  end
  
  function NonReversPlough:draw()
  
      if self:getIsActiveForInput(true) then  
          if self.ploughLimitToField then
              g_currentMission:addHelpButtonText(g_i18n:getText("allow_create_fields"), InputBinding.IMPLEMENT_EXTRA3);
          else
              g_currentMission:addHelpButtonText(g_i18n:getText("limit_to_fields"), InputBinding.IMPLEMENT_EXTRA3);
          end
      end
  
      if math.abs(self.speedViolationTimer - self.speedViolationMaxTime) > 2 then
          g_currentMission:addWarning(g_i18n:getText("Dont_drive_to_fast") .. "\n" .. string.format(g_i18n:getText("Cruise_control_levelN"), "1"), 0.07+0.022, 0.019+0.029);
      elseif self.showFieldNotOwnedWarning then
          g_currentMission:addWarning(g_i18n:getText("You_dont_own_this_field"));
      end
  end
  
  function NonReversPlough:onAttach(attacherVehicle)
      NonReversPlough.onActivate(self);
      NonReversPlough.addContactReports(self);
      self.startActivationTime = self.time + self.startActivationTimeout;
  end
  
  function NonReversPlough:onDetach()
  
      self.ploughLimitToField = true;
      if self.deactivateOnDetach then
          NonReversPlough.onDeactivate(self);
          NonReversPlough.removeContactReports(self);
      else
          NonReversPlough.onDeactivateSounds(self);
      end
  
  end
  
  function NonReversPlough:onActivate()
  end
  
  function NonReversPlough:onDeactivate()
      self.speedViolationTimer = self.speedViolationMaxTime;
      self.showFieldNotOwnedWarning = false;
      if self.groundParticleSystemActive then
          self.groundParticleSystemActive = false;
          Utils.setEmittingState(self.groundParticleSystems, false);
      end
      NonReversPlough.onDeactivateSounds(self);
  end
  
  function NonReversPlough:onDeactivateSounds()
      if self.ploughSoundEnabled then
          stopSample(self.ploughSound);
          self.ploughSoundEnabled = false;
      end
      if self.ploughTurnSound ~= nil and self.ploughTurnSoundEnabled  then
          stopSample(self.ploughTurnSound);
          self.ploughTurnSoundEnabled = false;
      end
  end
  
  function NonReversPlough:aiRotateLeft()
      if self:getIsPloughRotationAllowed() then
          self:setRotationMax(self.rotateLeftToMax, true);
      end
  end
  
  function NonReversPlough:aiRotateRight()
      if self:getIsPloughRotationAllowed() then
          self:setRotationMax(not self.rotateLeftToMax, true);
      end
  end
  
  function NonReversPlough:aiTurnOn()
      self.ploughLimitToField = true;
  end
  
  function NonReversPlough:aiTurnOff()
      --self.ploughLimitToField = false;
  end
  
  function NonReversPlough:aiInvertsMarkerOnTurn(turnLeft)
      if turnLeft then
          return self.rotationMax ~= self.rotateLeftToMax;
      else
          return self.rotationMax == self.rotateLeftToMax;
      end
  end
  
  function NonReversPlough:addContactReports()
      if not self.ploughContactReportsActive then
          addContactReport(self.contactReportNode, 0.0001, "groundContactReport", self);
  
          self.ploughContactReportsActive = true;
      end
  end
  
  function NonReversPlough:removeContactReports()
      if self.ploughContactReportsActive then
          removeContactReport(self.contactReportNode);
          self.ploughHasContactReport = false;
          self.ploughContactReportsActive = false;
      end
  end
  
  function NonReversPlough:groundContactReport(objectId, otherObjectId, isStart, normalForce, tangentialForce)
  
      if otherObjectId == g_currentMission.terrainRootNode then
          self.ploughHasContactReport = isStart or normalForce > 0 or tangentialForce > 0;
      end
  
  end
  
  function NonReversPlough:setRotationMax(rotationMax, noEventSend)
      if noEventSend == nil or noEventSend == false then
          if g_server ~= nil then
              g_server:broadcastEvent(PloughRotationEvent:new(self, rotationMax), nil, nil, self);
          else
              g_client:getServerConnection():sendEvent(PloughRotationEvent:new(self, rotationMax));
          end
      end
      self.rotationMax = rotationMax;
  
      if self.rotationPart.turnAnimation ~= nil then
          if self.rotationMax then
              self:playAnimation(self.rotationPart.turnAnimation, 1, nil, true);
          else
              self:playAnimation(self.rotationPart.turnAnimation, -1, nil, true);
          end
      end
  end
  
  function NonReversPlough:setPloughLimitToField(ploughLimitToField, noEventSend)
      if self.ploughLimitToField ~= ploughLimitToField then
          if noEventSend == nil or noEventSend == false then
              if g_server ~= nil then
                  g_server:broadcastEvent(PloughLimitToFieldEvent:new(self, ploughLimitToField), nil, nil, self);
              else
                  g_client:getServerConnection():sendEvent(PloughLimitToFieldEvent:new(self, ploughLimitToField));
              end
          end
          self.ploughLimitToField = ploughLimitToField;
      end
  end
  
  function NonReversPlough:getIsPloughRotationAllowed()
      local foldAnimTime = self.foldAnimTime;
      if foldAnimTime ~= nil and (foldAnimTime > self.rotationPart.rotationFoldMaxLimit or foldAnimTime < self.rotationPart.rotationFoldMinLimit) then
          return false;
      end
      if superFunc ~= nil then
          return superFunc(self);
      end
      return true;
  end
  
  function NonReversPlough:getIsFoldAllowed(superFunc)
      if self.rotationPart.limitFoldRotationMax ~= nil and self.rotationPart.limitFoldRotationMax == self.rotationMax then
          return false;
      end
      if self.rotationPart.turnAnimation ~= nil and self.getAnimationTime ~= nil then
          local rotationTime = self:getAnimationTime(self.rotationPart.turnAnimation);
          if rotationTime > self.rotationPart.foldRotationMaxLimit or rotationTime < self.rotationPart.foldRotationMinLimit then
              return false;
          end
      end
      if superFunc ~= nil then
          return superFunc(self);
      end
      return true;
  end
  
  function NonReversPlough:getIsFoldMiddleAllowed(superFunc)
      if self.rotationPart.limitFoldRotationMax ~= nil and self.rotationPart.limitFoldRotationMax == self.rotationMax then
          return false;
      end
      if self.rotationPart.turnAnimation ~= nil and self.getAnimationTime ~= nil then
          local rotationTime = self:getAnimationTime(self.rotationPart.turnAnimation);
          if rotationTime > self.rotationPart.foldRotationMaxLimit or rotationTime < self.rotationPart.foldRotationMinLimit then
              return false;
          end
      end
      if superFunc ~= nil then
          return superFunc(self);
      end
      return true;
  end;