--
-- AutoLoadWoodForwarder for FS22
-- @author:    	Marhu, kenny456 - FS19/FS22 conversion (kenny456@seznam.cz)
-- @history:	v1.0 - 2018-12-25 - converted to FS19
--								  - added function to move loading position FWD/BWD depending on logs length
--				v1.1 - 2018-12-28 - fixed multiplayer errors on dedi server
--				v1.2 - 2018-03-09 - -added new 15m and 20m trailers
--									-fixed showing controls when attached to dolly
--									-Autoload script edited (logs are now sorted to the piles)
--				v1.0.0.0 - 2019-12-16 - first release for modHub
--				v1.1.0.0 - 2020-12-14 - fixed conflict with Vehicle Straps mod
--									  - improve stability of the trailers
--									  - fixed issue when not all logs are unloaded from trailer
--									  - count and total mass of loaded logs are displayed in F1 menu
--				2021-12-03 - converted to FS22
--				2022-04-03 - Edit for forwarders
AutoLoadWoodForwarder = {};
AutoLoadWoodForwarder.confDir = getUserProfileAppPath().. "modsSettings/AutoLoadWoodForwarder/";
local modName = g_currentModName

function AutoLoadWoodForwarder:getTexts(i,text)
	if g_currentMission.AutoLoadWoodForwarder == nil then
		g_currentMission.AutoLoadWoodForwarder = {}
		g_currentMission.AutoLoadWoodForwarder.texts = {}
	end
	g_currentMission.AutoLoadWoodForwarder.texts[i] = text
end

function AutoLoadWoodForwarder.prerequisitesPresent(specializations)
    return true
end
function AutoLoadWoodForwarder.registerOverwrittenFunctions(vehicleType)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "dynamicMountTriggerCallback", AutoLoadWoodForwarder.dynamicMountTriggerCallback)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsTurnedOn", AutoLoadWoodForwarder.getIsTurnedOn)
end

function AutoLoadWoodForwarder.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "toggleState", AutoLoadWoodForwarder.toggleState)
	SpecializationUtil.registerFunction(vehicleType, "toggleLoad", AutoLoadWoodForwarder.toggleLoad)
	SpecializationUtil.registerFunction(vehicleType, "toggleUnload", AutoLoadWoodForwarder.toggleUnload)
	SpecializationUtil.registerFunction(vehicleType, "updateValues", AutoLoadWoodForwarder.updateValues)
	SpecializationUtil.registerFunction(vehicleType, "updateLoad", AutoLoadWoodForwarder.updateLoad)
	SpecializationUtil.registerFunction(vehicleType, "updateUnload", AutoLoadWoodForwarder.updateUnload)
	--SpecializationUtil.registerFunction(vehicleType, "getIsTurnedOn", AutoLoadWoodForwarder.getIsTurnedOn)
	SpecializationUtil.registerFunction(vehicleType, "saveToXml", AutoLoadWoodForwarder.saveToXml)
	SpecializationUtil.registerFunction(vehicleType, "loadFromXml", AutoLoadWoodForwarder.loadFromXml)
end
function AutoLoadWoodForwarder:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
	local spec = self.spec_autoLoadWoodForwarder
	
	if spec.event_IDs == nil then
		spec.event_IDs = {}
	else
		self:clearActionEventsTable(spec.event_IDs)
	end
	if isActiveForInputIgnoreSelection then
		if g_dedicatedServerInfo ~= nil then
			return
		end
		local actions = { InputAction.AUTOLOADWOOD_TOGGLE_AUTO, InputAction.AUTOLOADWOOD_TOGGLE_UNLOAD, InputAction.AUTOLOADWOOD_TOGGLE_SIDE,
							InputAction.AUTOLOADWOOD_TOGGLE_PILES, InputAction.AUTOLOADWOOD_SWITCH_PILE, InputAction.AUTOLOADWOOD_TOGGLE_LOAD_MARKER, InputAction.AUTOLOADWOOD_TOGGLE_UNLOAD_MARKER,
							InputAction.AUTOLOADWOOD_UNLOAD_MARKER_MOVE, InputAction.AUTOLOADWOOD_UNLOAD_MARKER_SCALE, InputAction.AUTOLOADWOOD_UNLOAD_MARKER_ROTATE, InputAction.AUTOLOADWOOD_TOGGLE_HELP}
		for _,actionName in pairs(actions) do
			local always = (actionName == InputAction.IREWOODPROCESSOR_FEED_LOG or actionName == InputAction.IREWOODPROCESSOR_FEED_LOG_BACK) and true or false
			local _, eventID = g_inputBinding:registerActionEvent(actionName, self, AutoLoadWoodForwarder.actionCallback, true, true, always, true)
			spec.event_IDs[actionName] = eventID
			if g_inputBinding ~= nil and g_inputBinding.events ~= nil and g_inputBinding.events[eventID] ~= nil then
				if actionName == 'something with lower priority' then
					g_inputBinding:setActionEventTextPriority(eventID, GS_PRIO_NORMAL)
				elseif actionName == InputAction.AUTOLOADWOOD_TOGGLE_AUTO or actionName == InputAction.AUTOLOADWOOD_TOGGLE_UNLOAD or actionName == InputAction.AUTOLOADWOOD_TOGGLE_SIDE or actionName == InputAction.AUTOLOADWOOD_TOGGLE_PILES then
					g_inputBinding:setActionEventTextPriority(eventID, GS_PRIO_VERY_HIGH)
				else
					g_inputBinding:setActionEventTextPriority(eventID, GS_PRIO_HIGH)
				end
				if actionName == InputAction.AUTOLOADWOOD_TOGGLE_HELP then
					g_inputBinding:setActionEventTextVisibility(eventID, true)
				else
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWoodForwarder.showHelp)
				end
			end
			local colliding = false
			_, colliding, _ = g_inputBinding:checkEventCollision(actionName)
			if colliding then
				if g_inputBinding.nameActions[actionName].bindings[1] ~= nil then
					if g_inputBinding.nameActions[actionName].bindings[1].inputString ~= nil then
						print(string.format('Warning: AutoLoadWoodForwarder got a colliding input action: %s', actionName)..' ('..g_inputBinding.nameActions[actionName].bindings[1].inputString..'). You can remap it in controls settings')
					end
				else
					print(string.format('Warning: AutoLoadWoodForwarder got a colliding input action: %s', actionName))
				end
			end
		end
	end
end
function AutoLoadWoodForwarder.registerEventListeners(vehicleType)
	for _,n in pairs( { "onLoad","onPostLoad","saveToXMLFile","onUpdate","onDraw","onRegisterActionEvents","registerActionEventsPlayer","saveToXml","loadFromXml","onReadStream","onWriteStream" } ) do
		SpecializationUtil.registerEventListener(vehicleType, n, AutoLoadWoodForwarder)
	end
end

