local metadata = {
"## Interface: FS15 1.3.0.1 1.3RC1",
"## Title: TheBeastFSM",
"## Notes: Specialization for TheBeastFSM",
"## Author: Marhu",
"## Version: 1.6.1-327",
"## Date: 28.08.2015",
"## Web: http://marhu.net"
}

local function L(name)
	return g_i18n:hasText("TheBeastFSM"..name) and g_i18n:getText("TheBeastFSM"..name) or "- "..name;
end

TheBeastFSM = {};
TheBeastFSM.ModDir = g_currentModDirectory;

function TheBeastFSM.prerequisitesPresent(specializations)
	if not SpecializationUtil.hasSpecialization(Attachable, specializations) then return false end;
	if not SpecializationUtil.hasSpecialization(Cylindered, specializations) then return false end;
	if not SpecializationUtil.hasSpecialization(Fillable, specializations) then return false end;
	if not SpecializationUtil.hasSpecialization(TurnOnVehicle, specializations) then return false end;
	if not SpecializationUtil.hasSpecialization(Foldable, specializations) then return false end;
	if not SpecializationUtil.hasSpecialization(Overloading, specializations) then return false end;
	if not SpecializationUtil.hasSpecialization(WoodCrusher, specializations) then return false end;
	return true
end;

function TheBeastFSM:load(xmlFile)	
	
	self.crushSplitShape = Utils.overwrittenFunction(self.crushSplitShape, TheBeastFSM.crushSplitShape)
	self.setFillLevel = Utils.overwrittenFunction(self.setFillLevel, TheBeastFSM.setFillLevel)
	self.getIsActive = Utils.overwrittenFunction(self.getIsActive, TheBeastFSM.getIsActive);
	self.getIsActiveForInput = Utils.overwrittenFunction(self.getIsActiveForInput, TheBeastFSM.getIsActiveForInput)
	self.getIsFoldAllowed = Utils.overwrittenFunction(self.getIsFoldAllowed, TheBeastFSM.getIsFoldAllowed);
	self.onTurnedOn = Utils.appendedFunction(self.onTurnedOn, TheBeastFSM.onTurnedOn);
	self.onTurnedOff = Utils.appendedFunction(self.onTurnedOff, TheBeastFSM.onTurnedOff);
	self.setIsFuelFilling = SpecializationUtil.callSpecializationsFunction("setIsFuelFilling");
    self.setFuelFillLevel = SpecializationUtil.callSpecializationsFunction("setFuelFillLevel");
    self.addFuelFillTrigger = TheBeastFSM.addFuelFillTrigger;
	self.removeFuelFillTrigger = TheBeastFSM.removeFuelFillTrigger;
	self.PlayerTriggerCallback = TheBeastFSM.PlayerTriggerCallback;
	self.setDieselOn = TheBeastFSM.setDieselOn;
	self.setLights = TheBeastFSM.setLights;
	self.BlinkSigLight = TheBeastFSM.BlinkSigLight;
	self.IsFillActivatable = TheBeastFSM.IsFillActivatable;
	
	self.fuelCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fuelCapacity"), 500);
    local fuelUsage = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fuelUsage"), 100);
    self.fuelUsage = fuelUsage / (60*60*1000); -- from l/h to l/ms
	self.motorizedFillActivatable = MotorizedRefuelActivatable:new(self);
	self.motorizedFillActivatable.getIsActivatable = function(...) return self:IsFillActivatable(...) end;
    self.fuelFillTriggers = {};
    self.isFuelFilling = false;
    self.fuelFillLitersPerSecond = 10;
    self:setFuelFillLevel(0);
	self.sentFuelFillLevel = self.fuelFillLevel;

	self.engineShakeActive = false;	
    self.delayOnLeft = 0;
    self.delayOffLeft = 0;
    self.delayOnRight = 0;
    self.delayOffRight = 0;
    self.delayKurs1 = 0;
    self.delayKurs2 = 0;
    self.delayOn = 0;
    self.delayOff = 0;
    self.deltafo = 0;

  local engineShakeNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.engine#index"));
    if engineShakeNode ~= nil then
        self.engineShake = {};
        self.engineShake.node = engineShakeNode;
        local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.engine#minRot"));
        self.engineShake.minRot = {};
        self.engineShake.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.engineShake.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.engineShake.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));
        x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.engine#maxRot"));
        self.engineShake.maxRot = {};
        self.engineShake.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0));
        self.engineShake.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0));
        self.engineShake.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0));
        self.engineShake.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.engine#rotTime"), 2)*70;
        self.engineShake.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.engine#touchRotLimit"), 10));
    end;
	
	if self.isClient then
		if self.defaultPipeParticleSystem == nil then
			local defaultPipePSNode = self.components[1].node

			if 0 < table.getn(self.pipeNodes) then
				defaultPipePSNode = self.pipeNodes[1].node
			end
			self.pipeParticleSystems = {}
			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 = Fillable.fillTypeNameToDesc[t]
				if desc ~= nil then
					local currentPS = Utils.getNoNil(self.pipeParticleSystems[desc.index], {})
					local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, key, self.components, false, nil, self.baseDirectory, defaultPipePSNode)
					for _, v in ipairs(currentPS) do
						local normalSpeed, tangentSpeed = getParticleSystemAverageSpeed(v.geometry)
						v.speed = math.sqrt(normalSpeed*normalSpeed + tangentSpeed*tangentSpeed)
						v.originalLifespan = getParticleSystemLifespan(v.geometry)
					end
					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
		end
		
		self.onParticleSystems = {}
		local i = 0
		while true do
			local key = string.format("vehicle.turnedOnParticleSystems.turnedOnParticleSystem(%d)", i)
			if not hasXMLProperty(xmlFile, key) then
				break
			end
			Utils.loadParticleSystem(xmlFile, self.onParticleSystems, key, self.components, false, nil, self.baseDirectory)
			i = i + 1
		end
		for _, v in ipairs(self.onParticleSystems) do
            v.originalLifespan = getParticleSystemLifespan(v.geometry);
		end;
		self.FadePS = 0;
		
		local linkNode = Utils.indexToObject(self.components, Utils.getNoNil(getXMLString(xmlFile, "vehicle.dieselStartSound#linkNode"), "0>"))
		self.dieselStartSound = Utils.loadSample(xmlFile, {}, "vehicle.dieselStartSound", nil, self.baseDirectory, linkNode);
		self.dieselIdleSound = Utils.loadSample(xmlFile, {}, "vehicle.dieselIdleSound", nil, self.baseDirectory, linkNode);
		self.dieselWorkSound = Utils.loadSample(xmlFile, {}, "vehicle.dieselWorkSound", nil, self.baseDirectory, linkNode);
		self.dieselStopSound = Utils.loadSample(xmlFile, {}, "vehicle.dieselStopSound", nil, self.baseDirectory, linkNode);
		self.dieselStartSound.startTime = -1
		self.dieselStopSound.stopTime = -1
		self.maxdieselFadeTime = 600
		self.dieselFadeTime = 0
		
		local linkNode = Utils.indexToObject(self.components, Utils.getNoNil(getXMLString(xmlFile, "vehicle.cylinderedHydraulicSound#linkNode"), "0>"))
		self.FoldSound = Utils.loadSample(xmlFile, {}, "vehicle.cylinderedHydraulicSound", nil, self.baseDirectory, linkNode)
		
		local linkNodeString = Utils.getNoNil(getXMLString(xmlFile, "vehicle.fillSound#linkNode"), "0>")
		local linkNode = Utils.indexToObject(self.components, Utils.getNoNil(getXMLString(xmlFile, "vehicle.pipeSound#linkNode"), linkNodeString))
		self.PipeSound = Utils.loadSample(xmlFile, {}, "vehicle.pipeSound", nil, self.baseDirectory, linkNode)
		self.PipeFillS = Utils.loadSample(xmlFile, {}, "vehicle.fillSound", nil, self.baseDirectory, linkNode)
		
		Utils.deleteSample(self.sampleWoodCrusherStart); Utils.deleteSample(self.sampleWoodCrusherStop); Utils.deleteSample(self.sampleWoodCrusherIdle); Utils.deleteSample(self.sampleWoodCrusherWork);
		local linkNode = Utils.indexToObject(self.components, Utils.getNoNil(getXMLString(xmlFile, "vehicle.woodCrusherStartSound#linkNode"),  "0>"))
		self.sampleWoodCrusherStart = Utils.loadSample(xmlFile, {}, "vehicle.woodCrusherStartSound", nil, self.baseDirectory, linkNode)
		self.sampleWoodCrusherStop = Utils.loadSample(xmlFile, {}, "vehicle.woodCrusherStopSound", nil, self.baseDirectory, linkNode)
		self.sampleWoodCrusherIdle = Utils.loadSample(xmlFile, {}, "vehicle.woodCrusherIdleSound", nil, self.baseDirectory, linkNode)
		self.sampleWoodCrusherWork = Utils.loadSample(xmlFile, {}, "vehicle.woodCrusherWorkSound", nil, self.baseDirectory, linkNode)
		self.sampleWoodCrusherStart.startTime = -1
		self.sampleWoodCrusherStop.stopTime = -1
		
		local linkNode = Utils.indexToObject(self.components, Utils.getNoNil(getXMLString(xmlFile, "vehicle.refuelSound#linkNode"),  "0>"))
		self.sampleRefuel = Utils.loadSample(xmlFile, {}, "vehicle.refuelSound", "$data/maps/sounds/refuel.wav", self.baseDirectory, linkNode);
		
		self.WarnSound = Utils.loadSample(xmlFile, {}, "vehicle.warnSound", nil, self.baseDirectory)
		
		
		local i = 0
		while true do
			local key = string.format("vehicle.workLights.workLight(%d)", i)
			if not hasXMLProperty(xmlFile, key) then
				break
			end
			local Light = {}
			Light.vis = false
			Light.node1 = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#index"));
			Light.node2 = getChildAt(Light.node1,0);
			Light.node3 = getChildAt(Light.node2,0);
			Light.Cam = getChildAt(Light.node2,1);
			if Light.Cam == 0 then Light.Cam = nil; end;
			local minRot = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#minRot"),0)
			local maxRot = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#maxRot"),35)
			Light.minRot = math.rad(minRot);
			Light.maxRot = math.rad(maxRot);
			local _, y, _ = getRotation(Light.node1)
			local x, _, _ = getRotation(Light.node2)
			Light.y = y;
			Light.x = x;
			setVisibility(Light.node3,false)
			if not self.workLights then self.workLights = {}; end;
			table.insert(self.workLights,Light)
			i = i + 1
		end
		if self.workLights then
			self.workLightsSelect = 0
			self.numWorkLights = i
		end;
		local i = 0
		while true do
			local key = string.format("vehicle.SigLights.SigLight(%d)", i)
			if not hasXMLProperty(xmlFile, key) then
				break
			end
			local SigLight = {};
			SigLight.node = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#index"));
			SigLight.Type = getXMLString(xmlFile, key .. "#type") --diesel,pipe,fold,crusher
			setVisibility(SigLight.node,false)
			if not self.SigLight then self.SigLight = {} end;
			self.SigLight[SigLight.Type] = SigLight
			i = i + 1
		end
	end
	
	if self.isServer then
		local crushingTimeOverWrite = getXMLFloat(xmlFile, "vehicle.woodCrusher#crushingTime")
		if crushingTimeOverWrite and crushingTimeOverWrite > 1000 then
			self.crushingTimeOverWrite = crushingTimeOverWrite;
		end;
	end;
	local PlayerTriggerIndex = getXMLString(xmlFile, "vehicle.PlayerTriger#index")
	if PlayerTriggerIndex then
		local PlayerTrigger = Utils.indexToObject(self.components, PlayerTriggerIndex);
		if PlayerTrigger then
			self.PlayerTrigger = PlayerTrigger;
			addTrigger(self.PlayerTrigger, "PlayerTriggerCallback", self);
		end;
	end;
	
	self.TheBeastFSMDirtyFlag = self:getNextDirtyFlag();

