--
-- BackHoe Digger Working Script
--
-- Original by Zippo
-- Modified with permission by Snapper 10/11/2011
--
-- Please Do not redistribute (modified or unmodified) without permission
--


BackHoeDigger = {}

function BackHoeDigger.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Steerable, specializations);
end;

function BackHoeDigger:load(xmlFile)
	
    local transBackActorNode = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.transBackActor#index"));
    	  if transBackActorNode ~= nil then
        self.transBackActor = {};
        self.transBackActor.node = transBackActorNode;
        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.transBackActor#minTrans"));
        self.transBackActor.minTrans = {};
        self.transBackActor.minTrans[1] = Utils.getNoNil(x, 0);
        self.transBackActor.minTrans[2] = Utils.getNoNil(y, 0);
        self.transBackActor.minTrans[3] = Utils.getNoNil(z, 0);

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

        self.transBackActor.transTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.transBackActor#transTime"), 2)*1000;
        self.transBackActor.touchTransLimit = Utils.getNoNil(getXMLString(xmlFile, "vehicle.transBackActor#touchTransLimit"), 10);
    end;

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

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

        self.transBackActorBucketRamPin.transTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.transBackActorBucketRamPin#transTime"), 2)*1000;
        self.transBackActorBucketRamPin.touchTransLimit = Utils.getNoNil(getXMLString(xmlFile, "vehicle.transBackActorBucketRamPin#touchTransLimit"), 10);
    end;

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

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

        self.transBackActorTelescopic.transTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.transBackActorTelescopic#transTime"), 2)*1000;
        self.transBackActorTelescopic.touchTransLimit = Utils.getNoNil(getXMLString(xmlFile, "vehicle.transBackActorTelescopic#touchTransLimit"), 10);
    end;

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

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

        self.rotBackActor.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotBackActor#rotTime"), 2)*1000;
        self.rotBackActor.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotBackActor#touchRotLimit"), 10));
    end;
	
    local rotBackActorBoomNode = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.rotBackActorBoom#index"));
        if rotBackActorBoomNode ~= nil then
        self.rotBackActorBoom = {};
        self.rotBackActorBoom.node = rotBackActorBoomNode;
        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotBackActorBoom#minRot"));
        self.rotBackActorBoom.minRot = {};
        self.rotBackActorBoom.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotBackActorBoom.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotBackActorBoom.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

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

        self.rotBackActorBoom.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotBackActorBoom#rotTime"), 2)*1000;
        self.rotBackActorBoom.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotBackActorBoom#touchRotLimit"), 10));
    end;
    
    local boomRamAnimRootNode = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.backActorBoomRam#index"));
    self.boomRamAnimCharSet = 0;
    if boomRamAnimRootNode ~= nil and boomRamAnimRootNode ~= 0 then
        self.boomRamAnimCharSet = getAnimCharacterSet(boomRamAnimRootNode);
        if self.boomRamAnimCharSet ~= 0 then
            local clip = getAnimClipIndex(self.boomRamAnimCharSet, getXMLString(xmlFile, "vehicle.backActorBoomRam#clip"));
            assignAnimTrackClip(self.boomRamAnimCharSet, 0, clip);
            setAnimTrackLoopState(self.boomRamAnimCharSet, 0, false);
            self.boomRamAnimSpeed = 0;
            setAnimTrackSpeedScale(self.boomRamAnimCharSet, 0, self.boomRamAnimSpeed);
            self.boomRamAnimDuration = getAnimClipDuration(self.boomRamAnimCharSet, clip);
            enableAnimTrack(self.boomRamAnimCharSet, 0);
        end;
    end;

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

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

        self.rotBackActorDipper.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotBackActorDipper#rotTime"), 2)*1000;
        self.rotBackActorDipper.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotBackActorDipper#touchRotLimit"), 10));
    end;
    
    local dipperRamAnimRootNode = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.backActorDipperRam#index"));
    self.dipperRamAnimCharSet = 0;
    if dipperRamAnimRootNode ~= nil and dipperRamAnimRootNode ~= 0 then
        self.dipperRamAnimCharSet = getAnimCharacterSet(dipperRamAnimRootNode);
        if self.dipperRamAnimCharSet ~= 0 then
            local clip = getAnimClipIndex(self.dipperRamAnimCharSet, getXMLString(xmlFile, "vehicle.backActorDipperRam#clip"));
            assignAnimTrackClip(self.dipperRamAnimCharSet, 0, clip);
            setAnimTrackLoopState(self.dipperRamAnimCharSet, 0, false);
            self.dipperRamAnimSpeed = 0;
            setAnimTrackSpeedScale(self.dipperRamAnimCharSet, 0, self.dipperRamAnimSpeed);
            self.dipperRamAnimDuration = getAnimClipDuration(self.dipperRamAnimCharSet, clip);
            enableAnimTrack(self.dipperRamAnimCharSet, 0);
        end;
    end;

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

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

        self.rotBackActorBucketQHitch.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotBackActorBucketQHitch#rotTime"), 2)*1000;
        self.rotBackActorBucketQHitch.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotBackActorBucketQHitch#touchRotLimit"), 10));
    end;
 
      
    local rotBackActorBucketNode = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.rotBackActorBucket#index"));
        if rotBackActorBucketNode ~= nil then
        self.rotBackActorBucket = {};
        self.rotBackActorBucket.node = rotBackActorBucketNode;
        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotBackActorBucket#minRot"));
        self.rotBackActorBucket.minRot = {};
        self.rotBackActorBucket.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.rotBackActorBucket.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.rotBackActorBucket.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));

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

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

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

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

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

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

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

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

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

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

        self.transLeftJack.transTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.transLeftJack#transTime"), 2)*1000;
        self.transLeftJack.touchTransLimit = Utils.getNoNil(getXMLString(xmlFile, "vehicle.transLeftJack#touchTransLimit"), 10);
    end;

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

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

        self.transRightJack.transTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.transRightJack#transTime"), 2)*1000;
        self.transRightJack.touchTransLimit = Utils.getNoNil(getXMLString(xmlFile, "vehicle.transRightJack#touchTransLimit"), 10);
    end;


    self.rearWindow = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.rearWindow#index"));
    self.rearWindowClosed = true;

    self.workCamera = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.cameras.camera2#index"));

    self.engineUnderLoadEvent = SpecializationUtil.callSpecializationsFunction("engineUnderLoadEvent");
    self.setEngineLoadState = SpecializationUtil.callSpecializationsFunction("setEngineLoadState");
    self.engineUnderLoadEffect = {};
    self.engineUnderLoadEffect.engineIsUnderLoad = false;
    self.engineUnderLoadEffect.motorSoundPitchOffsetNormal = self.motorSoundPitchOffset;
    self.engineUnderLoadEffect.exhaustParticleSystems = {};
    local underLoadExhaustParticleSystemCount = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.underLoadExhaustParticleSystems#count"), 0);
    for i=1, underLoadExhaustParticleSystemCount do
        local namei = string.format("vehicle.underLoadExhaustParticleSystems.exhaustParticleSystem%d", i);
        Utils.loadParticleSystem(xmlFile, self.engineUnderLoadEffect.exhaustParticleSystems, namei, self.components, false, nil, self.baseDirectory)
    end;
    Utils.setEmittingState(self.engineUnderLoadEffect.exhaustParticleSystems, false);
    self.engineUnderLoadEffect.enabled = getXMLBool(xmlFile, "vehicle.engineUnderLoadEffect#enabled");
    self.engineUnderLoadEffect.enabledFLBoomKey = getXMLBool(xmlFile, "vehicle.engineUnderLoadEffect#FLBoomKey");
    self.engineUnderLoadEffect.enabledFLBoomMouse = getXMLBool(xmlFile, "vehicle.engineUnderLoadEffect#FLBoomMouse");
    self.engineUnderLoadEffect.enabledFLToolKey = getXMLBool(xmlFile, "vehicle.engineUnderLoadEffect#FLToolKey");
    self.engineUnderLoadEffect.enabledFLToolMouse = getXMLBool(xmlFile, "vehicle.engineUnderLoadEffect#FLToolMouse");
    self.engineUnderLoadEffect.enabledBABoom = getXMLBool(xmlFile, "vehicle.engineUnderLoadEffect#BABoom");
    self.engineUnderLoadEffect.enabledBATool = getXMLBool(xmlFile, "vehicle.engineUnderLoadEffect#BATool");
    self.engineUnderLoadEffect.enabledJacks = getXMLBool(xmlFile, "vehicle.engineUnderLoadEffect#Jacks");
    self.engineUnderLoadEffect.pitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.engineUnderLoadEffect#pitchOffset"), 0.1);

    self.showHUDInfo = false;