function AutoLoadWoodForwarder:onLoad(savegame)
	self.spec_autoLoadWoodForwarder = {}
	local spec = self.spec_autoLoadWoodForwarder
	local xmlFile = self.xmlFile
	spec.event_IDs = {}
	
	spec.searchSizeY = Utils.getNoNil(getXMLFloat(xmlFile.handle, "vehicle.AutoLoadWoodForwarder#SizeY"),5);
	spec.searchSizeZ = Utils.getNoNil(getXMLFloat(xmlFile.handle, "vehicle.AutoLoadWoodForwarder#SizeZ"),10);
	spec.autoLoadSpeed = Utils.getNoNil(getXMLFloat(xmlFile.handle, "vehicle.AutoLoadWoodForwarder#Speed"),500);
	spec.autoLoadElaps = 0;
	spec.dynamicMountTrigger = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile.handle,"vehicle.AutoLoadWoodForwarder#dynamicMountTrigger"), self.i3dMappings)
	spec.autoLoadWood = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile.handle,"vehicle.AutoLoadWoodForwarder#node", self.i3dMappings))
	addTrigger(spec.dynamicMountTrigger, "dynamicMountTriggerCallback", self)
	
	local baseKey = "vehicle.AutoLoadWoodForwarder"
	spec.bunks = {}
    local i = 0
    while true do
        local key = string.format("%s.bunks.bunk(%d)", baseKey, i)
        if not hasXMLProperty(xmlFile.handle, key) then
            break
        end
        local bunk = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#size"),6)
        table.insert(spec.bunks, bunk)
        i = i + 1
    end
	spec.maxPiles = i
	
	local x,y,z = getTranslation(spec.dynamicMountTrigger)
	local key = "vehicle.AutoLoadWoodForwarder.fillPose";
	local fillPoseXOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#xOff"),0);
	local fillPoseYOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#yOff"),2);
	local fillPoseZOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#zOff"),0);
	
	local key = "vehicle.AutoLoadWoodForwarder.unloadPose";
	local unloadPoseXOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#xOff"),0);
	local unloadPoseYOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#yOff"),5);
	local unloadPoseZOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#zOff"),0);
		
	spec.fillPoseNodeX = x + fillPoseXOff
	spec.fillPoseNodeY = y + fillPoseYOff
	spec.fillPoseNodeZ = z + fillPoseZOff
	spec.fillPoseRoot = createTransformGroup("fillPoseRoot");
	spec.fillPoseNode = createTransformGroup("fillPoseNode");
	link(spec.autoLoadWood,spec.fillPoseRoot);
	link(spec.fillPoseRoot,spec.fillPoseNode);
	setTranslation(spec.fillPoseRoot, spec.fillPoseNodeX, spec.fillPoseNodeY, spec.fillPoseNodeZ)
	setRotation(spec.fillPoseRoot,math.rad(-90),0,0)
	
	local linkNode = getParent(spec.dynamicMountTrigger)
	spec.unloadPoseRoot = {	createTransformGroup("unloadPoseRoot1"),
							createTransformGroup("unloadPoseRoot2")};
	spec.unloadPoseNode = {	createTransformGroup("unloadPoseNode1"),
								createTransformGroup("unloadPoseNode2")};
	spec.unloadPoseRootX = 2
	spec.unloadPoseRootY = 0
	spec.unloadPoseRootZ = 0
	for i = 1, 2 do
		link(linkNode, spec.unloadPoseRoot[i]);
		setTranslation(spec.unloadPoseRoot[i],spec.unloadPoseRootX,spec.unloadPoseRootY,spec.unloadPoseRootZ)
		setRotation(spec.unloadPoseRoot[i],math.rad(0),math.rad(0),0)
		spec.unloadPoseRootX = spec.unloadPoseRootX * -1;
	end
	
	spec.unloadPoseNodeX =  unloadPoseXOff
	spec.unloadPoseNodeY  = unloadPoseYOff
	spec.unloadPoseNodeZ  = unloadPoseZOff
	for i = 1, 2 do					
		link(spec.unloadPoseRoot[i], spec.unloadPoseNode[i]);
		setTranslation(spec.unloadPoseNode[i], spec.unloadPoseNodeX, spec.unloadPoseNodeY, spec.unloadPoseNodeZ)
		setRotation(spec.unloadPoseNode[i],math.rad(-90),0,0)
		spec.unloadPoseNodeX = spec.unloadPoseNodeX * -1;
	end
		
	spec.loadMarker = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile.handle,"vehicle.AutoLoadWoodForwarder#loadMarker"), self.i3dMappings)
	spec.unloadMarker = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile.handle,"vehicle.AutoLoadWoodForwarder#unloadMarker"), self.i3dMappings)
	if spec.loadMarker ~= nil then
		spec.markerX, spec.markerY, spec.markerZ = getTranslation(spec.loadMarker)
		setVisibility(spec.loadMarker, false);
		link(spec.fillPoseRoot, spec.loadMarker);
		setRotation(spec.loadMarker,math.rad(90),0,math.rad(90))
		setTranslation(spec.loadMarker,0,0,-2.7)
	end
	if spec.unloadMarker ~= nil then
		spec.markerX, spec.markerY, spec.markerZ = getTranslation(spec.unloadMarker)
		setVisibility(spec.unloadMarker, false);
		link(spec.unloadPoseRoot[1], spec.unloadMarker);
	end
	
	if self.isServer then
		local key = "vehicle.AutoLoadWoodForwarder.woodTrigger";
		local woodTriggerXOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#xOff"),1.2);
		local woodTriggerYOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#yOff"),-3);
		local woodTriggerZOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#zOff"),1.5);

		local key = "vehicle.AutoLoadWoodForwarder.limitToLen";
		spec.loadMinLen = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#minLen"),2);
		spec.loadMaxLen = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#maxLen"),20);
		spec.loadMaxAtt = Utils.getNoNil(getXMLInt(xmlFile.handle, key .. "#delimbOk"),0);
		
		local linkNode = getParent(spec.dynamicMountTrigger)
		local x,y,z = getTranslation(spec.dynamicMountTrigger)
		
		spec.woodTrigger = {createTransformGroup("woodTrigger1"),
							createTransformGroup("woodTrigger2"),
							createTransformGroup("woodTrigger3"),
							createTransformGroup("woodTrigger4"),
							createTransformGroup("woodTrigger5"),
							createTransformGroup("woodTrigger6")}
		
		local rot = math.rad(90)
		local woodTriggerZOffd = woodTriggerZOff
		for i = 1, 6 do
			link(linkNode, spec.woodTrigger[i])
			setTranslation(spec.woodTrigger[i], x + woodTriggerXOff, y + woodTriggerYOff, z + woodTriggerZOffd)
			setRotation(spec.woodTrigger[i], 0, rot, 0)
			woodTriggerZOffd = woodTriggerZOffd - woodTriggerZOff
			if i == 3 then 
				woodTriggerXOff = woodTriggerXOff * -1
				rot = math.rad(-90)
				woodTriggerZOffd = woodTriggerZOff
			end
		end
	end
	self.onDeleteBackup = self.delete
	self.delete = Utils.prependedFunction(self.delete, AutoLoadWoodForwarder.onDeleteAL)