end;

function TheBeastFSM:delete()
	if self.PlayerTrigger then
		removeTrigger(self.PlayerTrigger)
	end;
	if self.isClient then
		Utils.deleteSample(self.dieselStartSound)
		Utils.deleteSample(self.dieselIdleSound)
		Utils.deleteSample(self.dieselWorkSound)
		Utils.deleteSample(self.dieselStopSound)
		Utils.deleteSample(self.FoldSound)
		Utils.deleteSample(self.PipeSound)
		Utils.deleteSample(self.PipeFillS)
		Utils.deleteSample(self.WarnSound)
		Utils.deleteParticleSystem(self.onParticleSystems)
	end
end;

function TheBeastFSM:getSaveAttributesAndNodes(nodeIdent)
	local attributes = 'fuelFillLevel="'..self.fuelFillLevel..'"';
    local nodes = "";
    return attributes,nodes;
end

function TheBeastFSM:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	local fuelFillLevel = getXMLFloat(xmlFile, key.."#fuelFillLevel");
    if fuelFillLevel ~= nil then
        self:setFuelFillLevel(fuelFillLevel);
	end;
	return BaseMission.VEHICLE_LOAD_OK;
end

function TheBeastFSM:writeStream(streamId, connection)
	streamWriteBool(streamId, self.DieselOn);
    streamWriteBool(streamId, self.isFuelFilling);
	streamWriteFloat32(streamId, self.fuelFillLevel);
	if self.workLights then
		for i = 1, self.numWorkLights do
			streamWriteBool(streamId,self.workLights[i].vis)
			local _, y, _ = getRotation(self.workLights[i].node1)
			Utils.writeCompressedAngle(streamId, y)
			local x, _, _ = getRotation(self.workLights[i].node2)
			Utils.writeCompressedRange(streamId, x, self.workLights[i].minRot, self.workLights[i].maxRot, 8)
		end
	end;