end;

function BackHoeDigger:delete()
end;

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

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

function BackHoeDigger:update(dt)

        self.engineUnderLoadEffect.engineIsUnderLoad = false;

        --if self.isEntered and self.isMotorStarted then    -- not checking for motor started because of bug where animations (sequences only - see dipper and boom rams) continue for short period after engine stops
        if self.isEntered then


	-- Back actor slide
       	local slideLeft = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_SLIDE_LEFT);
       	local slideRight = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_SLIDE_RIGHT);
        if self.transBackActor ~= nil and (slideLeft or slideRight) then
		local x, y, z = getTranslation(self.transBackActor.node);
		local trans = {x,y,z};
		local newTrans = Utils.getMovedLimitedValues(trans, self.transBackActor.maxTrans, self.transBackActor.minTrans, 3, self.transBackActor.transTime, dt, not slideLeft);
		setTranslation(self.transBackActor.node, unpack(newTrans));

                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledBABoom);
	end;


        -- Back actor dipper
        local dipperOut = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_DIPPER_OUT);
       	local dipperIn = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_DIPPER_IN);
	if self.rotBackActorDipper ~= nil and (dipperOut or dipperIn) then
	-- arm
                local a, b, c = getRotation(self.rotBackActorDipper.node);
                local rot = {a,b,c};
                local newRot = Utils.getMovedLimitedValues(rot, self.rotBackActorDipper.maxRot, self.rotBackActorDipper.minRot, 3, self.rotBackActorDipper.rotTime, dt, not dipperOut);
                setRotation(self.rotBackActorDipper.node, unpack(newRot));

                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledBABoom);
        end;
        -- ram
	if self.dipperRamAnimCharSet ~= nil then
           if InputBinding.isPressed(InputBinding.Backhoe_ACTOR_DIPPER_OUT) and self.dipperRamAnimSpeed ~= 1
              and getAnimTrackTime(self.dipperRamAnimCharSet, 0) <= self.dipperRamAnimDuration then
                  self.dipperRamAnimSpeed = 1;
                  setAnimTrackSpeedScale(self.dipperRamAnimCharSet, 0, self.dipperRamAnimSpeed);
           elseif InputBinding.isPressed(InputBinding.Backhoe_ACTOR_DIPPER_IN) and self.dipperRamAnimSpeed ~= -1
              and getAnimTrackTime(self.dipperRamAnimCharSet, 0) >= 0 then
                  self.dipperRamAnimSpeed = -1;
                  setAnimTrackSpeedScale(self.dipperRamAnimCharSet, 0, self.dipperRamAnimSpeed);
           elseif self.dipperRamAnimSpeed ~= 0 then
                  self.dipperRamAnimSpeed = 0;
                  setAnimTrackSpeedScale(self.dipperRamAnimCharSet, 0, self.dipperRamAnimSpeed);
           end;
        end;


        -- Back actor tool
	local openBucket = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_BUCKET_OPEN);
       	local closeBucket = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_BUCKET_CLOSE);
	if self.transBackActorBucketRamPin ~= nil and self.rotBackActorBucketQHitch ~= nil and (openBucket or closeBucket) then
	-- ram piston
                local x, y, z = getTranslation(self.transBackActorBucketRamPin.node);
		local trans = {x,y,z};
		local newTrans = Utils.getMovedLimitedValues(trans, self.transBackActorBucketRamPin.maxTrans, self.transBackActorBucketRamPin.minTrans, 3, self.transBackActorBucketRamPin.transTime, dt, not openBucket);
		setTranslation(self.transBackActorBucketRamPin.node, unpack(newTrans));
	-- bucket open close
                local a, b, c = getRotation(self.rotBackActorBucketQHitch.node);
                local rot = {a,b,c};
                local newRot = Utils.getMovedLimitedValues(rot, self.rotBackActorBucketQHitch.maxRot, self.rotBackActorBucketQHitch.minRot, 3, self.rotBackActorBucketQHitch.rotTime, dt, not openBucket);
                setRotation(self.rotBackActorBucketQHitch.node, unpack(newRot));
                
                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledBATool);
        end;
        -- bucket rotate
        local rotateClockwise = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_BUCKET_ROTATE_CWISE);
       	local rotateAnticlockwise = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_BUCKET_ROTATE_ACWISE);
        if self.rotBackActorBucket ~= nil and (rotateClockwise or rotateAnticlockwise) then
                local d, e, f = getRotation(self.rotBackActorBucket.node);
                local rot = {d,e,f};
                local newRot = Utils.getMovedLimitedValues(rot, self.rotBackActorBucket.maxRot, self.rotBackActorBucket.minRot, 3, self.rotBackActorBucket.rotTime, dt, not rotateClockwise);
                setRotation(self.rotBackActorBucket.node, unpack(newRot));
                
                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledBATool);
        end;


        -- Back actor telescopic extension
        local extendTelescopic = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_TELESCOPIC_EXTEND);
       	local retractTelescopic = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_TELESCOPIC_RETRACT);
	if self.transBackActorTelescopic ~= nil and (extendTelescopic or retractTelescopic) then
		local x, y, z = getTranslation(self.transBackActorTelescopic.node);
		local trans = {x,y,z}; 
		local newTrans = Utils.getMovedLimitedValues(trans, self.transBackActorTelescopic.maxTrans, self.transBackActorTelescopic.minTrans, 3, self.transBackActorTelescopic.transTime, dt, not extendTelescopic);
		setTranslation(self.transBackActorTelescopic.node, unpack(newTrans));

                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledBABoom);
	end;


        -- Back actor slew
	local slewRight = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_SLEW_RIGHT);
       	local slewLeft = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_SLEW_LEFT);
        if self.rotBackActor ~= nil and (slewRight or slewLeft) then
                local x, y, z = getRotation(self.rotBackActor.node);
                local rot = {x,y,z};
                local newRot = Utils.getMovedLimitedValues(rot, self.rotBackActor.maxRot, self.rotBackActor.minRot, 3, self.rotBackActor.rotTime, dt, not slewRight);
                setRotation(self.rotBackActor.node, unpack(newRot));

                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledBABoom);
        end;


        -- Back actor boom
        local lowerBoom = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_LOWER);
       	local raiseBoom = InputBinding.isPressed(InputBinding.Backhoe_ACTOR_RAISE);
        if self.rotBackActorBoom ~= nil and (lowerBoom or raiseBoom) then
        -- boom
                local x, y, z = getRotation(self.rotBackActorBoom.node);
                local rot = {x,y,z};
                local newRot = Utils.getMovedLimitedValues(rot, self.rotBackActorBoom.maxRot, self.rotBackActorBoom.minRot, 3, self.rotBackActorBoom.rotTime, dt, not lowerBoom);
                setRotation(self.rotBackActorBoom.node, unpack(newRot));

                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledBABoom);
        end;
        -- ram
	if self.boomRamAnimCharSet ~= nil then
           if InputBinding.isPressed(InputBinding.Backhoe_ACTOR_LOWER) and self.boomRamAnimSpeed ~= 1
              and getAnimTrackTime(self.boomRamAnimCharSet, 0) <= self.boomRamAnimDuration then
                  self.boomRamAnimSpeed = 1;
                  setAnimTrackSpeedScale(self.boomRamAnimCharSet, 0, self.boomRamAnimSpeed);
           elseif InputBinding.isPressed(InputBinding.Backhoe_ACTOR_RAISE) and self.boomRamAnimSpeed ~= -1
              and getAnimTrackTime(self.boomRamAnimCharSet, 0) >= 0 then
                  self.boomRamAnimSpeed = -1;
                  setAnimTrackSpeedScale(self.boomRamAnimCharSet, 0, self.boomRamAnimSpeed);
           elseif self.boomRamAnimSpeed ~= 0 then
                  self.boomRamAnimSpeed = 0;
                  setAnimTrackSpeedScale(self.boomRamAnimCharSet, 0, self.boomRamAnimSpeed);
           end;
        end;


        -- Front loader
        local raiseFrontloader = InputBinding.isPressed(InputBinding.Backhoe_FRONTLOADER_RAISE);
       	local lowerFrontloader = InputBinding.isPressed(InputBinding.Backhoe_FRONTLOADER_LOWER);
        local openFrontloaderTool = InputBinding.isPressed(InputBinding.Backhoe_FRONTLOADER_TOOL_OPEN);
        local closeFrontloaderTool = InputBinding.isPressed(InputBinding.Backhoe_FRONTLOADER_TOOL_CLOSE);
        local frontLoaderMouseControlActive = Input.isMouseButtonPressed(Input.MOUSE_BUTTON_LEFT);
        if raiseFrontloader or lowerFrontloader then
                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledFLBoomKey);
                -- loader is controlled using mousecontrols and cylindered specializations, so no further action here
        elseif frontLoaderMouseControlActive then
                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledFLBoomMouse);
                -- loader is controlled using mousecontrols and cylindered specializations, so no further action here
	end;
	if openFrontloaderTool or closeFrontloaderTool then
                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledFLToolKey);
                -- loader is controlled using mousecontrols and cylindered specializations, so no further action here
        elseif frontLoaderMouseControlActive then
                --self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledFLToolMouse);  -- no distinction for mouse axis currently, only button pressed recognised so not implementing this for now
                -- loader is controlled using mousecontrols and cylindered specializations, so no further action here
	end;


        -- Stabiliser jacks
	local leftJackLower = InputBinding.isPressed(InputBinding.Backhoe_LEFT_JACK_LOWER);
       	local leftJackRaise = InputBinding.isPressed(InputBinding.Backhoe_LEFT_JACK_RAISE);
        if self.transLeftJack ~= nil and (leftJackLower or leftJackRaise) then
		local x, y, z = getTranslation(self.transLeftJack.node);
		local trans = {x,y,z};
		local newTrans = Utils.getMovedLimitedValues(trans, self.transLeftJack.maxTrans, self.transLeftJack.minTrans, 3, self.transLeftJack.transTime, dt, not leftJackLower);
		setTranslation(self.transLeftJack.node, unpack(newTrans));
		setJointFrame(self.componentJoints[1].jointIndex,0,self.componentJoints[1].jointNode);

                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledJacks);
	end;

	local rightJackLower = InputBinding.isPressed(InputBinding.Backhoe_RIGHT_JACK_LOWER);
       	local rightJackRaise = InputBinding.isPressed(InputBinding.Backhoe_RIGHT_JACK_RAISE);
        if self.transRightJack ~= nil and (rightJackLower or rightJackRaise) then
		local x, y, z = getTranslation(self.transRightJack.node); 
		local trans = {x,y,z}; 
		local newTrans = Utils.getMovedLimitedValues(trans, self.transRightJack.maxTrans, self.transRightJack.minTrans, 3, self.transRightJack.transTime, dt, not rightJackLower);
		setTranslation(self.transRightJack.node, unpack(newTrans));
		setJointFrame(self.componentJoints[2].jointIndex,0,self.componentJoints[2].jointNode);

                self:engineUnderLoadEvent(self.engineUnderLoadEffect.enabledJacks);
	end;
	
	end;


        if self.isEntered then

        -- Door
       	local openDoor = InputBinding.isPressed(InputBinding.Backhoe_DOOR_OPEN);
       	local closeDoor = InputBinding.isPressed(InputBinding.Backhoe_DOOR_CLOSE);
	if self.rotLeftDoor ~= nil and (openDoor or closeDoor) then
                local x, y, z = getRotation(self.rotLeftDoor.node);
                local rot = {x,y,z};
                local newRot = Utils.getMovedLimitedValues(rot, self.rotLeftDoor.maxRot, self.rotLeftDoor.minRot, 3, self.rotLeftDoor.rotTime, dt, not openDoor);
                setRotation(self.rotLeftDoor.node, unpack(newRot));
        end;


        -- Operator platform
	local backActorMode = InputBinding.isPressed(InputBinding.Backhoe_SEAT_BACK_ACTOR_MODE);
       	local drivingMode = InputBinding.isPressed(InputBinding.Backhoe_SEAT_DRIVING_MODE);
        if self.rotOperatorSeat ~= nil and (backActorMode or drivingMode) then
                local x, y, z = getRotation(self.rotOperatorSeat.node);
                local rot = {x,y,z};
                local newRot = Utils.getMovedLimitedValues(rot, self.rotOperatorSeat.maxRot, self.rotOperatorSeat.minRot, 3, self.rotOperatorSeat.rotTime, dt, not backActorMode);
                setRotation(self.rotOperatorSeat.node, unpack(newRot));
                if backActorMode then
                       setFovy(self.workCamera, 80);  -- wider field of vision for working with back actor
                elseif drivingMode then
		       setFovy(self.workCamera, 54);  -- normal field of vision for driving mode
	        end;
	end;


        -- Rear window
        if InputBinding.hasEvent(InputBinding.Backhoe_REAR_WINDOW_TOGGLE) then
                setVisibility(self.rearWindow, not getVisibility(self.rearWindow));
	end;

        end;


        -- HUD panel controls information display toggle
        if InputBinding.hasEvent(InputBinding.Backhoe_HUD_INFO_TOGGLE) then
                self.showHUDInfo = not self.showHUDInfo;
	end;
	

	-- Set engine RPM according to load condition
        self:setEngineLoadState();