end
function AutoLoadWoodForwarder:onPostLoad(savegame)
	local spec = self.spec_autoLoadWoodForwarder
	local xmlFile = self.xmlFile
	if spec.event_IDs == nil then
		spec.event_IDs = {}
	end
	spec.woodLoad = {};
	spec.autoload = false;
	spec.rightSide = false;
	spec.unload = false;
	spec.showLoadPos = false;
	spec.numPiles = 2;
	spec.pile = 1;
	spec.showUnloadPos = false;
	spec.unloadPos = spec.unloadPoseRootX
	spec.unloadScale = 1;
	spec.unloadRot = false;
	spec.space = 0.3
	spec.rootShiftRotX = 6;
	spec.rootShiftRotXMax = 9;
	AutoLoadWoodForwarder.showHelp = true
	spec.logsCount = 0
	spec.logsMass = 0
	local x,y,z = getCenterOfMass(self.rootNode)
	spec.massYOrig = y
	spec.massYLast = spec.massYOrig
	
	self:updateValues()
	self:updateLoad()
	self:updateUnload()
	
	if savegame ~= nil then
		local xmlFile = savegame.xmlFile
		local key = savegame.key.."."..modName..".AutoLoadWoodForwarder"
		local rightSide = Utils.getNoNil(getXMLBool(xmlFile.handle, key.."#rightSide"), spec.rightSide);
		local showLoadPos = Utils.getNoNil(getXMLBool(xmlFile.handle, key.."#showLoadPos"), spec.showLoadPos);
		local showUnloadPos = Utils.getNoNil(getXMLBool(xmlFile.handle, key.."#showUnloadPos"), spec.showUnloadPos);
		local numPiles = Utils.getNoNil(getXMLInt(xmlFile.handle, key.."#numPiles"), spec.numPiles);
		local pile = Utils.getNoNil(getXMLInt(xmlFile.handle, key.."#pile"), spec.pile);
		local unloadPos = Utils.getNoNil(getXMLFloat(xmlFile.handle, key.."#unloadPos"), spec.unloadPos);
		local unloadScale = Utils.getNoNil(getXMLFloat(xmlFile.handle, key.."#unloadScale"), spec.unloadScale);
		local unloadRot = Utils.getNoNil(getXMLBool(xmlFile.handle, key.."#unloadRot"), spec.unloadRot);
		
		self:toggleState(autoload, rightSide, unload, true)
		self:toggleLoad(showLoadPos, numPiles, pile, true)
		self:toggleUnload(showUnloadPos, unloadPos, unloadScale, unloadRot, true)
	end
	
	local configFile = AutoLoadWoodForwarder.confDir .. "AutoLoadWoodForwarderConfig.xml";
	if fileExists(configFile) then
		AutoLoadWoodForwarder.configXml = loadXMLFile("AutoLoadWoodForwarder_XML", configFile);
		self:loadFromXml(self:getFullName());
	else
		createFolder(getUserProfileAppPath().. "modsSettings/");
		createFolder(AutoLoadWoodForwarder.confDir);
		AutoLoadWoodForwarder.configXml = createXMLFile("AutoLoadWoodForwarder_XML", configFile, "AutoLoadWoodForwarderConfig");
		self:saveToXml();
		saveXMLFile(AutoLoadWoodForwarder.configXml);
	end
	if self.spec_vehicleStraps ~= nil then
		self.spec_vehicleStraps.modInitialized = false
		self.spec_vehicleStraps.modAllowed = false
		self.dynamicMountTriggerCallback = Utils.overwrittenFunction(self.dynamicMountTriggerCallback, AutoLoadWoodForwarder.dynamicMountTriggerCallback)
	end
end
function AutoLoadWoodForwarder:onDelete()
	local spec = self.spec_autoLoadWoodForwarder
	if spec.dynamicMountTrigger ~= nil then
        removeTrigger(spec.dynamicMountTrigger)
    end
end
function AutoLoadWoodForwarder:onDeleteAL()
	local spec = self.spec_autoLoadWoodForwarder
	
	if spec.dynamicMountTrigger ~= nil then
		removeTrigger(spec.dynamicMountTrigger)
	end
end
function AutoLoadWoodForwarder:saveToXMLFile(xmlFile, key)
	local spec = self.spec_autoLoadWoodForwarder
	
	setXMLBool(xmlFile.handle, key.."#rightSide", spec.rightSide)
	setXMLBool(xmlFile.handle, key.."#showLoadPos", spec.showLoadPos)
	setXMLBool(xmlFile.handle, key.."#showUnloadPos", spec.showUnloadPos)
	setXMLInt(xmlFile.handle, key.."#numPiles", spec.numPiles)
	setXMLInt(xmlFile.handle, key.."#pile", spec.pile)
	setXMLFloat(xmlFile.handle, key.."#unloadPos", spec.unloadPos)
	setXMLFloat(xmlFile.handle, key.."#unloadScale", spec.unloadScale)
	setXMLBool(xmlFile.handle, key.."#unloadRot", spec.unloadRot)
end
function AutoLoadWoodForwarder:loadFromXml()
	local spec = self.spec_autoLoadWoodForwarder
    if AutoLoadWoodForwarder.configXml == nil then
        return
    end
	if getXMLBool(AutoLoadWoodForwarder.configXml, "AutoLoadWoodForwarderConfig.showHelp") ~= nil then
		AutoLoadWoodForwarder.showHelp = getXMLBool(AutoLoadWoodForwarder.configXml, "AutoLoadWoodForwarderConfig.showHelp")
	end
end
function AutoLoadWoodForwarder:saveToXml()
	local spec = self.spec_autoLoadWoodForwarder
    if AutoLoadWoodForwarder.configXml == nil then
        return;
    end
	setXMLBool(AutoLoadWoodForwarder.configXml, "AutoLoadWoodForwarderConfig.showHelp", AutoLoadWoodForwarder.showHelp);
    saveXMLFile(AutoLoadWoodForwarder.configXml);