end;

function TheBeastFSM:readStream(streamId, connection)
	local isDieselOn = streamReadBool(streamId);
    if isDieselOn then
        self:setDieselOn(true,true)
    end;
    local isFuelFilling = streamReadBool(streamId);
    self:setIsFuelFilling(isFuelFilling, true);
    local newFuelFillLevel=streamReadFloat32(streamId);
	self:setFuelFillLevel(newFuelFillLevel);
	if self.workLights then
		for i = 1, self.numWorkLights do
			self.workLights[i].vis = streamReadBool(streamId);
			if self.isClient then setVisibility(self.workLights[i].node3,self.workLights[i].vis); end;
			newRotY = Utils.readCompressedAngle(streamId)
			newRotX = Utils.readCompressedRange(streamId, self.workLights[i].minRot, self.workLights[i].maxRot, 8)
			local x, y, z = getRotation(self.workLights[i].node1)
			setRotation(self.workLights[i].node1,x,newRotY,z)
			local x, y, z = getRotation(self.workLights[i].node2)
			setRotation(self.workLights[i].node2,newRotX,y,z)
		end
	end;
end;

function TheBeastFSM:writeUpdateStream(streamId, connection, dirtyMask)
	if not connection.isServer then
        if streamWriteBool(streamId, bitAND(dirtyMask, self.TheBeastFSMDirtyFlag) ~= 0) then
            local percent = Utils.clamp(self.fuelFillLevel / self.fuelCapacity, 0, 1);
            streamWriteUIntN(streamId, math.floor(percent*32767), 15);
        end;
	end;
end;

function TheBeastFSM:readUpdateStream(streamId, timestamp, connection)
	if connection.isServer then
        if streamReadBool(streamId) then
            local fuelFillLevel = streamReadUIntN(streamId, 15)/32767*self.fuelCapacity;
            self:setFuelFillLevel(fuelFillLevel);
        end;
	end;
end;

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

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