end;

function BackHoeDigger:draw()
         if self.isEntered and not self.showHUDInfo then
            g_currentMission:addHelpButtonText("Display HUD info", InputBinding.Backhoe_HUD_INFO_TOGGLE);
         elseif self.isEntered and self.showHUDInfo then
            g_currentMission:addHelpButtonText("Hide HUD info", InputBinding.Backhoe_HUD_INFO_TOGGLE);
         end;
         if self.isEntered and self.showHUDInfo then
            g_currentMission:addExtraPrintText(string.format("Door: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_DOOR_OPEN), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_DOOR_CLOSE)));
            g_currentMission:addExtraPrintText(string.format("Seat: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_SEAT_BACK_ACTOR_MODE), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_SEAT_DRIVING_MODE)));
            g_currentMission:addExtraPrintText(string.format("Rear Window: %s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_REAR_WINDOW_TOGGLE)));
            if self.isMotorStarted then
               g_currentMission:addExtraPrintText(string.format("Back Actor Slide: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_SLIDE_RIGHT), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_SLIDE_LEFT)));
               g_currentMission:addExtraPrintText(string.format("Back Actor Boom: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_LOWER), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_RAISE)));
               g_currentMission:addExtraPrintText(string.format("Back Actor Slew: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_SLEW_RIGHT), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_SLEW_LEFT)));
               g_currentMission:addExtraPrintText(string.format("Back Actor Dipper: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_DIPPER_OUT), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_DIPPER_IN)));
               g_currentMission:addExtraPrintText(string.format("Back Actor Extend: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_TELESCOPIC_EXTEND), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_TELESCOPIC_RETRACT)));
               g_currentMission:addExtraPrintText(string.format("Back Actor Tool: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_BUCKET_OPEN), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_BUCKET_CLOSE)));
               g_currentMission:addExtraPrintText(string.format("Back Actor Rotate: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_BUCKET_ROTATE_CWISE), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_ACTOR_BUCKET_ROTATE_ACWISE)));
               g_currentMission:addExtraPrintText(string.format("Front Loader Boom: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_FRONTLOADER_RAISE), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_FRONTLOADER_LOWER)));
               g_currentMission:addExtraPrintText(string.format("Front Loader Tool: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_FRONTLOADER_TOOL_OPEN), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_FRONTLOADER_TOOL_CLOSE)));
               g_currentMission:addExtraPrintText(string.format("Left Jack: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_LEFT_JACK_LOWER), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_LEFT_JACK_RAISE)));
               g_currentMission:addExtraPrintText(string.format("Right Jack: %s,%s", InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_RIGHT_JACK_LOWER), InputBinding.getKeyNamesOfDigitalAction(InputBinding.Backhoe_RIGHT_JACK_RAISE)));
            end;
  	end;
end;

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

function BackHoeDigger:engineUnderLoadEvent(componentEnabledStatus)
    if self.engineUnderLoadEffect.enabled and componentEnabledStatus and self.isMotorStarted then
       self.engineUnderLoadEffect.engineIsUnderLoad = true;
    end;
end;

function BackHoeDigger:setEngineLoadState()
     	if self.engineUnderLoadEffect.engineIsUnderLoad then
		if self.motorSoundPitchOffset == self.engineUnderLoadEffect.motorSoundPitchOffsetNormal then
			self.motorSoundPitchOffset = self.engineUnderLoadEffect.motorSoundPitchOffsetNormal - math.abs(self.engineUnderLoadEffect.pitchOffset);
			Utils.setEmittingState(self.engineUnderLoadEffect.exhaustParticleSystems, true);
		end;
	else
		if self.motorSoundPitchOffset ~= self.engineUnderLoadEffect.motorSoundPitchOffsetNormal then
			self.motorSoundPitchOffset = self.engineUnderLoadEffect.motorSoundPitchOffsetNormal;
			Utils.setEmittingState(self.engineUnderLoadEffect.exhaustParticleSystems, false);
		end;
	end;
end;