end
function AutoLoadWoodForwarder:onUpdate(dt, vehicle)
	local spec = self.spec_autoLoadWoodForwarder
	
	if self.getIsControlled ~= nil then
		if not self:getIsControlled() then
			if getVisibility(spec.unloadMarker) then
				setVisibility(spec.unloadMarker, false)
			end
			if getVisibility(spec.loadMarker) then
				setVisibility(spec.loadMarker, false)
			end
		elseif self:getIsControlled() then
			if not getVisibility(spec.unloadMarker) and spec.showUnloadPos then
				setVisibility(spec.unloadMarker, true)
			end
			if not getVisibility(spec.loadMarker) and spec.showLoadPos then
				setVisibility(spec.loadMarker, true)
			end
		end
	else
		if not self:getIsActive() then
			if getVisibility(spec.unloadMarker) then
				setVisibility(spec.unloadMarker, false)
			end
			if getVisibility(spec.loadMarker) then
				setVisibility(spec.loadMarker, false)
			end
		elseif self:getIsActive() then
			if not getVisibility(spec.unloadMarker) and spec.showUnloadPos then
				setVisibility(spec.unloadMarker, true)
			end
			if not getVisibility(spec.loadMarker) and spec.showLoadPos then
				setVisibility(spec.loadMarker, true)
			end
		end
	end
	local count = 0
	local mass = 0
	if self:getIsActive() then
		for k, shape in pairs(spec.woodLoad) do
			if entityExists(shape) then
				count = count + 1
				mass = mass + getMass(shape)
			else
				spec.woodLoad[k] = nil
			end
		end
	end
	local massY
	if count > 0 then
		local k = 0
		if spec.trailer == 20 then
			k = -8.56
		elseif spec.trailer == 15 then
			k = -6.37
		elseif spec.trailer == 12 then
			k = -5.64
		end
		massY = (mass / k) + spec.massYOrig
	else
		massY = spec.massYOrig
	end
	if massY ~= spec.massYLast then
		spec.massYLast = massY
		local x,y,z = getCenterOfMass(self.rootNode)
		--setCenterOfMass(self.rootNode, x, massY, z)
	end
	if spec.autoload then
		spec.autoLoadElaps = spec.autoLoadElaps + dt;
		if spec.autoLoadElaps >= spec.autoLoadSpeed then
			spec.autoLoadElaps = 0;
			if self.isServer then
				if entityExists(spec.lastWoodShapes) and spec.woodLoad[spec.lastWoodShapes] == nil then
					if spec.pile + 1 <= spec.numPiles then
						spec.pile = spec.pile + 1
						spec.lastWoodShapes = 0
						self:toggleLoad(spec.showLoadPos, spec.numPiles, spec.pile)
					else
						self:toggleState(false, spec.rightSide, spec.unload)
						return
					end
				end
				local triggerNum = (spec.rightSide and 3 or 0) + 1
				local x,y,z = getWorldTranslation(spec.woodTrigger[triggerNum])
				local nx, ny, nz = localDirectionToWorld(spec.woodTrigger[triggerNum], 1, 0, 0)
				local yx, yy, yz = localDirectionToWorld(spec.woodTrigger[triggerNum], 0, 1, 0)
				local shape, minY, maxY, minZ, maxZ = findSplitShape(x, y, z, nx, ny, nz, yx, yy, yz, spec.searchSizeY, spec.searchSizeZ)
				if shape == nil or not entityExists(shape) or getRigidBodyType(shape) ~= 2 or spec.badTree == shape then
					triggerNum = triggerNum + 1
					x,y,z = getWorldTranslation(spec.woodTrigger[triggerNum])
					nx, ny, nz = localDirectionToWorld(spec.woodTrigger[triggerNum], 1, 0, 0)
					yx, yy, yz = localDirectionToWorld(spec.woodTrigger[triggerNum], 0, 1, 0)
					shape, minY, maxY, minZ, maxZ = findSplitShape(x, y, z, nx, ny, nz, yx, yy, yz, spec.searchSizeY, spec.searchSizeZ)
				end	
				if shape == nil or not entityExists(shape) or getRigidBodyType(shape) ~= 2 or spec.badTree == shape then
					triggerNum = triggerNum + 1
					x,y,z = getWorldTranslation(spec.woodTrigger[triggerNum])
					nx, ny, nz = localDirectionToWorld(spec.woodTrigger[triggerNum], 1, 0, 0)
					yx, yy, yz = localDirectionToWorld(spec.woodTrigger[triggerNum], 0, 1, 0)
					shape, minY, maxY, minZ, maxZ = findSplitShape(x, y, z, nx, ny, nz, yx, yy, yz, spec.searchSizeY, spec.searchSizeZ)
				end	
				local fX,fY,fZ = getTranslation(spec.fillPoseNode)
				if fX < 0.4 then
					fX = fX + 0.4
				else
					fX = -0.6
						rotate(spec.fillPoseNode, 0,0, math.rad(180))
						spec.fXRot = not spec.fXRot
				end
				setTranslation(spec.fillPoseNode, fX, fY, fZ)
				if shape ~= nil and entityExists(shape) and getRigidBodyType(shape) == 2 then
					local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(shape)
					local maxSize = math.max(sizeX, math.max(sizeY, sizeZ)) --fcelsa
					local a, b, c = localToWorld(spec.woodTrigger[triggerNum], 0, (minY + maxY)*0.5, (minZ + maxZ)*0.5)
					local sx,sy,sz = getWorldTranslation(shape)
					local dist = MathUtil.vector3Length(a - sx, b - sy, c - sz)
					local nx, ny, nz = localDirectionToWorld(shape, 0, 1, 0)
					local lenBelow, lenAbove = getSplitShapePlaneExtents(shape, a, b, c, nx, ny, nz);
					dist = dist - lenBelow
					local diffX = (spec.maxLength - sizeX) * 0.5 * (spec.fXRot and -1 or 1)
					local wx,wy,wz = localToWorld(spec.fillPoseNode,0,-dist - (sizeX/2) - diffX,0)
					local dx,dy,dz = worldToLocal(getParent(shape),wx,wy,wz)
					local nx, ny, nz = localDirectionToWorld(spec.fillPoseNode, 1, 0, 0)
					local yx, yy, yz = localDirectionToWorld(spec.fillPoseNode, 0, 1, 0)
					local x, y, z = worldDirectionToLocal(getParent(shape), nx, ny, nz)
					local xdir, ydir, zdir = worldDirectionToLocal(getParent(shape), yx, yy, yz)
					if numAttachments == nil then numAttachments = 0; end;
					if maxSize <= spec.loadMaxLen and maxSize >= spec.loadMinLen and numAttachments <= spec.loadMaxAtt --[[and maxSize <= (spec.maxLength + 0.2)]]  then
						removeFromPhysics(shape)
						setTranslation(shape,dx,dy,dz)
						setDirection(shape, x, y, z, xdir, ydir, zdir)
						addToPhysics(shape)
						spec.lastWoodShapes = shape
					else
						spec.badTree = shape
					end;
				end
			end
		end
	elseif spec.unload then
		spec.autoLoadElaps = spec.autoLoadElaps + dt;
		if spec.autoLoadElaps >= spec.autoLoadSpeed then
			spec.autoLoadElaps = 0;
			if self.isServer then
				local side = spec.rightSide and 2 or 1;
				local x,y,z = getWorldTranslation(spec.fillPoseNode)
				local uX,uY,uZ = getTranslation(spec.unloadPoseNode[side])
				local findWood = false;
				for k, shape in pairs(spec.woodLoad) do
					if entityExists(shape) then
						spec.shape = shape
						local sx,sy,sz = getWorldTranslation(shape)
						local dist = MathUtil.vector2Length(x - sx, z - sz)
						uX = math.abs(uX)
						if uX < spec.unloadPoseNodeX + spec.unloadShiftMax then
							uX = uX + 0.6
						else
							uX = spec.unloadPoseNodeX
						end
						setTranslation(spec.unloadPoseNode[side], (spec.rightSide and -1 or 1) * uX, uY, uZ)
						if getChild(shape,'logCenter') == 0 then
							local x,y,z = getWorldTranslation(shape)
							local nx,ny,nz = localDirectionToWorld(shape, 0,1,0);
							local lenBelow, lenAbove = getSplitShapePlaneExtents(shape, x,y,z,nx,ny,nz);
							local sizeX, sizeY, sizeZ = getSplitShapeStats(shape)
							local logCenter = createTransformGroup("logCenter")
							link(shape, logCenter)
							setTranslation(logCenter, 0,-lenBelow+(sizeX/2),0)
							spec.logCenter = logCenter
						else
							local logCenter = getChild(shape,'logCenter')
							spec.logCenter = logCenter
						end
						if dist < 100 then
							local wx,wy,wz = localToWorld(spec.unloadPoseNode[side],0,-dist,0)
							local dx,dy,dz = worldToLocal(getParent(shape),wx,wy,wz)
							local nx, ny, nz = localDirectionToWorld(spec.unloadPoseNode[side], 1, 0, 0)
							local yx, yy, yz = localDirectionToWorld(spec.unloadPoseNode[side], 0, 1, 0)
							local x, y, z = worldDirectionToLocal(getParent(shape), nx, ny, nz)
							local xdir, ydir, zdir = worldDirectionToLocal(getParent(shape), yx, yy, yz)
							removeFromPhysics(shape)
							setTranslation(shape,dx,dy,dz)
							setDirection(shape, x, y, z, xdir, ydir, zdir)
							addToPhysics(shape)
							local x,y,z = getWorldTranslation(spec.logCenter)
							local a,b,c = getWorldTranslation(spec.unloadPoseNode[side])
							local dist2 = MathUtil.vector2Length(x - a, z - c)
							if dist2 > 0 then
								local wx,wy,wz = localToWorld(spec.unloadPoseNode[side],0,-dist+dist2,0)
								local dx,dy,dz = worldToLocal(getParent(shape),wx,wy,wz)
								setTranslation(shape,dx,dy,dz)
							end
							local x,y,z = getWorldTranslation(spec.logCenter)
							local a,b,c = getWorldTranslation(spec.unloadPoseNode[side])
							local dist3 = MathUtil.vector2Length(x - a, z - c)
							if dist3 > 0 then
								local wx,wy,wz = localToWorld(spec.unloadPoseNode[side],0,-dist+dist2-dist3,0)
								local dx,dy,dz = worldToLocal(getParent(shape),wx,wy,wz)
								setTranslation(shape,dx,dy,dz)
							end
							self:dynamicMountTriggerCallback(spec.dynamicMountTrigger, shape, false, true, false, shape)
							if self.spec_timberTrailerInfo ~= nil and self.spec_timberTrailerInfo.logTrigger ~= nil then
								self:logTriggerCallback(self.spec_timberTrailerInfo.logTrigger, shape, false, true, false, shape)
							end
							findWood = true;
							break;
						else
							spec.woodLoad[k] = nil
						end
					else
						spec.woodLoad[k] = nil
					end
				end
				if findWood == false then
					self:toggleState(spec.autoload, spec.rightSide, false)
					setTranslation(spec.unloadPoseNode[side], spec.unloadPoseNodeX, spec.unloadPoseNodeY, spec.unloadPoseNodeZ)
				end;
			end;
		end;
	end
	if self:getIsActiveForInput(true) then
		if spec.event_IDs ~= nil and g_dedicatedServerInfo == nil and self.isClient then
			for actionName,eventID in pairs(spec.event_IDs) do
				g_inputBinding:setActionEventActive(eventID, true)
				if actionName == InputAction.AUTOLOADWOOD_TOGGLE_AUTO then
					g_inputBinding:setActionEventText(eventID, spec.autoload and g_i18n:getText('AUTOLOADWOOD_STOP_AUTOLOAD') or g_i18n:getText('AUTOLOADWOOD_START_AUTOLOAD'))
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWoodForwarder.showHelp)
				elseif actionName == InputAction.AUTOLOADWOOD_TOGGLE_UNLOAD then
					g_inputBinding:setActionEventText(eventID, spec.unload and g_i18n:getText('AUTOLOADWOOD_STOP_UNLOAD') or g_i18n:getText('AUTOLOADWOOD_START_UNLOAD'))
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWoodForwarder.showHelp)
				elseif actionName == InputAction.AUTOLOADWOOD_TOGGLE_SIDE then
					g_inputBinding:setActionEventText(eventID, spec.rightSide and g_i18n:getText('AUTOLOADWOOD_SWITCH_LEFT') or g_i18n:getText('AUTOLOADWOOD_SWITCH_RIGHT'))
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWoodForwarder.showHelp)
				elseif actionName == InputAction.AUTOLOADWOOD_TOGGLE_LOAD_MARKER then
					g_inputBinding:setActionEventText(eventID, spec.showLoadPos and g_i18n:getText('AUTOLOADWOOD_HIDE_LOAD_MARKER') or g_i18n:getText('AUTOLOADWOOD_SHOW_LOAD_MARKER'))
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWoodForwarder.showHelp)
				elseif actionName == InputAction.AUTOLOADWOOD_TOGGLE_UNLOAD_MARKER then
					g_inputBinding:setActionEventText(eventID, spec.showUnloadPos and g_i18n:getText('AUTOLOADWOOD_HIDE_UNLOAD_MARKER') or g_i18n:getText('AUTOLOADWOOD_SHOW_UNLOAD_MARKER'))
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWoodForwarder.showHelp)
				elseif actionName == InputAction.AUTOLOADWOOD_TOGGLE_HELP then
					g_inputBinding:setActionEventText(eventID, AutoLoadWoodForwarder.showHelp and g_i18n:getText('AUTOLOADWOOD_HIDE_HELP') or g_i18n:getText('AUTOLOADWOOD_SHOW_HELP'))
					g_inputBinding:setActionEventTextVisibility(eventID, true)
				elseif actionName == InputAction.AUTOLOADWOOD_TOGGLE_PILES then
					g_inputBinding:setActionEventText(eventID, g_i18n:getText('input_AUTOLOADWOOD_TOGGLE_PILES')..' : '..spec.numPiles..' (max. '..spec.maxLength..' m)')
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWoodForwarder.showHelp)
				elseif actionName == InputAction.AUTOLOADWOOD_SWITCH_PILE then
					g_inputBinding:setActionEventText(eventID, g_i18n:getText('input_AUTOLOADWOOD_SWITCH_PILE')..' : '..spec.pile)
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWoodForwarder.showHelp)
				elseif actionName == InputAction.AUTOLOADWOOD_UNLOAD_MARKER_MOVE then
					g_inputBinding:setActionEventText(eventID,  g_i18n:getText('input_AUTOLOADWOOD_UNLOAD_MARKER_MOVE')..' : '..math.ceil(spec.unloadRot and spec.unloadPos or spec.unloadPos-1)..'/'..math.ceil(spec.unloadRot and spec.rootShiftRotXMax or 4)..' m')
					g_inputBinding:setActionEventTextVisibility(eventID, spec.showUnloadPos)
				elseif actionName == InputAction.AUTOLOADWOOD_UNLOAD_MARKER_SCALE then
					g_inputBinding:setActionEventText(eventID, g_i18n:getText('input_AUTOLOADWOOD_UNLOAD_MARKER_SCALE')..' : '..math.ceil(spec.unloadScale*4.8)..'/'..math.ceil(2*4.8)..' m')
					g_inputBinding:setActionEventTextVisibility(eventID, spec.showUnloadPos)
				elseif actionName == InputAction.AUTOLOADWOOD_UNLOAD_MARKER_ROTATE then
					g_inputBinding:setActionEventText(eventID, g_i18n:getText('input_AUTOLOADWOOD_UNLOAD_MARKER_ROTATE'))
					g_inputBinding:setActionEventTextVisibility(eventID, spec.showUnloadPos and AutoLoadWoodForwarder.showHelp)
				end
			end
			if self.spec_timberTrailerInfo == nil or not self.spec_timberTrailerInfo.modActive then
				if count > 0 then
					g_currentMission:addExtraPrintText(g_i18n:getText('AUTOLOADWOOD_COUNT').." "..count.." ("..string.format('%.1f', mass).." t)")
				else
					g_currentMission:addExtraPrintText(g_i18n:getText('AUTOLOADWOOD_NO_LOGS'))
				end
			end
		end
	end