function TheBeastFSM:update(dt)
	if self.setFillLevelCache ~= nil then
		local delta = math.min(self.setFillLevelCache[1].lvl, (self.overloadingCapacity*dt*1.1)/1000);
		self:setFillLevel(self.fillLevel+delta, self.setFillLevelCache[1].typ, self.setFillLevelCache[1].force, self.setFillLevelCache[1].SourceStruct)
		self.setFillLevelCache[1].lvl = self.setFillLevelCache[1].lvl - delta
		if self.setFillLevelCache[1].lvl <= 0 then table.remove(self.setFillLevelCache,1) end;
		if table.getn(self.setFillLevelCache) <= 0 then self.setFillLevelCache = nil end;
	end
	
	if self.changeAllowed then
		TheBeastFSM.draw(self);
		if self.attacherVehicle == nil then
			if InputBinding.hasEvent(InputBinding.THEBEASTFSM_TOGGLEDIESEL) then
				self:setDieselOn(not self.DieselOn)
			end
		end;
		if not g_currentMission.controlPlayer then
			self.changeAllowed = false;
			if self.CamSet then
				g_currentMission:removeSpecialCamera(self.workLights[self.CamSet].Cam)
				self.CamSet = nil;
			end
		end;
		if self.workLights then
			if self.DieselOn then
				if InputBinding.hasEvent(InputBinding.THEBEASTFSM_TOGGLELIGHT) then
					self.workLightsSelect = self.workLightsSelect + 1
					if self.workLightsSelect > self.numWorkLights then self.workLightsSelect = 0; end;
					if self.workLights[self.workLightsSelect] and self.workLights[self.workLightsSelect].Cam then
						if self.CamSet then
							g_currentMission:removeSpecialCamera(self.workLights[self.CamSet].Cam)
							self.CamSet = nil;
						end
						g_currentMission:addSpecialCamera(self.workLights[self.workLightsSelect].Cam)
						setCamera(self.workLights[self.workLightsSelect].Cam)
						self.CamSet = self.workLightsSelect;
					elseif self.CamSet then
						g_currentMission:removeSpecialCamera(self.workLights[self.CamSet].Cam)
						self.CamSet = nil;
					end
				end
				if self.workLightsSelect > 0 then
					local LightChange = false;
					local vis, x, y = self.workLights[self.workLightsSelect].vis, self.workLights[self.workLightsSelect].x, self.workLights[self.workLightsSelect].y
					if InputBinding.hasEvent(InputBinding.THEBEASTFSM_LIGHTONOFF) then
						vis = not vis;
						LightChange = true;
					end
					if InputBinding.isPressed(InputBinding.THEBEASTFSM_LIGHTUP) then
						x = math.max(x-0.01,self.workLights[self.workLightsSelect].minRot)
						LightChange = true;
					elseif InputBinding.isPressed(InputBinding.THEBEASTFSM_LIGHTDOWN) then
						x = math.min(x+0.01,self.workLights[self.workLightsSelect].maxRot)
						LightChange = true;
					elseif InputBinding.isPressed(InputBinding.THEBEASTFSM_LIGHTLEFT) then
						y = y+0.01;
						if y > 2 * math.pi then y = y - 2*math.pi end;
						LightChange = true;
					elseif InputBinding.isPressed(InputBinding.THEBEASTFSM_LIGHTRIGHT) then
						y = y-0.01;
						if y < 0 then y = y + 2*math.pi end;
						LightChange = true;
					end
					if LightChange then
						self:setLights(self.workLightsSelect, vis, x, y)
					end;
				end;
			elseif InputBinding.hasEvent(InputBinding.THEBEASTFSM_LIGHTONOFF) then
				for i=1,table.getn(self.workLights) do
					local x, y = self.workLights[i].x, self.workLights[i].y;
					self:setLights(i, false, x, y)
				end;		
			end;
		end;
	end;
	if self.isServer then
		if self.DieselOn then
			local fuelUsedFac = 1
			if self:getIsTurnedOn() then fuelUsedFac = fuelUsedFac + 0.1 end
			if 0 < self.crushingTime then fuelUsedFac = fuelUsedFac + 0.5 end
			if self.FoldSound.isPlaying then fuelUsedFac = fuelUsedFac + 0.3 end
			if self.PipeSound.isPlaying or self.PipeFillS.isPlaying then fuelUsedFac = fuelUsedFac + 0.1 end
			local fuelUsed = fuelUsedFac * (self.fuelUsage * dt);
			self:setFuelFillLevel(self.fuelFillLevel-fuelUsed);
			if fuelUsed > 0 then g_currentMission.missionStats:updateStats("fuelUsage", fuelUsed); end;
			if self.fuelFillLevel <= 0 then
				self:setDieselOn(false)
			end
		end;
	end;
	
	if self.engineShakeActive ~= nil and self.DieselOn then
			   if self.delayKurs1 >= 0 then
				self.delayKurs1 = self.delayKurs1 -dt;
			end;
			   if self.delayKurs2 >= 0 then
				self.delayKurs2 = self.delayKurs2 -dt;
			end;
			     if not self.engineShakeMax then
				   if self.delayKurs1 < 0 then
					self.delayKurs2 = 70;
					self.engineShakeMax = true;
				end;
			end;
				if self.engineShakeMax then
				if self.delayKurs2 < 0 then
				self.delayKurs1 = 70;
				self.engineShakeMax = false;
			end;
		end;
	else
		self.engineShakeMax = false;
	end;

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