end
function AutoLoadWoodForwarder:onDraw()
	local spec = self.spec_autoLoadWoodForwarder
	
	--[[local source = spec.unloadPoseRoot[1]
	if source ~= nil then
		local x,y,z = getWorldTranslation(source)
		local nx, ny, nz = localDirectionToWorld(source, 3, 0, 0)
		local yx, yy, yz = localDirectionToWorld(source, 0, 3, 0)
		local zx, zy, zz = localDirectionToWorld(source, 0, 0, 3)
		drawDebugLine(x,y,z, 1, 0, 0, x + nx, y + ny, z + nz, 1, 0, 0)
		drawDebugLine(x,y,z, 0, 1, 0, x + yx, y + yy, z + yz, 0, 1, 0)
		drawDebugLine(x,y,z, 0, 0, 1, x + zx, y + zy, z + zz, 0, 0, 1)
	end
	local source = spec.unloadPoseNode[1]
	if source ~= nil then
		local x,y,z = getWorldTranslation(source)
		local nx, ny, nz = localDirectionToWorld(source, 1, 0, 0)
		local yx, yy, yz = localDirectionToWorld(source, 0, 1, 0)
		local zx, zy, zz = localDirectionToWorld(source, 0, 0,1)
		drawDebugLine(x,y,z, 1, 0, 0, x + nx, y + ny, z + nz, 1, 0, 0)
		drawDebugLine(x,y,z, 0, 1, 0, x + yx, y + yy, z + yz, 0, 1, 0)
		drawDebugLine(x,y,z, 0, 0, 1, x + zx, y + zy, z + zz, 0, 0, 1)
	end
	local source = spec.fillPoseRoot
	if source ~= nil then
		local x,y,z = getWorldTranslation(source)
		local nx, ny, nz = localDirectionToWorld(source, 3, 0, 0)
		local yx, yy, yz = localDirectionToWorld(source, 0, 3, 0)
		local zx, zy, zz = localDirectionToWorld(source, 0, 0,3)
		drawDebugLine(x,y,z, 1, 0, 0, x + nx, y + ny, z + nz, 1, 0, 0)
		drawDebugLine(x,y,z, 0, 1, 0, x + yx, y + yy, z + yz, 0, 1, 0)
		drawDebugLine(x,y,z, 0, 0, 1, x + zx, y + zy, z + zz, 0, 0, 1)
	end
	local source = spec.fillPoseNode
	if source ~= nil then
		local x,y,z = getWorldTranslation(source)
		local nx, ny, nz = localDirectionToWorld(source, 1, 0, 0)
		local yx, yy, yz = localDirectionToWorld(source, 0, 1, 0)
		local zx, zy, zz = localDirectionToWorld(source, 0, 0,1)
		drawDebugLine(x,y,z, 1, 0, 0, x + nx, y + ny, z + nz, 1, 0, 0)
		drawDebugLine(x,y,z, 0, 1, 0, x + yx, y + yy, z + yz, 0, 1, 0)
		drawDebugLine(x,y,z, 0, 0, 1, x + zx, y + zy, z + zz, 0, 0, 1)
	end
	local source = self.rootNode
	if source ~= nil then
		local x,y,z = getWorldTranslation(source)
		local nx, ny, nz = localDirectionToWorld(source, 5, 0, 0)
		local yx, yy, yz = localDirectionToWorld(source, 0, 5, 0)
		local zx, zy, zz = localDirectionToWorld(source, 0, 0, 5)
		drawDebugLine(x,y,z, 1, 0, 0, x + nx, y + ny, z + nz, 1, 0, 0)
		drawDebugLine(x,y,z, 0, 1, 0, x + yx, y + yy, z + yz, 0, 1, 0)
		drawDebugLine(x,y,z, 0, 0, 1, x + zx, y + zy, z + zz, 0, 0, 1)
	end
	for i = 1, 6 do
		local source = spec.woodTrigger[i]
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, 1, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.searchSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.searchSizeZ)
			drawDebugLine(x,y,z, 1, 0, 0, x + nx, y + ny, z + nz, 1, 0, 0)
			drawDebugLine(x,y,z, 0, 1, 0, x + yx, y + yy, z + yz, 0, 1, 0)
			drawDebugLine(x,y,z, 0, 0, 1, x + zx, y + zy, z + zz, 0, 0, 1)
		end
	end]]