function TheBeastFSM:updateTick(dt)
	if self.isClient then
		if self.onParticleSystems ~= nil then
			if self.onParticleSystems.isEmitting ~= true and (self:getIsTurnedOn() or self.DieselOn) then
				Utils.setEmittingState(self.onParticleSystems, true)
			elseif self.onParticleSystems.isEmitting == true and not self:getIsTurnedOn() and not self.DieselOn then
				Utils.setEmittingState(self.onParticleSystems, false)
			end
		end
		
		if self.dieselStartSound.startTime ~= -1 and self.dieselStartSound.startTime <= g_currentMission.time then 
			stopSample(self.dieselStartSound.sound3D)
			playSample(self.dieselIdleSound.sound3D, 0, 0, nil)
			playSample(self.dieselWorkSound.sound3D, 0, 0, nil)
			setVisibility(self.dieselStartSound.sound3D, false)
			setVisibility(self.dieselIdleSound.sound3D, true)
			setVisibility(self.dieselWorkSound.sound3D, true)
			self.dieselStartSound.startTime = -1
		elseif self.dieselStopSound.stopTime ~= -1 and self.dieselStopSound.stopTime <= g_currentMission.time then 
			stopSample(self.dieselStopSound.sound3D)
			setVisibility(self.dieselStopSound.sound3D, false)
			self.dieselStopSound.stopTime = -1
		end
		
		if self.DieselOn and self.foldMoveDirection ~= 0 and self.foldAnimTime > 0 and self.foldAnimTime < 1 then
			if not self.FoldSound.isPlaying then
				playSample(self.FoldSound.sound3D, 0, 0, nil)
				setVisibility(self.FoldSound.sound3D, true)
				self.FoldSound.isPlaying = true;
			end
		elseif self.FoldSound.isPlaying then
			stopSample(self.FoldSound.sound3D)
			setVisibility(self.FoldSound.sound3D, false)
			self.FoldSound.isPlaying = nil;
		end
		if self.SigLight and self.SigLight["fold"] then
			if self:getIsTurnedOnAllowed(true) then
				if not self.SigLight["fold"].vis then
					self.SigLight["fold"].vis = true;
					setVisibility(self.SigLight["fold"].node, true);
				end;
			elseif self.SigLight["fold"].vis then
				self.SigLight["fold"].vis = nil;
				setVisibility(self.SigLight["fold"].node, false);
			end
		end
		
		if self.DieselOn and self.currentPipeState ~= self.targetPipeState then
			if not self.PipeSound.isPlaying then
				playSample(self.PipeSound.sound3D, 0, 0, nil)
				setVisibility(self.PipeSound.sound3D, true)
				self.PipeSound.isPlaying = true;
			end
		elseif self.PipeSound.isPlaying then
			stopSample(self.PipeSound.sound3D)
			setVisibility(self.PipeSound.sound3D, false)
			self.PipeSound.isPlaying = nil;
		end
		if self.SigLight and self.SigLight["pipe"] then
			if self.pipeStateIsUnloading[self.currentPipeState] then
				if not self.SigLight["pipe"].vis then
					self.SigLight["pipe"].vis = true;
					setVisibility(self.SigLight["pipe"].node, true);
				end;
			elseif self.SigLight["pipe"].vis then
				self.SigLight["pipe"].vis = nil;
				setVisibility(self.SigLight["pipe"].node, false);
			end
		end;
		
		if self.sampleWoodCrusherStart.startTime ~= -1 and self.sampleWoodCrusherStart.startTime <= g_currentMission.time then 
			stopSample(self.sampleWoodCrusherStart.sound3D)
			playSample(self.sampleWoodCrusherIdle.sound3D, 0, 0, nil)
			playSample(self.sampleWoodCrusherWork.sound3D, 0, 0, nil)
			setVisibility(self.sampleWoodCrusherStart.sound3D, false)
			setVisibility(self.sampleWoodCrusherIdle.sound3D, true)
			setVisibility(self.sampleWoodCrusherWork.sound3D, true)
			self.sampleWoodCrusherStart.startTime = -1
		elseif self.sampleWoodCrusherStop.stopTime ~= -1 and self.sampleWoodCrusherStop.stopTime <= g_currentMission.time then 
			stopSample(self.sampleWoodCrusherStop.sound3D)
			setVisibility(self.sampleWoodCrusherStop.sound3D, false)
			self.sampleWoodCrusherStop.stopTime = -1
		end
		
		if self.DieselOn then
			local volume = (self.sampleWoodCrusherWork.volume*self.workFadeTime)/self.maxWorkFadeTime
			Utils.setSampleVolume(self.sampleWoodCrusherWork, volume)
			if self:getIsTurnedOn() or self.FoldSound.isPlaying or self.PipeSound.isPlaying or self.PipeFillS.isPlaying then
				self.dieselFadeTime = math.min(self.maxdieselFadeTime, self.dieselFadeTime + dt)
			else
				self.dieselFadeTime = math.max(0, self.dieselFadeTime - dt)
			end
			
			local volumeDiesel = (self.dieselWorkSound.volume*self.dieselFadeTime)/self.maxdieselFadeTime
			Utils.setSampleVolume(self.dieselWorkSound, volumeDiesel)
			
			local pitch = 0.5
			if volumeDiesel >= self.dieselWorkSound.volume then
				if self.FoldSound.isPlaying then pitch = pitch + 0.1 end;
				if self.PipeSound.isPlaying then pitch = pitch + 0.05 end;
				if self.PipeFillS.isPlaying then pitch = pitch + 0.05 end;
				if self:getIsTurnedOn() then pitch = pitch + 0.1 end;
				if volume > 0 then pitch = pitch + 0.2 end;
			end	
			Utils.setSamplePitch(self.dieselWorkSound, pitch)

			if self.FadePS + 0.01 < pitch then
				self.FadePS = self.FadePS + 0.05
			elseif self.FadePS - 0.01 > pitch then
				self.FadePS = self.FadePS - 0.01
			end
			local color = 0.8-self.FadePS
			
			Utils.setEmitCountScale(self.onParticleSystems, self.FadePS);
            for _, ps in ipairs(self.onParticleSystems) do
                setParticleSystemLifespan(ps.geometry, ps.originalLifespan * self.FadePS, true);
				if getHasShaderParameter(ps.shape,"psColor") then
					setShaderParameter(ps.shape,"psColor",color,color,color,1,false)	
				end;
			end;
			
			if self:getCapacity() <= self.fillLevel then
				if self:getIsTurnedOn() then
					self:setIsTurnedOn(false)
					local warning = string.format(L("BunkerVoll"), self.typeDesc)
					g_currentMission:showBlinkingWarning(warning, 5000)
					self:setBeaconLightsVisibility(true,true)
					Utils.playSample(self.WarnSound, 1, 0, nil)
				end;
				self:BlinkSigLight("crusher",dt)
			elseif self.SigLight and self.SigLight["crusher"] and self.SigLight["crusher"].elaps then
				self.SigLight["crusher"].elaps = nil;
				setVisibility(self.SigLight["crusher"].node, false);
				self:setBeaconLightsVisibility(false,true)
			end;
		end;
		
		if self.pipeIsUnloading then
			playSample(self.PipeFillS.sound3D, 0, 0, nil)
			setVisibility(self.PipeFillS.sound3D, true)
			self.PipeFillS.isPlaying = true;
		elseif self.PipeFillS.isPlaying then
			stopSample(self.PipeFillS.sound3D)
			setVisibility(self.PipeFillS.sound3D, false)
			self.PipeFillS.isPlaying = nil;
		end
	end
	if self.isServer then
		if self.isFuelFilling then
            local delta = 0;
            if self.fuelFillTrigger ~= nil then
                delta = self.fuelFillLitersPerSecond*dt*0.001;
                delta = self.fuelFillTrigger:fillFuel(self, delta);
            end
            if delta <= 0.001 then
                self:setIsFuelFilling(false);
            end
        end
		if math.abs(self.fuelFillLevel-self.sentFuelFillLevel) > 0.001 then
            self:raiseDirtyFlags(self.TheBeastFSMDirtyFlag);
            self.sentFuelFillLevel = self.fuelFillLevel;
		end;
	end;