end
function AutoLoadWoodForwarder:toggleState(autoload, rightSide, unload, noEventSend)
	local spec = self.spec_autoLoadWoodForwarder
	
	AutoLoadWoodForwarderToggleEvent.sendEvent(self, autoload, rightSide, unload, noEventSend)
	
	if rightSide ~= spec.rightSide then
		unlink(spec.unloadMarker)
		rotate(spec.unloadMarker, 0,math.rad(180),0)
		local mX,mY,mZ = getTranslation(spec.unloadMarker)
		mX = mX * -1
		link(spec.unloadPoseRoot[rightSide and 2 or 1], spec.unloadMarker);
	end
	
	spec.lastWoodShapes = 0;
	spec.autoload = autoload;
	spec.rightSide = rightSide;
	spec.unload = unload
end
function AutoLoadWoodForwarder:toggleLoad(showLoadPos, numPiles, pile, noEventSend)
	local spec = self.spec_autoLoadWoodForwarder
	
	AutoLoadWoodForwarderToggleLoadEvent.sendEvent(self, showLoadPos, numPiles, pile, noEventSend)
	
	spec.showLoadPos = showLoadPos
	spec.numPiles = numPiles
	spec.pile = pile
	self:updateValues()
	self:updateLoad()
	self:updateUnload()
end
function AutoLoadWoodForwarder:toggleUnload(showUnloadPos, unloadPos, unloadScale, unloadRot, noEventSend)
	local spec = self.spec_autoLoadWoodForwarder
	
	AutoLoadWoodForwarderToggleUnloadEvent.sendEvent(self, showUnloadPos, unloadPos, unloadScale, unloadRot, noEventSend)
	
	if unloadPos ~= spec.unloadPos or unloadRot ~= spec.unloadRot then
		setTranslation(spec.unloadPoseNode[spec.rightSide and 2 or 1], spec.unloadPoseNodeX, spec.unloadPoseNodeY, spec.unloadPoseNodeZ)
	end
	
	spec.showUnloadPos = showUnloadPos
	spec.unloadPos = unloadPos
	spec.unloadScale = unloadScale
	spec.unloadRot = unloadRot
	self:updateValues()
	self:updateUnload()