end

local function addHelpTextFunction(param)
	g_currentMission:addHelpTextFunction(TheBeastFSM.drawHelpText,nil,g_currentMission.hudHelpContentHeight,param)
end

function TheBeastFSM:draw()
	if self.changeAllowed then
		if self.attacherVehicle ~= nil then
			addHelpTextFunction({t=L("DetachTruck"),c={1,0,0,1}})
		else
			if self.DieselOn then
				g_currentMission:addHelpButtonText(L("DieselOff"), InputBinding.THEBEASTFSM_TOGGLEDIESEL)
				Foldable.draw(self);
				TurnOnVehicle.draw(self);
				Overloading.draw(self);
				if self.workLights then
					g_currentMission:addHelpButtonText(L("SelectLight"), InputBinding.THEBEASTFSM_TOGGLELIGHT)
					if self.workLightsSelect > 0 then
						g_currentMission:addHelpButtonText(string.format("%s %s",L("Light"..self.workLightsSelect), L("LightOnOff")), InputBinding.THEBEASTFSM_LIGHTONOFF)
						g_currentMission:addHelpButtonText(L("LightUp"), InputBinding.THEBEASTFSM_LIGHTUP)
						g_currentMission:addHelpButtonText(L("LightDown"), InputBinding.THEBEASTFSM_LIGHTDOWN)
						g_currentMission:addHelpButtonText(L("LightLeft"), InputBinding.THEBEASTFSM_LIGHTLEFT)
						g_currentMission:addHelpButtonText(L("LightRight"), InputBinding.THEBEASTFSM_LIGHTRIGHT)
					end
				end
			else
				g_currentMission:addHelpButtonText(L("DieselOn"), InputBinding.THEBEASTFSM_TOGGLEDIESEL)
			end
		end
	end
	if self.changeAllowed or table.getn(self.fuelFillTriggers) ~= 0 then 
		addHelpTextFunction({t=string.format("%s: %d %s",L("DieselLvl"),self.fuelFillLevel,g_i18n:getText("fluid_unit_long")),c={0, 0, 1, 1}})
	end
end;

function TheBeastFSM.drawHelpText(posY,param)
	setTextColor(unpack(param.c))
	setTextAlignment(RenderText.ALIGN_RIGHT)
	renderText(g_currentMission.hudHelpTextPosX2, posY-g_currentMission.hudHelpContentHeight, g_currentMission.hudHelpTextSize, param.t)
	setTextAlignment(RenderText.ALIGN_LEFT)
	setTextColor(1, 1, 1, 1)
end

function TheBeastFSM:onAttach(attacherVehicle, jointDescIndex)
	self:setDieselOn(false)
end; 

function TheBeastFSM:onDetach(attacherVehicle, jointDescIndex)
	
end;

function TheBeastFSM:getIsActive(superFunc)
	if self.DieselOn then
		if g_gui.currentGui ~= nil or g_currentMission.isPlayerFrozen then
			return false
		end
		return true
	else
		return superFunc(self)
	end
end

function TheBeastFSM:getIsActiveForInput(superFunc, onlyTrueIfSelected, askActivatable)
	if not self.DieselOn or not self.changeAllowed then
		if (self.attacherVehicle == nil or askActivatable) then return superFunc(self, onlyTrueIfSelected) end;
	else
		if g_gui.currentGui ~= nil or g_currentMission.isPlayerFrozen then
			return false
		end
		return true
	end

end

function TheBeastFSM:getIsFoldAllowed(superFunc)
	if self.attacherVehicle == nil then
		if not self.DieselOn then
			return false;
		else
			if g_gui.currentGui ~= nil or g_currentMission.isPlayerFrozen then return false	end;
			return true
		end;
	end;
	return superFunc(self)
end

function TheBeastFSM:onTurnedOn(noEventSend)
	if self.attacherVehicle == nil and self.DieselOn then
		setVisibility(self.sampleWoodCrusherStart.sound3D, true)
		setVisibility(self.sampleWoodCrusherIdle.sound3D, false)
		setVisibility(self.sampleWoodCrusherWork.sound3D, false)
		setVisibility(self.sampleWoodCrusherStop.sound3D, false)
		playSample(self.sampleWoodCrusherStart.sound3D, 1, 0, nil)
		stopSample(self.sampleWoodCrusherIdle.sound3D)
		stopSample(self.sampleWoodCrusherWork.sound3D)
		stopSample(self.sampleWoodCrusherStop.sound3D)
		self.sampleWoodCrusherStart.startTime = g_currentMission.time + (self.sampleWoodCrusherStart.duration*.9)
		self.sampleWoodCrusherStop.stopTime = -1
		if self.SigLight and self.SigLight["crusher"] then setVisibility(self.SigLight["crusher"].node, true); end;
	end
end;