end
function AutoLoadWoodForwarder:actionCallback(actionName, keyStatus, arg4, arg5, arg6)
	local spec = self.spec_autoLoadWoodForwarder
	if keyStatus > 0 then
		if actionName == 'AUTOLOADWOOD_TOGGLE_AUTO' then
			self:toggleState(not spec.autoload, spec.rightSide, false)
		elseif actionName == 'AUTOLOADWOOD_TOGGLE_UNLOAD' then
			self:toggleState(false, spec.rightSide, not spec.unload)
		elseif actionName == 'AUTOLOADWOOD_TOGGLE_SIDE' then
			self:toggleState(false, not spec.rightSide, false)
		elseif actionName == 'AUTOLOADWOOD_TOGGLE_PILES' then
			if spec.numPiles + 1 <= spec.maxPiles then
				spec.numPiles = spec.numPiles + 1
			else
				spec.numPiles = 1
			end
			self:toggleLoad(spec.showLoadPos, spec.numPiles, 1)
			self:toggleState(false, spec.rightSide, false)
			if spec.unloadRot then
				spec.unloadPos = spec.rootShiftRotX
			end
			self:toggleUnload(spec.showUnloadPos, spec.unloadPos, spec.unloadScale, spec.unloadRot)
		elseif actionName == 'AUTOLOADWOOD_SWITCH_PILE' then
			if spec.pile + 1 <= spec.numPiles then
				spec.pile = spec.pile + 1
			else
				spec.pile = 1
			end
			self:toggleLoad(spec.showLoadPos, spec.numPiles, spec.pile)
		elseif actionName == 'AUTOLOADWOOD_TOGGLE_LOAD_MARKER' then
			self:toggleLoad(not spec.showLoadPos, spec.numPiles, spec.pile)
		elseif actionName == 'AUTOLOADWOOD_TOGGLE_UNLOAD_MARKER' then
			self:toggleUnload(not spec.showUnloadPos, spec.unloadPos, spec.unloadScale, spec.unloadRot)
		elseif actionName == 'AUTOLOADWOOD_UNLOAD_MARKER_MOVE' then
			local rX,rY,rZ = getTranslation(spec.unloadPoseRoot[1])
			if spec.unloadRot == true then
				rX = math.ceil(math.abs(rX))
				if rX < spec.rootShiftRotXMax then
					rX = rX + 1
				else
					rX = spec.rootShiftRotX
				end
			else
				rX = math.ceil(math.abs(rX))
				if rX < 5 then
					rX = rX + 1
				else
					rX = spec.unloadPoseRootX
				end
			end
			local unloadPos = rX
			self:toggleUnload(spec.showUnloadPos, unloadPos, spec.unloadScale, spec.unloadRot)
		elseif actionName == 'AUTOLOADWOOD_UNLOAD_MARKER_SCALE' then
			local msX,msY,msZ = getScale(spec.unloadMarker)
			if msZ < 2 then
				msZ = msZ + 0.2
			else
				msZ = 1
			end
			spec.unloadScale = msZ
			self:toggleUnload(spec.showUnloadPos, spec.unloadPos, spec.unloadScale, spec.unloadRot)
		elseif actionName == 'AUTOLOADWOOD_UNLOAD_MARKER_ROTATE' then
			if spec.unloadRot == false then
				spec.unloadPos = spec.rootShiftRotX
			else
				spec.unloadPos = spec.unloadPoseRootX
			end
			self:toggleUnload(spec.showUnloadPos, spec.unloadPos, spec.unloadScale, not spec.unloadRot)
		elseif actionName == 'AUTOLOADWOOD_TOGGLE_HELP' then
			AutoLoadWoodForwarder.showHelp = not AutoLoadWoodForwarder.showHelp
			self:saveToXml();
		end
	end
end
function AutoLoadWoodForwarder:updateValues()
	local spec = self.spec_autoLoadWoodForwarder
	spec.unloadShiftMax = spec.unloadScale * 4
	if spec.numPiles == 1 then
		spec.maxLength = spec.bunks[1]
		spec.startPosZ = -spec.maxLength/2
		spec.rootShiftRotX = spec.maxLength / 2 + 2
		spec.rootShiftRotXMax = spec.rootShiftRotX + 3
	elseif spec.numPiles == 2 then
		spec.maxLength = spec.bunks[2]
		spec.startPosZ = -spec.maxLength/2
		spec.rootShiftRotX = spec.maxLength / 2 + 2
		spec.rootShiftRotXMax = spec.rootShiftRotX + 3
		if spec.pile == 1 then
			spec.startPosZ = spec.startPosZ;
		elseif spec.pile == 2 then
			spec.startPosZ = spec.startPosZ - spec.maxLength - spec.space
		end
	elseif spec.numPiles == 3 then
		spec.maxLength = spec.bunks[3]
		spec.startPosZ = -spec.maxLength/2
		spec.rootShiftRotX = spec.maxLength / 2 + 2
		spec.rootShiftRotXMax = spec.rootShiftRotX + 3
		if spec.pile == 1 then
			spec.startPosZ = spec.startPosZ;
		elseif spec.pile == 2 then
			spec.startPosZ = spec.startPosZ - spec.maxLength - spec.space
		elseif spec.pile == 3 then
			spec.startPosZ = spec.startPosZ - (2 * (spec.maxLength + spec.space))
		end
	end
end
function AutoLoadWoodForwarder:updateLoad()
	local spec = self.spec_autoLoadWoodForwarder
	
	setTranslation(spec.fillPoseRoot, spec.fillPoseNodeX, spec.fillPoseNodeY, spec.startPosZ)
	setScale(spec.loadMarker, spec.maxLength,1,2.5)
	setVisibility(spec.loadMarker, spec.showLoadPos);
	setScale(spec.unloadMarker, spec.maxLength,1,spec.unloadScale)
end
function AutoLoadWoodForwarder:updateUnload()
	local spec = self.spec_autoLoadWoodForwarder
	
	local j = 1
	for i = 1, 2 do
		setRotation(spec.unloadPoseRoot[i], 0,(spec.unloadRot and math.rad(90) or math.rad(0)) * j,0)
		setTranslation(spec.unloadPoseRoot[i], spec.unloadPos * j, 0, (spec.unloadRot and 2.5 or spec.unloadPoseRootZ))
		j = j * -1
	end
	setScale(spec.unloadMarker, spec.maxLength,1,spec.unloadScale)
	setVisibility(spec.unloadMarker, spec.showUnloadPos);
end
function AutoLoadWoodForwarder:dynamicMountTriggerCallback(superFunc,triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
	local spec = self.spec_autoLoadWoodForwarder
	local splitType = g_splitTypeManager:getSplitTypeByIndex(getSplitType(otherActorId))
	if splitType ~= nil then
		if onEnter then
			--if not spec.unload then
				if spec.woodLoad[otherActorId] == nil then
					spec.woodLoad[otherActorId] = otherActorId
				end
			--end
		elseif onLeave then
			if not spec.autoload then
				spec.woodLoad[otherActorId] = nil
			end
		end
	end
	--superFunc(spec, triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
end
function AutoLoadWoodForwarder:getIsTurnedOn()
	local spec = self.spec_autoLoadWoodForwarder
    return spec.autoload or spec.unload
end
function AutoLoadWoodForwarder:onReadStream(streamId, connection)
	local spec = self.spec_autoLoadWoodForwarder
	
	local rightSide = streamReadBool(streamId);
	local showLoadPos = streamReadBool(streamId);
	local showUnloadPos = streamReadBool(streamId);
	local numPiles = streamReadInt8(streamId);
    local pile = streamReadInt8(streamId);
    local unloadPos = streamReadFloat32(streamId);
    local unloadScale = streamReadFloat32(streamId);
    local unloadRot = streamReadBool(streamId);
	
	if rightSide ~= nil and showLoadPos ~= nil and showUnloadPos ~= nil and numPiles ~= nil and pile ~= nil and unloadPos ~= nil and unloadScale ~= nil and unloadRot ~= nil then
		self:toggleState(spec.autoload, rightSide, spec.unload, true)
		self:toggleLoad(showLoadPos, numPiles, pile, true)
		self:toggleUnload(showUnloadPos, unloadPos, unloadScale, unloadRot, true)
	end
end;

function AutoLoadWoodForwarder:onWriteStream(streamId, connection)
	local spec = self.spec_autoLoadWoodForwarder
	
	streamWriteBool(streamId, spec.rightSide);
	streamWriteBool(streamId, spec.showLoadPos);
	streamWriteBool(streamId, spec.showUnloadPos);
	streamWriteInt8(streamId, spec.numPiles);
	streamWriteInt8(streamId, spec.pile);
	streamWriteFloat32(streamId, spec.unloadPos);
	streamWriteFloat32(streamId, spec.unloadScale);
	streamWriteBool(streamId, spec.unloadRot);
end;

--- Events ---

AutoLoadWoodForwarderToggleEvent = {};
AutoLoadWoodForwarderToggleEvent_mt = Class(AutoLoadWoodForwarderToggleEvent, Event);

InitEventClass(AutoLoadWoodForwarderToggleEvent, "AutoLoadWoodForwarderToggleEvent");

function AutoLoadWoodForwarderToggleEvent.emptyNew()
    local self = Event.new(AutoLoadWoodForwarderToggleEvent_mt);
    return self;
end;
    
function AutoLoadWoodForwarderToggleEvent.new(object, autoload, rightSide, unload)
	local self = AutoLoadWoodForwarderToggleEvent.emptyNew()
	self.object = object;
	self.autoload = autoload;
	self.rightSide = rightSide;
	self.unload = unload;
	return self;
end;

function AutoLoadWoodForwarderToggleEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId);
	self.autoload = streamReadBool(streamId);
	self.rightSide = streamReadBool(streamId);
	self.unload = streamReadBool(streamId);
	self:run(connection);
end;

function AutoLoadWoodForwarderToggleEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object);
	streamWriteBool(streamId, self.autoload);
	streamWriteBool(streamId, self.rightSide);
	streamWriteBool(streamId, self.unload);