function TheBeastFSM:onTurnedOff(noEventSend)
	setVisibility(self.sampleWoodCrusherStart.sound3D, false)
	setVisibility(self.sampleWoodCrusherIdle.sound3D, false)
	setVisibility(self.sampleWoodCrusherWork.sound3D, false)
	setVisibility(self.sampleWoodCrusherStop.sound3D, true)
	stopSample(self.sampleWoodCrusherStart.sound3D)
	stopSample(self.sampleWoodCrusherIdle.sound3D)
	stopSample(self.sampleWoodCrusherWork.sound3D)
	playSample(self.sampleWoodCrusherStop.sound3D, 1, 0, nil)
	self.sampleWoodCrusherStart.startTime = -1
	self.sampleWoodCrusherStop.stopTime = g_currentMission.time + (self.sampleWoodCrusherStop.duration*.8)
	if self.SigLight and self.SigLight["crusher"] then setVisibility(self.SigLight["crusher"].node, false); end;
end;

function TheBeastFSM:setDieselOn(on, noEventSend)
	if self.DieselOn ~= on then
		TheBeastFSMsetDieselEvent.sendEvent(self, on, noEventSend)
		self.DieselOn = on
		if self.DieselOn then
			setVisibility(self.dieselStartSound.sound3D, true)
			setVisibility(self.dieselIdleSound.sound3D, false)
			setVisibility(self.dieselWorkSound.sound3D, false)
			setVisibility(self.dieselStopSound.sound3D, false)
			playSample(self.dieselStartSound.sound3D, 1, 0, nil)
			stopSample(self.dieselIdleSound.sound3D)
			stopSample(self.dieselWorkSound.sound3D)
			stopSample(self.dieselStopSound.sound3D)
			self.dieselStartSound.startTime = g_currentMission.time + (self.dieselStartSound.duration*.8)
			self.dieselStopSound.stopTime = -1
			if self.SigLight and self.SigLight["diesel"] then setVisibility(self.SigLight["diesel"].node, true); end;
		else
			setVisibility(self.dieselStartSound.sound3D, false)
			setVisibility(self.dieselIdleSound.sound3D, false)
			setVisibility(self.dieselWorkSound.sound3D, false)
			setVisibility(self.dieselStopSound.sound3D, true)
			stopSample(self.dieselStartSound.sound3D)
			stopSample(self.dieselIdleSound.sound3D)
			stopSample(self.dieselWorkSound.sound3D)
			playSample(self.dieselStopSound.sound3D, 1, 0, nil)
			self.dieselStartSound.startTime = -1
			self.dieselStopSound.stopTime = g_currentMission.time + (self.dieselStopSound.duration*.8)
			if self.SigLight and self.SigLight["diesel"] then setVisibility(self.SigLight["diesel"].node, false); end;
			self:onDeactivate()
		end
	end;
end
	
function TheBeastFSM:setLights(num, vis, x, y, noEventSend)
	if noEventSend == nil or noEventSend == false then
        if g_server ~= nil then
            g_server:broadcastEvent(TheBeastFSMSetLightsEvent:new(self, num, vis, x, y), nil, nil, self);
        else
            g_client:getServerConnection():sendEvent(TheBeastFSMSetLightsEvent:new(self, num, vis, x, y));
        end
    end

	local x1, _, z1 = getRotation(self.workLights[num].node1)
	local _, y2, z2 = getRotation(self.workLights[num].node2)
	if self.isClient then setVisibility(self.workLights[num].node3, vis); end;
	setRotation(self.workLights[num].node1, x1, y, z1)
	setRotation(self.workLights[num].node2, x, y2, z2)
	self.workLights[num].vis = vis;
	self.workLights[num].x = x;
	self.workLights[num].y = y;
end

function TheBeastFSM:BlinkSigLight(typ,dt)
	if self.SigLight and self.SigLight[typ] then
		self.SigLight[typ].elaps = (self.SigLight[typ].elaps or 0) + dt;
		if self.SigLight[typ].elaps >= 500 then
			self.SigLight[typ].elaps = self.SigLight[typ].elaps - 500;
			setVisibility(self.SigLight[typ].node, not getVisibility(self.SigLight[typ].node)); 
		end
	end;
end

function TheBeastFSM:crushSplitShape(superFunc, shape)
	local splitType = SplitUtil.splitTypes[getSplitType(shape)]
	superFunc(self,shape)
	if self.crushingTimeOverWrite and splitType ~= nil and 0 < splitType.woodChipsPerLiter then
		self.crushingTime = self.crushingTimeOverWrite;
	end;
end;

function TheBeastFSM:setFillLevel(superFunc, fillLevel, fillType, force, fillSourceStruct)
	if self.isServer and self.firstTimeRun and fillLevel > (self.fillLevel + (self.overloadingCapacity*1.1)) then
		if not self.setFillLevelCache then self.setFillLevelCache = {} end
		local LevelCache = {lvl = fillLevel-self.fillLevel,typ = fillType, force = force, SourceStruct = fillSourceStruct};
		table.insert(self.setFillLevelCache,LevelCache);
	else
		superFunc(self, fillLevel, fillType, force, fillSourceStruct)
	end
end

function TheBeastFSM:setFuelFillLevel(newFillLevel)
    self.fuelFillLevel = math.max(math.min(newFillLevel, self.fuelCapacity), 0);
end;

function TheBeastFSM:setIsFuelFilling(isFilling, noEventSend)
    if isFilling ~= self.isFuelFilling then
        if noEventSend == nil or noEventSend == false then
            if g_server ~= nil then
                g_server:broadcastEvent(SteerableToggleRefuelEvent:new(self, isFilling), nil, nil, self);
            else
                g_client:getServerConnection():sendEvent(SteerableToggleRefuelEvent:new(self, isFilling));
            end;
        end;
        self.isFuelFilling = isFilling;
        if isFilling then
            self.fuelFillTrigger = nil;
            for i=1, table.getn(self.fuelFillTriggers) do
                local trigger = self.fuelFillTriggers[i];
                if trigger:getIsActivatable(self) then
                    self.fuelFillTrigger = trigger;
                    break;
                end;
            end;
        end
        if self.isClient and self.sampleRefuel ~= nil then
            if isFilling then
                Utils.play3DSample(self.sampleRefuel);
            else
                Utils.stop3DSample(self.sampleRefuel);
            end;
        end;
    end
end

function TheBeastFSM:addFuelFillTrigger(trigger)
	if table.getn(self.fuelFillTriggers) == 0 then
        g_currentMission:addActivatableObject(self.motorizedFillActivatable);
    end;
    table.insert(self.fuelFillTriggers, trigger);
end;

function TheBeastFSM:removeFuelFillTrigger(trigger)
    for i=1, table.getn(self.fuelFillTriggers) do
        if self.fuelFillTriggers[i] == trigger then
            table.remove(self.fuelFillTriggers, i);
            break;
        end;
    end;
    if table.getn(self.fuelFillTriggers) == 0 or trigger == self.fuelFillTrigger then
        if self.isServer then
            self:setIsFuelFilling(false);
        end;
        if table.getn(self.fuelFillTriggers) == 0 then
            g_currentMission:removeActivatableObject(self.motorizedFillActivatable);
        end
    end;
end;

function TheBeastFSM:IsFillActivatable(me)
	 if (self:getIsActiveForInput(false,true) or self.changeAllowed) and self.fuelFillLevel-1 < self.fuelCapacity then
        for i=1, table.getn(self.fuelFillTriggers) do
            local trigger = self.fuelFillTriggers[i];
            if trigger:getIsActivatable(self) then
                me:updateActivateText();
                return true;
            end
        end
    end
	return false;
end

function TheBeastFSM:PlayerTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay)
	if (g_currentMission.controlPlayer and g_currentMission.player and otherId == g_currentMission.player.rootNode) then
		if (onEnter) then 
            self.changeAllowed = true;
        elseif (onLeave) then
            self.changeAllowed = false;
			if self.CamSet then
				g_currentMission:removeSpecialCamera(self.workLights[self.CamSet].Cam)
				self.CamSet = nil;
			end
        end;
	end;
end;

TheBeastFSMsetDieselEvent = {}
TheBeastFSMsetDieselEvent_mt = Class(TheBeastFSMsetDieselEvent, Event)
InitEventClass(TheBeastFSMsetDieselEvent, "TheBeastFSMsetDieselEvent")
function TheBeastFSMsetDieselEvent:emptyNew()
	local self = Event:new(TheBeastFSMsetDieselEvent_mt);
	return self;
end
function TheBeastFSMsetDieselEvent:new(object, DieselOn)
	local self = TheBeastFSMsetDieselEvent:emptyNew();
	self.object = object;
	self.DieselOn = DieselOn;
	return self;
end
function TheBeastFSMsetDieselEvent:readStream(streamId, connection)
	local id = streamReadInt32(streamId)
	self.DieselOn = streamReadBool(streamId)
	self.object = networkGetObject(id)
	self:run(connection)
end
function TheBeastFSMsetDieselEvent:writeStream(streamId, connection)
	streamWriteInt32(streamId, networkGetObjectId(self.object))
	streamWriteBool(streamId, self.DieselOn)
end
function TheBeastFSMsetDieselEvent:run(connection)
	if not connection:getIsServer() then
		g_server:broadcastEvent(self, false, connection, self.object)
	end

	if self.object ~= nil then
		self.object:setDieselOn(self.DieselOn, true)
	end
end
function TheBeastFSMsetDieselEvent.sendEvent(vehicle, DieselOn, noEventSend)
	if DieselOn ~= vehicle.DieselOn and (noEventSend == nil or noEventSend == false) then
		if g_server ~= nil then
			g_server:broadcastEvent(TheBeastFSMsetDieselEvent:new(vehicle, DieselOn), nil, nil, vehicle)
		else
			g_client:getServerConnection():sendEvent(TheBeastFSMsetDieselEvent:new(vehicle, DieselOn))
		end
	end;
end

TheBeastFSMSetLightsEvent = {};
TheBeastFSMSetLightsEvent_mt = Class(TheBeastFSMSetLightsEvent, Event);
InitEventClass(TheBeastFSMSetLightsEvent, "TheBeastFSMSetLightsEvent");
function TheBeastFSMSetLightsEvent:emptyNew()
    local self = Event:new(TheBeastFSMSetLightsEvent_mt);
    return self;
end;
function TheBeastFSMSetLightsEvent:new(object, num, vis, x, y)
    local self = TheBeastFSMSetLightsEvent:emptyNew()
    self.object = object;
	self.num = num;
	self.vis = vis;
	self.x = x;
	self.y = y;
    return self;
end;
function TheBeastFSMSetLightsEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.object));
	streamWriteInt8(streamId, self.num);
	streamWriteBool(streamId, self.vis)
	Utils.writeCompressedAngle(streamId, self.x)
	Utils.writeCompressedAngle(streamId, self.y)
end;
function TheBeastFSMSetLightsEvent:readStream(streamId, connection)
	local id = streamReadInt32(streamId);
	self.num = streamReadInt8(streamId);
	self.vis = streamReadBool(streamId);
	self.x = Utils.readCompressedAngle(streamId)
	self.y = Utils.readCompressedAngle(streamId)
	self.object = networkGetObject(id);
	self:run(connection);
end;
function TheBeastFSMSetLightsEvent:run(connection)
	self.object:setLights(self.num, self.vis, self.x, self.y, true);
	if not connection:getIsServer() then
		g_server:broadcastEvent(TheBeastFSMSetLightsEvent:new(self.object, self.num, self.vis, self.x, self.y), nil, connection, self.object);
	end;
end;