end;

function AutoLoadWoodForwarderToggleEvent:run(connection)
	if not connection:getIsServer() then
		g_server:broadcastEvent(self, false, connection, self.object);
	end;
	if self.object ~= nil then
		self.object:toggleState(self.autoload, self.rightSide, self.unload, true);
	end;
end;

function AutoLoadWoodForwarderToggleEvent.sendEvent(vehicle, autoload, rightSide, unload, noEventSend)
	if autoload ~= vehicle.autoload or rightSide ~= vehicle.rightSide or unload ~= vehicle.unload then
		if noEventSend == nil or noEventSend == false then
			if g_server ~= nil then
				g_server:broadcastEvent(AutoLoadWoodForwarderToggleEvent.new(vehicle, autoload, rightSide, unload), nil, nil, vehicle);
			else
				g_client:getServerConnection():sendEvent(AutoLoadWoodForwarderToggleEvent.new(vehicle, autoload, rightSide, unload));
			end;
		end;
	end;
end;

AutoLoadWoodForwarderToggleLoadEvent = {};
AutoLoadWoodForwarderToggleLoadEvent_mt = Class(AutoLoadWoodForwarderToggleLoadEvent, Event);

InitEventClass(AutoLoadWoodForwarderToggleLoadEvent, "AutoLoadWoodForwarderToggleLoadEvent");

function AutoLoadWoodForwarderToggleLoadEvent.emptyNew()
    local self = Event.new(AutoLoadWoodForwarderToggleLoadEvent_mt);
    return self;
end;
    
function AutoLoadWoodForwarderToggleLoadEvent.new(object, showLoadPos, numPiles, pile)
	local self = AutoLoadWoodForwarderToggleLoadEvent.emptyNew()
	self.object = object;
	self.showLoadPos = showLoadPos;
	self.numPiles = numPiles;
	self.pile = pile;
	return self;
end;

function AutoLoadWoodForwarderToggleLoadEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId);
	self.showLoadPos = streamReadBool(streamId);
	self.numPiles = streamReadInt8(streamId);
	self.pile = streamReadInt8(streamId);
	self:run(connection);
end;

function AutoLoadWoodForwarderToggleLoadEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object);
	streamWriteBool(streamId, self.showLoadPos);
	streamWriteInt8(streamId, self.numPiles);
	streamWriteInt8(streamId, self.pile);
end;

function AutoLoadWoodForwarderToggleLoadEvent:run(connection)
	if not connection:getIsServer() then
		g_server:broadcastEvent(self, false, connection, self.object);
	end;
	if self.object ~= nil then
		self.object:toggleLoad(self.showLoadPos, self.numPiles, self.pile, true);
	end;
end;

function AutoLoadWoodForwarderToggleLoadEvent.sendEvent(vehicle, showLoadPos, numPiles, pile, noEventSend)
	if showLoadPos ~= vehicle.showLoadPos or numPiles ~= vehicle.numPiles or pile ~= vehicle.pile then
		if noEventSend == nil or noEventSend == false then
			if g_server ~= nil then
				g_server:broadcastEvent(AutoLoadWoodForwarderToggleLoadEvent.new(vehicle, showLoadPos, numPiles, pile), nil, nil, vehicle);
			else
				g_client:getServerConnection():sendEvent(AutoLoadWoodForwarderToggleLoadEvent.new(vehicle, showLoadPos, numPiles, pile));
			end;
		end;
	end;
end;

AutoLoadWoodForwarderToggleUnloadEvent = {};
AutoLoadWoodForwarderToggleUnloadEvent_mt = Class(AutoLoadWoodForwarderToggleUnloadEvent, Event);

InitEventClass(AutoLoadWoodForwarderToggleUnloadEvent, "AutoLoadWoodForwarderToggleUnloadEvent");

function AutoLoadWoodForwarderToggleUnloadEvent.emptyNew()
    local self = Event.new(AutoLoadWoodForwarderToggleUnloadEvent_mt);
    return self;
end;
    
function AutoLoadWoodForwarderToggleUnloadEvent.new(object, showUnloadPos, unloadPos, unloadScale, unloadRot)
	local self = AutoLoadWoodForwarderToggleUnloadEvent.emptyNew()
	self.object = object;
	self.showUnloadPos = showUnloadPos;
	self.unloadPos = unloadPos;
	self.unloadScale = unloadScale;
	self.unloadRot = unloadRot;
	return self;
end;

function AutoLoadWoodForwarderToggleUnloadEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId);
	self.showUnloadPos = streamReadBool(streamId);
	self.unloadPos = streamReadFloat32(streamId);
	self.unloadScale = streamReadFloat32(streamId);
	self.unloadRot = streamReadBool(streamId);
	self:run(connection);
end;

function AutoLoadWoodForwarderToggleUnloadEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object);
	streamWriteBool(streamId, self.showUnloadPos);
	streamWriteFloat32(streamId, self.unloadPos);
	streamWriteFloat32(streamId, self.unloadScale);
	streamWriteBool(streamId, self.unloadRot);
end;

function AutoLoadWoodForwarderToggleUnloadEvent:run(connection)
	if not connection:getIsServer() then
		g_server:broadcastEvent(self, false, connection, self.object);
	end;
	if self.object ~= nil then
		self.object:toggleUnload(self.showUnloadPos, self.unloadPos, self.unloadScale, self.unloadRot, true);
	end;
end;

function AutoLoadWoodForwarderToggleUnloadEvent.sendEvent(vehicle, showUnloadPos, unloadPos, unloadScale, unloadRot, noEventSend)
	if showUnloadPos ~= vehicle.showUnloadPos or unloadPos ~= vehicle.unloadPos or unloadScale ~= vehicle.unloadScale or unloadRot ~= vehicle.unloadRot then
		if noEventSend == nil or noEventSend == false then
			if g_server ~= nil then
				g_server:broadcastEvent(AutoLoadWoodForwarderToggleUnloadEvent.new(vehicle, showUnloadPos, unloadPos, unloadScale, unloadRot), nil, nil, vehicle);
			else
				g_client:getServerConnection():sendEvent(AutoLoadWoodForwarderToggleUnloadEvent.new(vehicle, showUnloadPos, unloadPos, unloadScale, unloadRot));
			end;
		end;
	end;
end