--
-- FS19 - FirewoodProcessor
-- @author:    	kenny456 (kenny456@seznam.cz)
-- @history:	v1.0.0.0 - 2022-01-28 - first release
-- converted by Sablerock
-- date: 2026/06/04
-- v1.0.1.0 - 2026-06-04 - FS25 API conversion

--
FirewoodProcessor = {}
FirewoodProcessor.confDir = getUserProfileAppPath().. "modsSettings/FirewoodProcessor/"
FirewoodProcessor.modDirectory = g_currentModDirectory
local modName = g_currentModName
FirewoodProcessor.showHelp = true
FirewoodProcessor.debug = false

local function log(fmt, ...)
    if FirewoodProcessor.debug then
        print(("[FS25_FirewoodProcessor] " .. fmt):format(...))
    end
end


local function dumpTable(name, t, maxDepth, depth, visited)
    depth    = depth or 0
    maxDepth = maxDepth or 3
    visited  = visited or {}

    -- Not a table
    if type(t) ~= "table" then
        log("%s = %s (type=%s, not a table)", name, tostring(t), type(t))
        return
    end

    -- Already seen
    if visited[t] then
        log("%s = %s (already visited)", name, tostring(t))
        return
    end
    visited[t] = true

    -- Header
    log("==== table: %s", tostring(name))
    if depth >= maxDepth then
        log("%s (maxDepth %d reached, not descending further)", name, maxDepth)
        return
    end

    local hasEntries = false

    for k, v in pairs(t) do
        hasEntries = true
        local keyName = string.format("%s[%s]", name, tostring(k))
        if type(v) == "table" then
			log(">>>> sub-table following")
		
            dumpTable(keyName, v, maxDepth, depth + 1, visited)
        else
            log("%s = %s", keyName, tostring(v))
        end
    end

    if not hasEntries then
        log("%s is an empty table (no Lua-visible keys)", name)
    end
end


function FirewoodProcessor.prerequisitesPresent(specializations)
	return true
end
function FirewoodProcessor.initSpecialization()
    local schema = Vehicle.xmlSchema
    schema:setXMLSpecializationType("FirewoodProcessor")
    AnimationManager.registerAnimationNodesXMLPaths(schema, "vehicle.firewoodProcessor.feeder.animationNodesFeeder")
    AnimationManager.registerAnimationNodesXMLPaths(schema, "vehicle.firewoodProcessor.animationNodesSaw")
    AnimationManager.registerAnimationNodesXMLPaths(schema, "vehicle.firewoodProcessor.belts.animationNodes")
    EffectManager.registerEffectXMLPaths(schema, "vehicle.firewoodProcessor.cutEffects")

    schema:setXMLSpecializationType()
end

function FirewoodProcessor.registerOverwrittenFunctions(vehicleType)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsFoldAllowed",     FirewoodProcessor.getIsFoldAllowed)
end
function FirewoodProcessor.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "toggleFeedingLog", 			FirewoodProcessor.toggleFeedingLog)
	SpecializationUtil.registerFunction(vehicleType, "cutTree", 					FirewoodProcessor.cutTree)
	SpecializationUtil.registerFunction(vehicleType, "splitShapesTrigger", 			FirewoodProcessor.splitShapesTrigger)
	SpecializationUtil.registerFunction(vehicleType, "splitShapeCallback", 			FirewoodProcessor.splitShapeCallback)
	SpecializationUtil.registerFunction(vehicleType, "splitShapeCallback2", 		FirewoodProcessor.splitShapeCallback2)
	SpecializationUtil.registerFunction(vehicleType, "splitShapeCallback3", 		FirewoodProcessor.splitShapeCallback3)
	SpecializationUtil.registerFunction(vehicleType, "splitShapeCallback4", 		FirewoodProcessor.splitShapeCallback4)
	SpecializationUtil.registerFunction(vehicleType, "splitShapeCallback5", 		FirewoodProcessor.splitShapeCallback5)
	SpecializationUtil.registerFunction(vehicleType, "toggleSplitCount", 			FirewoodProcessor.toggleSplitCount)
	SpecializationUtil.registerFunction(vehicleType, "toggleUnload", 				FirewoodProcessor.toggleUnload)
	SpecializationUtil.registerFunction(vehicleType, "toggleLoadLog", 				FirewoodProcessor.toggleLoadLog)
end
function FirewoodProcessor:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
	local spec = self.spec_firewoodProcessor
	if not spec.modInitialized or not spec.modAllowed then
		return
	end
	
	if spec.event_IDs == nil then
		spec.event_IDs = {}
	else
		self:clearActionEventsTable(spec.event_IDs)
	end
	if self:getIsActiveForInput() or (self.getIscontrolled ~= nil and self:getIsControlled()) then
		if g_dedicatedServerInfo ~= nil then
			return
		end
		local actions = { InputAction.FIREWOODPROCESSOR_UNLOAD, InputAction.FIREWOODPROCESSOR_LOAD_LOG, InputAction.FIREWOODPROCESSOR_FEED_LOG_BACK, InputAction.FIREWOODPROCESSOR_FEED_LOG, InputAction.FIREWOODPROCESSOR_SPLIT_LOG, InputAction.FIREWOODPROCESSOR_CUT_LOG, InputAction.FIREWOODPROCESSOR_TOGGLE_SPLIT_COUNT }
		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, FirewoodProcessor.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)
				else
					g_inputBinding:setActionEventTextPriority(eventID, GS_PRIO_VERY_HIGH)
				end
				g_inputBinding:setActionEventTextVisibility(eventID, FirewoodProcessor.showHelp)
			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: FirewoodProcessor 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: FirewoodProcessor got a colliding input action: %s', actionName))
				end
			end
		end
	end
end
function FirewoodProcessor.registerEventListeners(vehicleType)
	for _,n in pairs( { "onLoad", "onPostLoad", "onDelete", "saveToXMLFile", "onUpdate", "onUpdateTick", "onDraw", "onReadStream", "onWriteStream", "onRegisterActionEvents", "toggleFeedingLog", "cutTree", "toggleSplitCount", "toggleUnload", "toggleLoadLog" } ) do
		SpecializationUtil.registerEventListener(vehicleType, n, FirewoodProcessor)
	end
end
function FirewoodProcessor:onLoad(savegame)
	self.spec_firewoodProcessor = {}
	local spec = self.spec_firewoodProcessor
	
	local xmlFile = self.xmlFile.handle
	local baseKey = "vehicle.firewoodProcessor"
	spec.moveColNodes = {}
    local i = 0
    while true do
        local key = string.format("%s.moveCollisions.collision(%d)", baseKey, i)
        if not hasXMLProperty(xmlFile, key) then
            break
        end
        local node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#node"), self.i3dMappings)
        if node ~= nil then
            table.insert(spec.moveColNodes, node)
        end
        i = i + 1
    end
	local moveColDisableCollisionPairs = Utils.getNoNil(getXMLBool(xmlFile, baseKey.."#moveColDisableCollisionPairs"), true)
    if moveColDisableCollisionPairs then
        for _, component in pairs(self.components) do
            for _, node in pairs(spec.moveColNodes) do
                --setPairCollision(component.node, node, false)
            end
        end
    end
	spec.moveColNodesUnload = {}
    i = 0
    while true do
        local key = string.format("%s.belts.moveCollisions.collision(%d)", baseKey, i)
        if not hasXMLProperty(xmlFile, key) then
            break
        end
        local node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#node"), self.i3dMappings)
        if node ~= nil then
            table.insert(spec.moveColNodesUnload, node)
        end
        i = i + 1
    end
	local moveColDisableCollisionPairs = Utils.getNoNil(getXMLBool(xmlFile, baseKey.."#moveColDisableCollisionPairs"), true)
    if moveColDisableCollisionPairs then
        for _, component in pairs(self.components) do
            for _, node in pairs(spec.moveColNodesUnload) do
                --setPairCollision(component.node, node, false)
            end
        end
    end
	spec.moveVelocityZ = Utils.getNoNil(getXMLFloat(xmlFile, baseKey.."#moveVelocityZ"), 0.8) -- m/s
	spec.moveVelocityUnload = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".belts.moveCollisions#moveVelocityZ"), 0.8) -- m/s
	spec.cutLengthMovingTool = Utils.getNoNil(getXMLInt(xmlFile, baseKey.."#cutLengthMovingTool"), 2)
	spec.splitHeightMovingTool = Utils.getNoNil(getXMLInt(xmlFile, baseKey.."#splitHeightMovingTool"), 3)
	spec.loadAnimation = {}
    spec.loadAnimation.name = getXMLString(xmlFile, baseKey..".loader.loadAnimation#name")
    spec.loadAnimation.speedScale = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".loader.loadAnimation#speedScale"), 1)
	spec.searchLogNode = {}
	spec.searchLogNode[1] = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".loader#searchLogNode1"), self.i3dMappings)
	spec.searchLogNode[2] = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".loader#searchLogNode2"), self.i3dMappings)
	spec.searchLogNode[3] = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".loader#searchLogNode3"), self.i3dMappings)
	spec.logToLoadNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".loader#logToLoadNode"), self.i3dMappings)
	spec.loadPosition = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".loader#loadPosition"), self.i3dMappings)
	spec.searchSizeY = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".loader#searchSizeY"), 5)
    spec.searchSizeZ = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".loader#searchSizeZ"), 10)
    spec.maxLogSize = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".loader#maxLogSize"), 6)
    spec.minLogSize = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".loader#minLogSize"), 2)
    spec.minLogDiameter = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".loader#minLogDiameter"), 0.3)
    spec.loadMaxAtt = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".loader#loadMaxAtt"), 2)
	spec.feedAnimation = {}
    spec.feedAnimation.name = getXMLString(xmlFile, baseKey..".feeder.feedAnimation#name")
    spec.feedAnimation.speedScale = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".feeder.feedAnimation#speedScale"), 1)
	spec.cutAnimation = {}
    spec.cutAnimation.name = getXMLString(xmlFile, baseKey..".cutAnimation#name")
    spec.cutAnimation.speedScale = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".cutAnimation#speedScale"), 1)
	spec.cutAnimation2 = {}
    spec.cutAnimation2.name = getXMLString(xmlFile, baseKey..".cutAnimation2#name")
    spec.cutAnimation2.speedScale = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".cutAnimation2#speedScale"), 1)
    spec.cutAnimation.cutTime = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".cutAnimation#cutTime"), 1)
	spec.cutNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".cutNode#node"), self.i3dMappings)
    spec.cutSizeY = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".cutNode#sizeY"), 1.3)
    spec.cutSizeZ = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".cutNode#sizeZ"), 1.8)
	spec.curSplitShape = nil
	spec.lastDiameter = 0
	spec.cutDone = false
	spec.splitterSplitShape = nil
	spec.hasSplit = false
	spec.splitetShapes = {}
	spec.splitetShapes2 = {}
	spec.splitetShapes3 = {}
	spec.splitetShapes4 = {}
	spec.splitetShapes5 = {}
	spec.cutLength = 0
	spec.splitCount = 6
	spec.shapesInTrigger = {}
	spec.unload = false
	spec.logToLoad = nil
	spec.logFound = nil
	spec.splitDiameter = 0
	spec.feederDiameter = 0
	spec.feederHeight = 0
	spec.feederShape = nil

	if self.isClient then
        spec.animationNodesFeeder = g_animationManager:loadAnimations(self.xmlFile, baseKey..".feeder.animationNodesFeeder", self.components, self, self.i3dMappings)
        -- dumpTable("spec.animationNodesFeeder",spec.animationNodesFeeder, 3)

		-- NEW (FS25)
		spec.animationNodesFeeder[1].scrollSpeedOrig = spec.animationNodesFeeder[1].scrollSpeed
		spec.animationNodesFeeder[2].rotSpeedOrig = spec.animationNodesFeeder[2].rotSpeed
		spec.animationNodesFeeder[3].rotSpeedOrig = spec.animationNodesFeeder[3].rotSpeed
		spec.animationNodesFeeder[4].rotSpeedOrig = spec.animationNodesFeeder[4].rotSpeed
		spec.animationNodesFeeder[5].rotSpeedOrig = spec.animationNodesFeeder[5].rotSpeed
		
		-- spec.animationNodesFeeder[1].speedFuncTarget.scrollSpeedOrig = spec.animationNodesFeeder[1].speedFuncTarget.scrollSpeed
		-- spec.animationNodesFeeder[2].speedFuncTarget.rotSpeedOrig = spec.animationNodesFeeder[2].speedFuncTarget.rotSpeed
		-- spec.animationNodesFeeder[3].speedFuncTarget.rotSpeedOrig = spec.animationNodesFeeder[3].speedFuncTarget.rotSpeed
		-- spec.animationNodesFeeder[4].speedFuncTarget.rotSpeedOrig = spec.animationNodesFeeder[4].speedFuncTarget.rotSpeed
		-- spec.animationNodesFeeder[5].speedFuncTarget.rotSpeedOrig = spec.animationNodesFeeder[5].speedFuncTarget.rotSpeed
        spec.animationNodesSaw = g_animationManager:loadAnimations(self.xmlFile, baseKey..".animationNodesSaw", self.components, self, self.i3dMappings)
		spec.samples = {}
		spec.samples.cut    = g_soundManager:loadSampleFromXML(xmlFile, baseKey..".sounds", "cut", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
		spec.samples.start = g_soundManager:loadSampleFromXML(xmlFile, baseKey..".sounds", "start", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
		spec.samples.stop  = g_soundManager:loadSampleFromXML(xmlFile, baseKey..".sounds", "stop", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
		spec.samples.work  = g_soundManager:loadSampleFromXML(xmlFile, baseKey..".sounds", "work", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
		spec.samples.feed  = g_soundManager:loadSampleFromXML(xmlFile, baseKey..".sounds", "feed", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
		spec.samples.belt  = g_soundManager:loadSampleFromXML(xmlFile, baseKey..".sounds", "belt", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
		spec.samples.split  = g_soundManager:loadSampleFromXML(xmlFile, baseKey..".sounds", "split", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
		spec.cutEffects = g_effectManager:loadEffect(self.xmlFile, baseKey..".cutEffects", self.components, self, self.i3dMappings)
    end
	spec.splitAnimation = {}
    spec.splitAnimation.name = getXMLString(xmlFile, baseKey..".splitter.splitAnimation#name")
    spec.splitAnimation.speedScale = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".splitter.splitAnimation#speedScale"), 1)
	spec.splitTrigger = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#splitTrigger"), self.i3dMappings)
	
-- I3d needs TREE trigger in preset	
	addTrigger(spec.splitTrigger, "splitShapesTrigger", self)
	
	spec.splitNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#splitNode"), self.i3dMappings)
	spec.splitNode2 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#splitNode2"), self.i3dMappings)
	spec.splitNode3 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#splitNode3"), self.i3dMappings)
	spec.splitNode4 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#splitNode4"), self.i3dMappings)
	spec.splitNode5 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#splitNode5"), self.i3dMappings)
	spec.searchNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#searchNode"), self.i3dMappings)
	spec.searchNode2 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#searchNode2"), self.i3dMappings)
	spec.searchNode3 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#searchNode3"), self.i3dMappings)
	spec.searchNode4 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#searchNode4"), self.i3dMappings)
	spec.searchNode5 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#searchNode5"), self.i3dMappings)
	spec.splitterAdvance = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#splitterAdvance"), self.i3dMappings)
	spec.splitterAdvance2 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".splitter#splitterAdvance2"), self.i3dMappings)
	spec.feederSearchNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey..".feeder#searchNode"), self.i3dMappings)
	spec.splitSizeY = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".splitter#sizeY"), 1.5)
    spec.splitSizeZ = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".splitter#sizeZ"), 1.5)
	spec.splitSearchSizeY = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".splitter#splitSearchSizeY"), 1.3)
	spec.splitSearchSizeY2 = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".splitter#splitSearchSizeY2"), 1)
    spec.splitSearchSizeZ = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".splitter#splitSearchSizeZ"), 1.8)
    spec.splitSearchSizeZ2 = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".splitter#splitSearchSizeZ2"), 1)
	spec.animationNodesBelts = g_animationManager:loadAnimations(self.xmlFile, baseKey..".belts.animationNodes", self.components, self, self.i3dMappings)
	spec.beltAnimation = {}
    spec.beltAnimation.name = getXMLString(xmlFile, baseKey..".belts.beltAnimation#name")
    spec.beltAnimation.speedScale = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".belts.beltAnimation#speedScale"), 1)
	spec.beltAnimation2 = {}
    spec.beltAnimation2.name = getXMLString(xmlFile, baseKey..".belts.beltAnimation2#name")
    spec.beltAnimation2.speedScale = Utils.getNoNil(getXMLFloat(xmlFile, baseKey..".belts.beltAnimation2#speedScale"), 1)
	spec.pipeExtra1 = I3DUtil.indexToObject(self.components, "pipeExtra1", self.i3dMappings)
	spec.pipeExtra2 = I3DUtil.indexToObject(self.components, "pipeExtra2", self.i3dMappings)
	spec.feedDir = 0
	if spec.cutNode ~= nil then
		spec.cutNode2 = createTransformGroup("cutNode2")
		link(spec.cutNode, spec.cutNode2)
		setTranslation(spec.cutNode2, 0.1, 0, 0)
	end
	spec.infoText = ''
	spec.isFeeding = false
	spec.unfoldWarning = string.format(g_i18n:getText(Utils.getNoNil(getXMLString(xmlFile, baseKey.."#unfoldWarning"), "warning_firstUnfoldTheTool"), self.customEnvironment), self.typeDesc)
	spec.debug = false
end
function FirewoodProcessor:onPostLoad(savegame)
	local spec = self.spec_firewoodProcessor

	spec.modInitialized = true
	spec.modAllowed = true
	if not spec.modInitialized then
		return
	else
	end
	spec.modActive = true
	if savegame ~= nil then
		local xmlFile = savegame.xmlFile.handle
		local key = savegame.key.."."..modName..".FirewoodProcessor"
		spec.splitCount = Utils.getNoNil(getXMLInt(xmlFile, key.."#splitCount"), spec.splitCount)
	end
	self:toggleSplitCount(spec.splitCount, true)
end
function FirewoodProcessor:onDelete()
	local spec = self.spec_firewoodProcessor
	if not spec.modInitialized or not spec.modAllowed then
		return
	end
	if self.isClient then
        g_effectManager:deleteEffects(spec.cutEffects)
        g_soundManager:deleteSamples(spec.samples)
		g_animationManager:deleteAnimations(spec.animationNodesFeeder)
		g_animationManager:deleteAnimations(spec.animationNodesSaw)
		g_animationManager:deleteAnimations(spec.animationNodesBelts)
    end
	if spec.splitTrigger ~= nil then
        removeTrigger(spec.splitTrigger)
    end
end
function FirewoodProcessor:saveToXMLFile(xmlFile, key)
	local spec = self.spec_firewoodProcessor
	if not spec.modInitialized or not spec.modAllowed then
		return
	end
	
	setXMLInt(xmlFile.handle, key.."#splitCount", spec.splitCount)
end
function FirewoodProcessor:splitShapeCallback(shape, isBelow, isAbove, minY, maxY, minZ, maxZ)
	local spec = self.spec_firewoodProcessor
	
	spec.hasSplit = true
	spec.splitetShapes[shape] = shape
end
function FirewoodProcessor:splitShapeCallback2(shape, isBelow, isAbove, minY, maxY, minZ, maxZ)
	local spec = self.spec_firewoodProcessor
	
	spec.hasSplit2 = true
	spec.splitetShapes2[shape] = shape
end
function FirewoodProcessor:splitShapeCallback3(shape, isBelow, isAbove, minY, maxY, minZ, maxZ)
	local spec = self.spec_firewoodProcessor
	
	spec.hasSplit3 = true
	spec.splitetShapes5 = {}
end
function FirewoodProcessor:splitShapeCallback4(shape, isBelow, isAbove, minY, maxY, minZ, maxZ)
	local spec = self.spec_firewoodProcessor
	
	spec.hasSplit4 = true
	spec.splitetShapes2 = {}
end
function FirewoodProcessor:splitShapeCallback5(shape, isBelow, isAbove, minY, maxY, minZ, maxZ)
	local spec = self.spec_firewoodProcessor
	
	spec.hasSplit5 = true
	spec.splitetShapes5[shape] = shape
end
function FirewoodProcessor:splitShapesTrigger(triggerId, otherId, onEnter, onLeave, onStay)
 -- log("SPLITTRIGGER FIRED: triggerId=%s otherId=%s onEnter=%s onLeave=%s", tostring(triggerId), tostring(otherId), tostring(onEnter), tostring(onLeave))
	local spec = self.spec_firewoodProcessor;
	if otherId ~= 0 and otherId ~= self.rootNode then	
	-- if otherId ~= 0 then
		local splitType = getSplitType(otherId)
		if splitType ~= nil then
			local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(otherId)
			if onEnter then
				if spec.shapesInTrigger[otherId] == nil then
					spec.shapesInTrigger[otherId] = otherId
				end
			elseif onLeave then
				if spec.shapesInTrigger[otherId] ~= nil then
					spec.shapesInTrigger[otherId] = nil
				end
			end
		end
	end
end
function FirewoodProcessor:onUpdate(dt)
	local spec = self.spec_firewoodProcessor
	if not spec.modInitialized or not spec.modAllowed then
		return
	end
	if self:getIsUnfolded() ~= nil and self:getIsUnfolded() == false then
		self:toggleUnload(false, true)
		self:setAnimationTime(spec.beltAnimation.name, 0, true)
		setVisibility(spec.pipeExtra1, false)
		setVisibility(spec.pipeExtra2, false)
	else
		setVisibility(spec.pipeExtra1, true)
		setVisibility(spec.pipeExtra2, true)
	end
	if self.spec_cylindered.movingTools[spec.cutLengthMovingTool] ~= nil and self.spec_cylindered.movingTools[spec.cutLengthMovingTool].curTrans ~= nil then
		spec.cutLength = 0.55 - self.spec_cylindered.movingTools[spec.cutLengthMovingTool].curTrans[3]
	end
	if self.spec_cylindered.movingTools[spec.splitHeightMovingTool] ~= nil and self.spec_cylindered.movingTools[spec.splitHeightMovingTool].curTrans ~= nil then
		spec.splitDiameter = 0.53 + (self.spec_cylindered.movingTools[spec.splitHeightMovingTool].curTrans[2] * 1.5)
	end
	
	local readyToCut = false
	if spec.curSplitShape ~= nil then
        if not entityExists(spec.curSplitShape) then
            spec.curSplitShape = nil
			spec.lastDiameter = 0
			readyToCut = false
        end
    end
	if spec.logToLoad ~= nil then
        if not entityExists(spec.logToLoad) then
            spec.logToLoad = nil
        end
    end
	if spec.logFound ~= nil then
        if not entityExists(spec.logFound) then
            spec.logFound = nil
        end
    end
	if self:getAnimationTime(spec.cutAnimation.name) == 1 then
		self:setAnimationTime(spec.cutAnimation.name, 0, true)
		spec.cutDone = false
		if self.isClient then
			g_animationManager:stopAnimations(spec.animationNodesSaw)
			g_soundManager:stopSample(spec.samples.work)
			g_soundManager:playSample(spec.samples.stop)
		end
	end
		------------------------------------------------------------------------------------			FEEDER				---------------------------------------------------------------------------------------------
	local x,y,z = getWorldTranslation(spec.feederSearchNode)
    local nx,ny,nz = localDirectionToWorld(spec.feederSearchNode, 1,0,0)
    local yx,yy,yz = localDirectionToWorld(spec.feederSearchNode, 0,1,0)
    local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, 1.5, 1.5)
    if shape ~= 0 then
		spec.feederDiameter = math.max(maxY-minY, maxZ-minZ)
        spec.feederShape = shape
	else
        spec.feederShape = nil
    end
	
	
	if spec.feederShape ~= nil and spec.feederShape ~= 0 then
-- log("Feeder debug: feederSearchNode=%s feederShape=%s feederDiameter=%s | node5=%s node5_node=%s",
    -- tostring(spec.feederSearchNode), 
    -- tostring(spec.feederShape), 
    -- string.format("%.4f", spec.feederDiameter or 0), 
    -- tostring(spec.animationNodesFeeder and spec.animationNodesFeeder[5]), 
    -- tostring(spec.animationNodesFeeder and spec.animationNodesFeeder[5] and spec.animationNodesFeeder[5].rootNode)
-- )	
		local x,y,z = getWorldTranslation(spec.animationNodesFeeder[5].rootNode)
		local a,b,c = getWorldTranslation(spec.feederSearchNode)
		spec.feederHeight = y - b - 0.25
		local x,y,z = getRotation(self.spec_cylindered.movingTools[5].node)
		local moved = false
		local diff = math.abs(spec.feederHeight - spec.feederDiameter)
		if diff > 0.01 then
			if spec.feederHeight < spec.feederDiameter then
				x = x + 0.01
				moved = true
			else
				x = x - 0.01
				moved = true
			end
		end
		if moved and self.isServer then
			self.spec_cylindered.movingTools[5].curRot[1] = x
			setRotation(self.spec_cylindered.movingTools[5].node, x,y,z)
			Cylindered.setDirty(self, self.spec_cylindered.movingTools[5])
		end
	end	
		------------------------------------------------------------------------------------			SPLITTER				---------------------------------------------------------------------------------------------
	if not self:getIsAnimationPlaying(spec.splitAnimation.name) then
		spec.splitetShapes = {}
		spec.splitetShapes2 = {}
		spec.splitetShapes3 = {}
		spec.splitetShapes4 = {}
		spec.splitetShapes5 = {}
	end
	local x,y,z = getWorldTranslation(spec.searchNode)
    local nx,ny,nz = localDirectionToWorld(spec.searchNode, 1,0,0)
    local yx,yy,yz = localDirectionToWorld(spec.searchNode, 0,1,0)
    local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.splitSearchSizeY, spec.splitSearchSizeZ)
    if shape ~= 0 and spec.shapesInTrigger[shape] ~= nil then
        spec.splitterSplitShape = shape
	else
		spec.splitterSplitShape = nil
    end
	if self.isServer then
        if spec.splitAnimation.name ~= nil then
            if self:getIsAnimationPlaying(spec.splitAnimation.name) then
					if spec.splitterSplitShape ~= nil and entityExists(spec.splitterSplitShape) and not spec.hasSplit then
						local x, y, z = getWorldTranslation(spec.splitNode)
						local nx, ny, nz = localDirectionToWorld(spec.splitNode, 1,0,0)
						local yx, yy, yz = localDirectionToWorld(spec.splitNode, 0,1,0)
						splitShape(spec.splitterSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, spec.splitSizeY, spec.splitSizeZ, "splitShapeCallback", self)
						if self.isClient and not g_soundManager:getIsSamplePlaying(spec.samples.split) then
							g_soundManager:playSample(spec.samples.split)
						end
					end
			else
					spec.hasSplit = false
			end
		end
	end
	if spec.splitCount == 4 or spec.splitCount == 6 then
		local x,y,z = getWorldTranslation(spec.searchNode2)
		local nx,ny,nz = localDirectionToWorld(spec.searchNode2, 1,0,0)
		local yx,yy,yz = localDirectionToWorld(spec.searchNode2, 0,1,0)
		local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.splitSearchSizeY, spec.splitSearchSizeZ2)
		if shape ~= 0 and spec.shapesInTrigger[shape] ~= nil then
			spec.splitterSplitShape2 = shape
		else
			spec.splitterSplitShape2 = nil
		end
		if self.isServer then
			if spec.splitAnimation.name ~= nil then
				if self:getIsAnimationPlaying(spec.splitAnimation.name) then
						if spec.splitterSplitShape2 ~= nil and entityExists(spec.splitterSplitShape2) and not spec.hasSplit2 then
							local x, y, z = getWorldTranslation(spec.splitNode2)
							local nx, ny, nz = localDirectionToWorld(spec.splitNode2, 1,0,0)
							local yx, yy, yz = localDirectionToWorld(spec.splitNode2, 0,1,0)
							splitShape(spec.splitterSplitShape2, x,y,z, nx,ny,nz, yx,yy,yz, spec.splitSizeY, spec.splitSizeZ, "splitShapeCallback2", self)
							if self.isClient and not g_soundManager:getIsSamplePlaying(spec.samples.split) then
								g_soundManager:playSample(spec.samples.split)
							end
						end
				else
						spec.hasSplit2 = false
				end
			end
		end
	end
	if spec.splitCount == 6 then
		local x,y,z = getWorldTranslation(spec.searchNode5)
		local nx,ny,nz = localDirectionToWorld(spec.searchNode5, 1,0,0)
		local yx,yy,yz = localDirectionToWorld(spec.searchNode5, 0,1,0)
		local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.splitSearchSizeY, spec.splitSearchSizeZ2)
		if shape ~= 0 and spec.shapesInTrigger[shape] ~= nill then
			spec.splitterSplitShape5 = shape
		else
			spec.splitterSplitShape5 = nil
		end
		if self.isServer then
			if spec.splitAnimation.name ~= nil then
				if self:getIsAnimationPlaying(spec.splitAnimation.name) then
						if spec.splitterSplitShape5 ~= nil and entityExists(spec.splitterSplitShape5) and not spec.hasSplit5 then
							local x, y, z = getWorldTranslation(spec.splitNode5)
							local nx, ny, nz = localDirectionToWorld(spec.splitNode5, 1,0,0)
							local yx, yy, yz = localDirectionToWorld(spec.splitNode5, 0,1,0)
							splitShape(spec.splitterSplitShape5, x,y,z, nx,ny,nz, yx,yy,yz, spec.splitSizeY, spec.splitSizeZ, "splitShapeCallback5", self)
							if self.isClient and not g_soundManager:getIsSamplePlaying(spec.samples.split) then
								g_soundManager:playSample(spec.samples.split)
							end
						end
				else
						spec.hasSplit5 = false
				end
			end
		end
	end
	if spec.splitCount == 4 or spec.splitCount == 6 then
		local x,y,z = getWorldTranslation(spec.searchNode3)
		local nx,ny,nz = localDirectionToWorld(spec.searchNode3, 1,0,0)
		local yx,yy,yz = localDirectionToWorld(spec.searchNode3, 0,1,0)
		local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.splitSearchSizeY2,spec.splitSearchSizeZ2)
		if spec.splitCount == 4 then
			if shape ~= 0 and spec.shapesInTrigger[shape] ~= nil then
				spec.splitterSplitShape3 = shape
			else
				spec.splitterSplitShape3 = nil
			end
		elseif spec.splitCount == 6 then
			if shape ~= 0 then
				local vol = 0
				for i,shape in pairs(spec.splitetShapes5) do
					if shape ~= nil and entityExists(shape) and getVolume(shape) > vol then
						spec.splitterSplitShape3 = shape
						vol = getVolume(shape)
					end
				end
			else
				spec.splitterSplitShape3 = nil
			end
		end
		if self.isServer then
			if spec.splitAnimation.name ~= nil then
				if self:getIsAnimationPlaying(spec.splitAnimation.name) then
						if spec.splitterSplitShape3 ~= nil and entityExists(spec.splitterSplitShape3) and not spec.hasSplit3 then
							local x, y, z = getWorldTranslation(spec.splitNode3)
							local nx, ny, nz = localDirectionToWorld(spec.splitNode3, 1,0,0)
							local yx, yy, yz = localDirectionToWorld(spec.splitNode3, 0,1,0)
							splitShape(spec.splitterSplitShape3, x,y,z, nx,ny,nz, yx,yy,yz, spec.splitSizeY, spec.splitSizeZ, "splitShapeCallback3", self)
							if self.isClient and not g_soundManager:getIsSamplePlaying(spec.samples.split) then
								g_soundManager:playSample(spec.samples.split)
							end
						end
				else
						spec.hasSplit3 = false
				end
			end
		end
	end
	if spec.splitCount == 6 then
		local x,y,z = getWorldTranslation(spec.searchNode4)
		local nx,ny,nz = localDirectionToWorld(spec.searchNode4, 1,0,0)
		local yx,yy,yz = localDirectionToWorld(spec.searchNode4, 0,1,0)
		local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.splitSearchSizeY2, spec.splitSearchSizeZ2)
		if shape ~= nil and shape ~= 0 then
			local vol = 0
			for i,shape in pairs(spec.splitetShapes2) do
				if shape ~= nil and entityExists(shape) and getVolume(shape) > vol then
					spec.splitterSplitShape4 = shape
					vol = getVolume(shape)
				end
			end
		else
			spec.splitterSplitShape4 = nil
		end
		if self.isServer then
			if spec.splitAnimation.name ~= nil then
				if self:getIsAnimationPlaying(spec.splitAnimation.name) then
					if spec.splitterSplitShape4 ~= nil and entityExists(spec.splitterSplitShape4) and not spec.hasSplit4 then
						local x, y, z = getWorldTranslation(spec.splitNode4)
						local nx, ny, nz = localDirectionToWorld(spec.splitNode4, 1,0,0)
						local yx, yy, yz = localDirectionToWorld(spec.splitNode4, 0,1,0)
						splitShape(spec.splitterSplitShape4, x,y,z, nx,ny,nz, yx,yy,yz, spec.splitSizeY, spec.splitSizeZ, "splitShapeCallback4", self)
						if self.isClient and not g_soundManager:getIsSamplePlaying(spec.samples.split) then
							g_soundManager:playSample(spec.samples.split)
						end
					end
				else
					spec.hasSplit4 = false
					spec.splitterSplitShape4 = nil
				end
			end
		end
	end
			------------------------------------------------------------------------------------			SAW				---------------------------------------------------------------------------------------------
	if self.isServer and spec.curSplitShape ~= nil and not spec.cutDone then
        if spec.cutAnimation.name ~= nil then
            if self:getIsAnimationPlaying(spec.cutAnimation.name) then
                if self:getAnimationTime(spec.cutAnimation.name) > spec.lastDiameter * 1.1 then
					readyToCut = true
                end
            end
		end
        if readyToCut then
			readyToCut = false
			spec.cutDone = true
            local x,y,z = getWorldTranslation(spec.cutNode)
            local nx,ny,nz = localDirectionToWorld(spec.cutNode, 1,0,0)
            local yx,yy,yz = localDirectionToWorld(spec.cutNode, 0,1,0)
            local currentSplitShape
            currentSplitShape = spec.curSplitShape
            spec.curSplitShape = nil
            g_currentMission:removeKnownSplitShape(currentSplitShape)
			ChainsawUtil.cutSplitShape(currentSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ, self:getActiveFarm())
        end
	end
			------------------------------------------------------------------------------------			BELT				---------------------------------------------------------------------------------------------
	if self.isServer or self.isClient then
		if self.spec_cylindered.movingTools[1] ~= nil and self.spec_cylindered.movingTools[1].curRot[1] ~= nil then
			if math.deg(self.spec_cylindered.movingTools[1].curRot[1]) > 5 then
				if not self:getIsAnimationPlaying(spec.beltAnimation2.name) and self:getAnimationTime(spec.beltAnimation2.name) < 1 then
					self:playAnimation(spec.beltAnimation2.name, spec.beltAnimation2.speedScale, self:getAnimationTime(spec.beltAnimation2.name), true)
					
				end
				if spec.unload and not self:getIsAnimationPlaying(spec.beltAnimation.name) then
					self:playAnimation(spec.beltAnimation.name, spec.beltAnimation.speedScale, self:getAnimationTime(spec.beltAnimation.name), true)
				end
			else
				if not self:getIsAnimationPlaying(spec.beltAnimation2.name) and self:getAnimationTime(spec.beltAnimation2.name) > 0 then
					self:playAnimation(spec.beltAnimation2.name, -spec.beltAnimation2.speedScale, self:getAnimationTime(spec.beltAnimation2.name), true)
					if self:getIsAnimationPlaying(spec.beltAnimation.name) then
						self:stopAnimation(spec.beltAnimation.name, true)
						self:setAnimationTime(spec.beltAnimation.name, 0, true)
					end
				end
			end
		end
	end
	if spec.debug then
		spec.infoText = ''
		spec.infoText = spec.infoText .. 'moveVelocityZ - '..(spec.moveVelocityZ)..'\n'
		spec.infoText = spec.infoText .. 'getAnimationTime - '..(self:getAnimationTime(spec.beltAnimation.name))..'\n'
		spec.infoText = spec.infoText .. 'spec.isFeeding - '..(tostring(spec.isFeeding))..'\n'
		spec.infoText = spec.infoText .. 'lastDiameter - '..string.format('%.1f', spec.lastDiameter)..'\n'
		spec.infoText = spec.infoText .. 'curSplitShape - '..(spec.curSplitShape ~= nil and spec.curSplitShape or 'nil')..'\n'
		spec.infoText = spec.infoText .. 'splitterSplitShape - '..(spec.splitterSplitShape ~= nil and spec.splitterSplitShape or 'nil')..'\n'
		spec.infoText = spec.infoText .. 'splitterSplitShape2 - '..(spec.splitterSplitShape2 ~= nil and spec.splitterSplitShape2 or 'nil')..'\n'
		spec.infoText = spec.infoText .. 'splitterSplitShape3 - '..(spec.splitterSplitShape3 ~= nil and spec.splitterSplitShape3 or 'nil')..'\n'
		spec.infoText = spec.infoText .. 'splitterSplitShape4 - '..(spec.splitterSplitShape4 ~= nil and spec.splitterSplitShape4 or 'nil')..'\n'
		spec.infoText = spec.infoText .. 'splitterSplitShape5 - '..(spec.splitterSplitShape5 ~= nil and spec.splitterSplitShape5 or 'nil')..'\n'
		spec.infoText = spec.infoText .. 'beltMovingTool - '..tostring(math.deg(self.spec_cylindered.movingTools[1].curRot[1]))..'\n'
		spec.infoText = spec.infoText .. 'splitHeightMovingTool - '..tostring(self.spec_cylindered.movingTools[spec.cutLengthMovingTool].curTrans[3])..'\n'
		spec.infoText = spec.infoText .. 'cutLength - '..tostring(spec.cutLength)..'\n'
		spec.infoText = spec.infoText .. 'splitCount - '..tostring(spec.splitCount)..'\n'
		spec.infoText = spec.infoText .. 'logToLoad - '..tostring(spec.logToLoad)..'\n'
		spec.infoText = spec.infoText .. 'logFound - '..tostring(spec.logFound)..'\n'
		spec.infoText = spec.infoText .. 'feederHeight - '..tostring(spec.feederHeight)..'\n'
		spec.infoText = spec.infoText .. 'feederDiameter - '..tostring(spec.feederDiameter)..'\n'
		spec.infoText = spec.infoText .. 'feederMovingTool - '..tostring(math.deg(self.spec_cylindered.movingTools[5].curRot[1]))..'\n'
		local shapesCount = 0
		for _,shape in pairs(spec.shapesInTrigger) do
			if not entityExists(shape) then
				spec.shapesInTrigger[shape] = nil
			else
				spec.infoText = spec.infoText .. shape..'\n'
				shapesCount = shapesCount + 1
			end
		end
		spec.infoText = spec.infoText .. 'shapesCount - '..shapesCount..'\n'
	end
	if self.isClient then
		if self:getIsAnimationPlaying(spec.cutAnimation.name) then
            if self:getAnimationTime(spec.cutAnimation.name) > 0.5 - (spec.lastDiameter / 2) and self:getAnimationTime(spec.cutAnimation.name) < spec.lastDiameter and spec.curSplitShape ~= nil then
				if not g_soundManager:getIsSamplePlaying(spec.samples.cut) then
					g_soundManager:playSample(spec.samples.cut)
				end
				-- g_effectManager:setFillType(spec.cutEffects, FillType.WOODCHIPS)
				-- NEW
				g_effectManager:setEffectTypeInfo(spec.cutEffects, FillType.WOODCHIPS)				
				g_effectManager:startEffects(spec.cutEffects)
			elseif self:getAnimationTime(spec.cutAnimation.name) > spec.lastDiameter * 1.1 then
				if g_soundManager:getIsSamplePlaying(spec.samples.cut) then
					g_soundManager:stopSample(spec.samples.cut)
				end
				g_effectManager:stopEffects(spec.cutEffects)
			end
		end
		if self:getIsActiveForInput() then
			if spec.event_IDs ~= nil and g_dedicatedServerInfo == nil then
				for actionName,eventID in pairs(spec.event_IDs) do
					g_inputBinding:setActionEventTextVisibility(eventID, FirewoodProcessor.showHelp)
					if actionName == InputAction.FIREWOODPROCESSOR_LOAD_LOG then
						local can = false
						if spec.loadAnimation.name ~= nil and not self:getIsAnimationPlaying(spec.loadAnimation.name) then can = true end
						g_inputBinding:setActionEventActive(eventID, can)
						g_inputBinding:setActionEventText(eventID, spec.logToLoad == nil and g_i18n:getText('FIREWOODPROCESSOR_FIND_LOAD_LOG') or g_i18n:getText('input_FIREWOODPROCESSOR_LOAD_LOG'))
					elseif actionName == InputAction.FIREWOODPROCESSOR_CUT_LOG then
						local can = false
						if spec.cutAnimation.name ~= nil and not self:getIsAnimationPlaying(spec.cutAnimation.name) then can = true end
						g_inputBinding:setActionEventActive(eventID, can)
					elseif actionName == InputAction.FIREWOODPROCESSOR_SPLIT_LOG then
						local can = false
						if spec.splitAnimation.name ~= nil and not self:getIsAnimationPlaying(spec.splitAnimation.name) then can = true end
						g_inputBinding:setActionEventActive(eventID, can)
					elseif actionName == InputAction.FIREWOODPROCESSOR_TOGGLE_SPLIT_COUNT then
						g_inputBinding:setActionEventText(eventID, g_i18n:getText('input_FIREWOODPROCESSOR_TOGGLE_SPLIT_COUNT').." "..spec.splitCount.." "..g_i18n:getText('FIREWOODPROCESSOR_TOGGLE_SPLIT_COUNT2'))
					elseif actionName == InputAction.FIREWOODPROCESSOR_UNLOAD then
						g_inputBinding:setActionEventText(eventID, spec.unload and g_i18n:getText('FIREWOODPROCESSOR_STOPT_UNLOADING') or g_i18n:getText('FIREWOODPROCESSOR_START_UNLOADING'))
					end
				end
			end
			g_currentMission:addExtraPrintText(g_i18n:getText('FIREWOODPROCESSOR_INFO').." "..string.format('%.1f', spec.cutLength * 100).." cm, "..g_i18n:getText('FIREWOODPROCESSOR_INFO2').." "..string.format('%.1f', spec.lastDiameter*1000).." mm")
			g_currentMission:addExtraPrintText(g_i18n:getText('FIREWOODPROCESSOR_INFO3').." "..string.format('%.1f', spec.splitDiameter * 1000).." mm")
		end
	end
end
function FirewoodProcessor:onUpdateTick(dt)
	local spec = self.spec_firewoodProcessor
	
	local x,y,z = getWorldTranslation(spec.cutNode)
    local nx,ny,nz = localDirectionToWorld(spec.cutNode, 1,0,0)
    local yx,yy,yz = localDirectionToWorld(spec.cutNode, 0,1,0)
    local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ)
    if shape ~= 0 then
        spec.curSplitShape = shape
	else
		spec.curSplitShape = nil
    end
	local x,y,z = getWorldTranslation(spec.cutNode2)
    local nx,ny,nz = localDirectionToWorld(spec.cutNode2, 1,0,0)
    local yx,yy,yz = localDirectionToWorld(spec.cutNode2, 0,1,0)
    local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ)
    if shape ~= 0 then
		spec.lastDiameter = math.max(maxY-minY, maxZ-minZ)
	else
		spec.lastDiameter = 0
    end
	if spec.curSplitShape ~= nil then
        local minY,maxY, minZ,maxZ = testSplitShape(spec.curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ)
        if minY == nil then
            spec.curSplitShape = nil
        else
            local cutTooLow = false
            _,y,_ = localToLocal(spec.cutNode, spec.curSplitShape, 0,minY,minZ)
            cutTooLow = cutTooLow or y < 0.01
            _,y,_ = localToLocal(spec.cutNode, spec.curSplitShape, 0,minY,maxZ)
            cutTooLow = cutTooLow or y < 0.01
            _,y,_ = localToLocal(spec.cutNode, spec.curSplitShape, 0,maxY,minZ)
            cutTooLow = cutTooLow or y < 0.01
            _,y,_ = localToLocal(spec.cutNode, spec.curSplitShape, 0,maxY,maxZ)
            cutTooLow = cutTooLow or y < 0.01
            if cutTooLow then
                spec.curSplitShape = nil
            end
        end
    end
			------------------------------------------------------------------------------------			LOADER				---------------------------------------------------------------------------------------------
	local x,y,z = getWorldTranslation(spec.logToLoadNode)
    local nx,ny,nz = localDirectionToWorld(spec.logToLoadNode, 1,0,0)
    local yx,yy,yz = localDirectionToWorld(spec.logToLoadNode, 0,1,0)
    local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, 1.5, 1.2)
	if shape ~= nil then
		spec.logToLoad = shape
	else
		spec.logToLoad = nil
	end
	local x,y,z = getWorldTranslation(spec.searchLogNode[1])
	local nx,ny,nz = localDirectionToWorld(spec.searchLogNode[1], 1,0,0)
	local yx,yy,yz = localDirectionToWorld(spec.searchLogNode[1], 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) then
		x,y,z = getWorldTranslation(spec.searchLogNode[2])
		nx,ny,nz = localDirectionToWorld(spec.searchLogNode[2], 1,0,0)
		yx,yy,yz = localDirectionToWorld(spec.searchLogNode[2], 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 then
		spec.logFound = shape
	else
		spec.logFound = nil
	end
			------------------------------------------------------------------------------------			FEEDER				---------------------------------------------------------------------------------------------
	
end
function FirewoodProcessor:cutTree(noEventSend)
    local spec = self.spec_firewoodProcessor
    
	WoodHarvesterCutTreeEvent.sendEvent(self, noEventSend)
    if self.isServer then
        if spec.cutAnimation.name ~= nil then
            self:setAnimationTime(spec.cutAnimation.name, 0, true)
            self:playAnimation(spec.cutAnimation.name, spec.cutAnimation.speedScale, self:getAnimationTime(spec.cutAnimation.name), false)
            self:setAnimationTime(spec.cutAnimation2.name, 0, true)
            self:playAnimation(spec.cutAnimation2.name, spec.cutAnimation2.speedScale, self:getAnimationTime(spec.cutAnimation2.name), false)
        end
    end
	if self.isClient then
		g_animationManager:startAnimations(spec.animationNodesSaw)
		g_soundManager:playSample(spec.samples.start)
		g_soundManager:playSample(spec.samples.work, 0, spec.samples.start)
	end
end
function FirewoodProcessor:toggleFeedingLog(state, dir, noEventSend)
	local spec = self.spec_firewoodProcessor
	
	FirewoodProcessorToggleFeedEvent.sendEvent(self, state, dir, noEventSend)
	
	spec.isFeeding = state
	spec.feedDir = dir
	if spec.moveColNodes ~= nil then
		if state then
			self:raiseActive()
			for _,node in pairs(spec.moveColNodes) do
				local x,y,z = getTranslation(node)
				z = z + 0.001
				setTranslation(node, x,y,z)
				setFrictionVelocity(node, spec.moveVelocityZ * dir)
			end
			if self.isClient then


				-- NEW FS25
				spec.animationNodesFeeder[1].scrollSpeed = spec.animationNodesFeeder[1].scrollSpeedOrig * dir * -1
				spec.animationNodesFeeder[2].rotSpeed = spec.animationNodesFeeder[2].rotSpeedOrig * dir
				spec.animationNodesFeeder[3].rotSpeed = spec.animationNodesFeeder[3].rotSpeedOrig * dir
				spec.animationNodesFeeder[4].rotSpeed = spec.animationNodesFeeder[4].rotSpeedOrig * dir
				spec.animationNodesFeeder[5].rotSpeed = spec.animationNodesFeeder[5].rotSpeedOrig * dir * -1
			
				-- spec.animationNodesFeeder[1].speedFuncTarget.scrollSpeed = spec.animationNodesFeeder[1].speedFuncTarget.scrollSpeedOrig * dir * -1
				-- spec.animationNodesFeeder[2].speedFuncTarget.rotSpeed = spec.animationNodesFeeder[2].speedFuncTarget.rotSpeedOrig * dir
				-- spec.animationNodesFeeder[3].speedFuncTarget.rotSpeed = spec.animationNodesFeeder[3].speedFuncTarget.rotSpeedOrig * dir
				-- spec.animationNodesFeeder[4].speedFuncTarget.rotSpeed = spec.animationNodesFeeder[4].speedFuncTarget.rotSpeedOrig * dir
				-- spec.animationNodesFeeder[5].speedFuncTarget.rotSpeed = spec.animationNodesFeeder[5].speedFuncTarget.rotSpeedOrig * dir * -1
				g_animationManager:startAnimations(spec.animationNodesFeeder)
				self:playAnimation(spec.feedAnimation.name, spec.feedAnimation.speedScale * dir, self:getAnimationTime(spec.feedAnimation.name), true)
				if not g_soundManager:getIsSamplePlaying(spec.samples.feed) then
					g_soundManager:playSample(spec.samples.feed)
				end
			end
		else
			for _,node in pairs(spec.moveColNodes) do
				local x,y,z = getTranslation(node)
				z = z - 0.001
				setTranslation(node, x,y,z)
				setFrictionVelocity(node, 0.0)
			end
			if self.isClient then
				self:stopAnimation(spec.feedAnimation.name, true)
				g_animationManager:stopAnimations(spec.animationNodesFeeder)
				if g_soundManager:getIsSamplePlaying(spec.samples.feed) then
					g_soundManager:stopSample(spec.samples.feed)
				end
			end
		end
    end
end
function FirewoodProcessor:toggleSplitCount(count, noEventSend)
	local spec = self.spec_firewoodProcessor
	
	FirewoodProcessorToggleSplitCountEvent.sendEvent(self, count, noEventSend)
	spec.splitCount = count
	if count == 2 then
		setVisibility(spec.splitterAdvance, false)
	elseif count == 4 then
		setVisibility(spec.splitterAdvance, true)
		setVisibility(spec.splitterAdvance2, false)
		setRotation(spec.splitterAdvance, 0, 0, 0)
	elseif count == 6 then
		setVisibility(spec.splitterAdvance, true)
		setVisibility(spec.splitterAdvance2, true)
		setRotation(spec.splitterAdvance, 0, 0, math.rad(30))
	end
end
function FirewoodProcessor:toggleUnload(state, noEventSend)
	local spec = self.spec_firewoodProcessor
	
	FirewoodProcessorToggleUnloadEvent.sendEvent(self, state, noEventSend)
	spec.unload = state
	if state then
		if self.isServer then
			for _,node in pairs(spec.moveColNodesUnload) do
				setFrictionVelocity(node, spec.moveVelocityUnload)
				if math.deg(self.spec_cylindered.movingTools[1].curRot[1]) > 5 then
					--self:playAnimation(spec.beltAnimation.name, spec.beltAnimation.speedScale, self:getAnimationTime(spec.beltAnimation.name))
				end
			end
		end
		if math.deg(self.spec_cylindered.movingTools[1].curRot[1]) > 5 then
			if not self:getIsAnimationPlaying(spec.beltAnimation.name) then
				--self:playAnimation(spec.beltAnimation.name, spec.beltAnimation.speedScale, self:getAnimationTime(spec.beltAnimation.name), true)
			end
		end
		if self.isClient then
			g_animationManager:startAnimations(spec.animationNodesBelts)
			if not g_soundManager:getIsSamplePlaying(spec.samples.belt) then
				g_soundManager:playSample(spec.samples.belt)
			end
		end
	else
		if self.isServer then
			for _,node in pairs(spec.moveColNodesUnload) do
				setFrictionVelocity(node, 0.0)
				self:stopAnimation(spec.beltAnimation.name, false)
			end
		end
		if self.isClient then
			g_animationManager:stopAnimations(spec.animationNodesBelts)
			if g_soundManager:getIsSamplePlaying(spec.samples.belt) then
				g_soundManager:stopSample(spec.samples.belt)
			end
		end
	end
end
function FirewoodProcessor:toggleLoadLog(noEventSend)
	local spec = self.spec_firewoodProcessor
	
	FirewoodProcessorToggLoadLogEvent.sendEvent(self, noEventSend)
	if self.isServer then
		local canLoad = false
		local badTree = false
		if spec.logToLoad == nil or spec.logToLoad == 0 then
			local node = 1
			local x,y,z = getWorldTranslation(spec.searchLogNode[1])
			local nx,ny,nz = localDirectionToWorld(spec.searchLogNode[1], 1,0,0)
			local yx,yy,yz = localDirectionToWorld(spec.searchLogNode[1], 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 and shape ~= 0 and getRigidBodyType(shape) == "Dynamic" then
				local diameter = math.max(maxY-minY, maxZ-minZ)
				local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(shape)
				if sizeX > spec.maxLogSize or sizeX < spec.minLogSize or diameter < spec.minLogDiameter then
					badTree = true
					shape = nil
				end
			end
			if shape == nil or not entityExists(shape) or badTree then
				x,y,z = getWorldTranslation(spec.searchLogNode[2])
				nx,ny,nz = localDirectionToWorld(spec.searchLogNode[2], 1,0,0)
				yx,yy,yz = localDirectionToWorld(spec.searchLogNode[2], 0,1,0)
				shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.searchSizeY, spec.searchSizeZ)
				node = 2
				if shape ~= nil and shape ~= 0 and getRigidBodyType(shape) == "Dynamic" then
					diameter = math.max(maxY-minY, maxZ-minZ)
					local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(shape)
					if sizeX > spec.maxLogSize or sizeX < spec.minLogSize or diameter < spec.minLogDiameter then
						badTree = true
						shape = nil
					else
						badTree = false
					end
				end
			end
			if shape == nil or not entityExists(shape) or badTree then
				x,y,z = getWorldTranslation(spec.searchLogNode[3])
				nx,ny,nz = localDirectionToWorld(spec.searchLogNode[3], 1,0,0)
				yx,yy,yz = localDirectionToWorld(spec.searchLogNode[3], 0,1,0)
				shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.searchSizeY, spec.searchSizeZ)
				node = 3
				if shape ~= nil and shape ~= 0 and getRigidBodyType(shape) == "Dynamic" then
					diameter = math.max(maxY-minY, maxZ-minZ)
					local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(shape)
					if sizeX > spec.maxLogSize or sizeX < spec.minLogSize or diameter < spec.minLogDiameter then
						badTree = true
						shape = nil
					else
						badTree = false
					end
				end
			end
			if shape ~= nil then
				spec.logFound = shape
			else
				spec.logFound = nil
			end
			if not badTree then
				if spec.logFound == nil or spec.logFound == 0 then
					canLoad = true
				else
					local shape = spec.logFound
					local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(shape)
					local maxSize = math.max(sizeX, math.max(sizeY, sizeZ)) --fcelsa
					local a, b, c = localToWorld(spec.searchLogNode[node], 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 = 0
					if sizeX < 3.5 then
						diffX = 0.5
					end
					local wx,wy,wz = localToWorld(spec.loadPosition,0, -dist + math.abs(diffX),0)
					local dx,dy,dz = worldToLocal(getParent(shape),wx,wy,wz)
					local nx, ny, nz = localDirectionToWorld(spec.loadPosition, 1, 0, 0)
					local yx, yy, yz = localDirectionToWorld(spec.loadPosition, 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.minLogSize and maxSize <= spec.maxLogSize and numAttachments <= spec.loadMaxAtt then
						removeFromPhysics(shape)
						setTranslation(shape,dx,dy,dz)
						setDirection(shape, x, y, z, xdir, ydir, zdir)
						addToPhysics(shape)
						canLoad = true
					else
						g_currentMission:showBlinkingWarning(g_i18n:getText('FIREWOODPROCESSOR_WARNING'), 2000)
					end
				end
			end
		else
			local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(spec.logToLoad)
			if sizeX > spec.maxLogSize or sizeX < spec.minLogSize then
				badTree = true
			else
				canLoad = true
			end
		end
		if canLoad then
			self:setAnimationTime(spec.loadAnimation.name, 0, true)
			self:playAnimation(spec.loadAnimation.name, spec.loadAnimation.speedScale, self:getAnimationTime(spec.loadAnimation.name))
		elseif badTree then
			g_currentMission:showBlinkingWarning(g_i18n:getText('FIREWOODPROCESSOR_WARNING'), 2000)
		end
	end
end
function FirewoodProcessor:actionCallback(actionName, keyStatus, arg4, arg5, arg6)
	local spec = self.spec_firewoodProcessor
	if not spec.modInitialized or not spec.modAllowed then
		return
	end
	
	if self:getIsActive() then
		if keyStatus > 0 then
			if actionName == 'FIREWOODPROCESSOR_LOAD_LOG' then
				if self:getIsUnfolded() ~= nil and self:getIsUnfolded() == true then
					self:toggleLoadLog(false)
				else
					g_currentMission:showBlinkingWarning(spec.unfoldWarning, 2000)
				end
			elseif actionName == 'FIREWOODPROCESSOR_FEED_LOG' then
				self:toggleFeedingLog(true, 1, false)
			elseif actionName == 'FIREWOODPROCESSOR_FEED_LOG_BACK' then
				self:toggleFeedingLog(true, -1, false)
			elseif actionName == 'FIREWOODPROCESSOR_CUT_LOG' then
				self:cutTree(false)
			elseif actionName == 'FIREWOODPROCESSOR_SPLIT_LOG' then
				self:setAnimationTime(spec.splitAnimation.name, 0, true)
				self:playAnimation(spec.splitAnimation.name, spec.splitAnimation.speedScale, self:getAnimationTime(spec.splitAnimation.name), false)
			elseif actionName == 'FIREWOODPROCESSOR_TOGGLE_SPLIT_COUNT' then
				if spec.splitCount < 6 then
					spec.splitCount = spec.splitCount + 2
				else
					spec.splitCount = 2
				end
				self:toggleSplitCount(spec.splitCount)
			elseif actionName == 'FIREWOODPROCESSOR_UNLOAD' then
				if self:getIsUnfolded() ~= nil and self:getIsUnfolded() == true then
					spec.unload = not spec.unload
					self:toggleUnload(spec.unload, false)
				else
					g_currentMission:showBlinkingWarning(spec.unfoldWarning, 2000)
				end
				
			end
		elseif keyStatus == 0 then
			if actionName == 'FIREWOODPROCESSOR_FEED_LOG' then
				self:toggleFeedingLog(false, 0, false)
			elseif actionName == 'FIREWOODPROCESSOR_FEED_LOG_BACK' then
				self:toggleFeedingLog(false, 0, false)
			end
		end
	end
end
function FirewoodProcessor:getIsFoldAllowed(superFunc)
    local spec = self.spec_firewoodProcessor
    
	if spec.loadAnimation ~= nil and self:getAnimationTime(spec.loadAnimation.name) ~= 0 and self:getAnimationTime(spec.loadAnimation.name) ~= 1 then
		return false
	end
	return superFunc(self)
end
function FirewoodProcessor:onDraw()
	local spec = self.spec_firewoodProcessor
	if not spec.modInitialized or not spec.modAllowed then
		return
	end
	if spec.debug then
		renderText(0.69, 0.85, 0.013, spec.infoText)
		local source = spec.cutNode
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSizeY, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSizeZ)
			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.cutNode2
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSearchSizeY2, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSearchSizeY2, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSearchSizeZ2)
			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.searchNode3
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSearchSizeY2, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSearchSizeY2, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSearchSizeZ2)
			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.searchNode4
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSearchSizeY2, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSearchSizeY2, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSearchSizeZ2)
			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.splitNode2
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSizeY, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSizeZ)
			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.splitNode3
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSizeY, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSizeZ)
			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.splitNode4
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSizeY, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSizeZ)
			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.splitNode5
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSizeY, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSizeZ)
			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.logToLoadNode
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSizeY, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSizeZ)
			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.searchLogNode[1]
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSizeY, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSizeZ)
			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.searchLogNode[2]
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSizeY, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSizeZ)
			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.searchLogNode[3]
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSizeY, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSizeZ)
			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.loadPosition
		if source ~= nil then
			local x,y,z = getWorldTranslation(source)
			local nx, ny, nz = localDirectionToWorld(source, spec.splitSizeY, 0, 0)
			local yx, yy, yz = localDirectionToWorld(source, 0, spec.splitSizeY, 0)
			local zx, zy, zz = localDirectionToWorld(source, 0, 0, spec.splitSizeZ)
			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 FirewoodProcessor:onReadStream(streamId, connection)
	local spec = self.spec_firewoodProcessor
	if not spec.modInitialized or not spec.modAllowed then
		return
	end
	spec.isFeeding = streamReadBool(streamId)
	spec.feedDir = streamReadInt16(streamId)
	spec.splitCount = streamReadInt16(streamId)
	spec.unload = streamReadBool(streamId)
	
	if spec.isFeeding then
		self:toggleFeedingLog(spec.isFeeding, spec.feedDir, true)
	end
	self:toggleSplitCount(spec.splitCount, true)
	self:toggleUnload(spec.unload, true)
end
function FirewoodProcessor:onWriteStream(streamId, connection)
	local spec = self.spec_firewoodProcessor
	if not spec.modInitialized or not spec.modAllowed then
		return
	end
	streamWriteBool(streamId, spec.isFeeding)
	streamWriteInt16(streamId, spec.feedDir)
	streamWriteInt16(streamId, spec.splitCount)
	streamWriteBool(streamId, spec.unload)
end

FirewoodProcessorToggLoadLogEvent = {}
FirewoodProcessorToggLoadLogEvent_mt = Class(FirewoodProcessorToggLoadLogEvent, Event)

InitEventClass(FirewoodProcessorToggLoadLogEvent, "FirewoodProcessorToggLoadLogEvent")

function FirewoodProcessorToggLoadLogEvent.emptyNew()
    local self = Event.new(FirewoodProcessorToggLoadLogEvent_mt)
    self.className="FirewoodProcessorToggLoadLogEvent"
    return self
end
function FirewoodProcessorToggLoadLogEvent.new(object, grabRotationReleased)
	local self = FirewoodProcessorToggLoadLogEvent.emptyNew()
	self.object = object
	return self
end
function FirewoodProcessorToggLoadLogEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId)
    self:run(connection)
end
function FirewoodProcessorToggLoadLogEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object)
end
function FirewoodProcessorToggLoadLogEvent:run(connection)
	if self.object ~= nil then
		self.object:toggleLoadLog(true)
	end
	if not connection:getIsServer() then
		g_server:broadcastEvent(FirewoodProcessorToggLoadLogEvent.new(self.object), nil, connection, self.object)
	end
end
function FirewoodProcessorToggLoadLogEvent.sendEvent(vehicle, noEventSend)
	if noEventSend == nil or noEventSend == false then
		if g_server ~= nil then
			g_server:broadcastEvent(FirewoodProcessorToggLoadLogEvent.new(vehicle), nil, nil, vehicle)
		else
			g_client:getServerConnection():sendEvent(FirewoodProcessorToggLoadLogEvent.new(vehicle))
		end
	end
end

FirewoodProcessorToggleFeedEvent = {}
FirewoodProcessorToggleFeedEvent_mt = Class(FirewoodProcessorToggleFeedEvent, Event)

InitEventClass(FirewoodProcessorToggleFeedEvent, "FirewoodProcessorToggleFeedEvent")

function FirewoodProcessorToggleFeedEvent.emptyNew()
    local self = Event.new(FirewoodProcessorToggleFeedEvent_mt)
    self.className="FirewoodProcessorToggleFeedEvent"
    return self
end
function FirewoodProcessorToggleFeedEvent.new(object, state, dir)
	local self = FirewoodProcessorToggleFeedEvent.emptyNew()
	self.object = object
	self.state = state
	self.dir = dir
	return self
end
function FirewoodProcessorToggleFeedEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId)
    self.state = streamReadBool(streamId)
    self.dir = streamReadInt16(streamId)
    self:run(connection)
end
function FirewoodProcessorToggleFeedEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object)
	streamWriteBool(streamId, self.state)
	streamWriteInt16(streamId, self.dir)
end
function FirewoodProcessorToggleFeedEvent:run(connection)
	if self.object ~= nil then
		self.object:toggleFeedingLog(self.state, self.dir, true)
	end
	if not connection:getIsServer() then
		g_server:broadcastEvent(FirewoodProcessorToggleFeedEvent.new(self.object, self.state, self.dir), nil, connection, self.object)
	end
end
function FirewoodProcessorToggleFeedEvent.sendEvent(vehicle, state, dir, noEventSend)
	if noEventSend == nil or noEventSend == false then
		if g_server ~= nil then
			g_server:broadcastEvent(FirewoodProcessorToggleFeedEvent.new(vehicle, state, dir), nil, nil, vehicle)
		else
			g_client:getServerConnection():sendEvent(FirewoodProcessorToggleFeedEvent.new(vehicle, state, dir))
		end
	end
end

WoodHarvesterCutTreeEvent = {}
WoodHarvesterCutTreeEvent_mt = Class(WoodHarvesterCutTreeEvent, Event)

InitEventClass(WoodHarvesterCutTreeEvent, "WoodHarvesterCutTreeEvent")

function WoodHarvesterCutTreeEvent.emptyNew()
    local self = Event.new(WoodHarvesterCutTreeEvent_mt)
    self.className="WoodHarvesterCutTreeEvent"
    return self
end
function WoodHarvesterCutTreeEvent.new(object, grabRotationReleased)
	local self = WoodHarvesterCutTreeEvent.emptyNew()
	self.object = object
	return self
end
function WoodHarvesterCutTreeEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId)
    self:run(connection)
end
function WoodHarvesterCutTreeEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object)
end
function WoodHarvesterCutTreeEvent:run(connection)
	if self.object ~= nil then
		self.object:cutTree(true)
	end
	if not connection:getIsServer() then
		g_server:broadcastEvent(WoodHarvesterCutTreeEvent.new(self.object), nil, connection, self.object)
	end
end
function WoodHarvesterCutTreeEvent.sendEvent(vehicle, noEventSend)
	if noEventSend == nil or noEventSend == false then
		if g_server ~= nil then
			g_server:broadcastEvent(WoodHarvesterCutTreeEvent.new(vehicle), nil, nil, vehicle)
		else
			g_client:getServerConnection():sendEvent(WoodHarvesterCutTreeEvent.new(vehicle))
		end
	end
end

FirewoodProcessorToggleSplitCountEvent = {}
FirewoodProcessorToggleSplitCountEvent_mt = Class(FirewoodProcessorToggleSplitCountEvent, Event)

InitEventClass(FirewoodProcessorToggleSplitCountEvent, "FirewoodProcessorToggleSplitCountEvent")

function FirewoodProcessorToggleSplitCountEvent.emptyNew()
    local self = Event.new(FirewoodProcessorToggleSplitCountEvent_mt)
    self.className="FirewoodProcessorToggleSplitCountEvent"
    return self
end
function FirewoodProcessorToggleSplitCountEvent.new(object, count)
	local self = FirewoodProcessorToggleSplitCountEvent.emptyNew()
	self.object = object
	self.count = count
	return self
end
function FirewoodProcessorToggleSplitCountEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId)
    self.count = streamReadInt16(streamId)
    self:run(connection)
end
function FirewoodProcessorToggleSplitCountEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object)
	streamWriteInt16(streamId, self.count)
end
function FirewoodProcessorToggleSplitCountEvent:run(connection)
	if self.object ~= nil then
		self.object:toggleSplitCount(self.count, true)
	end
	if not connection:getIsServer() then
		g_server:broadcastEvent(FirewoodProcessorToggleSplitCountEvent.new(self.object, self.count), nil, connection, self.object)
	end
end
function FirewoodProcessorToggleSplitCountEvent.sendEvent(vehicle, count, noEventSend)
	if noEventSend == nil or noEventSend == false then
		if g_server ~= nil then
			g_server:broadcastEvent(FirewoodProcessorToggleSplitCountEvent.new(vehicle, count), nil, nil, vehicle)
		else
			g_client:getServerConnection():sendEvent(FirewoodProcessorToggleSplitCountEvent.new(vehicle, count))
		end
	end
end
FirewoodProcessorToggleUnloadEvent = {}
FirewoodProcessorToggleUnloadEvent_mt = Class(FirewoodProcessorToggleUnloadEvent, Event)

InitEventClass(FirewoodProcessorToggleUnloadEvent, "FirewoodProcessorToggleUnloadEvent")

function FirewoodProcessorToggleUnloadEvent.emptyNew()
    local self = Event.new(FirewoodProcessorToggleUnloadEvent_mt)
    self.className="FirewoodProcessorToggleUnloadEvent"
    return self
end
function FirewoodProcessorToggleUnloadEvent.new(object, state)
	local self = FirewoodProcessorToggleUnloadEvent.emptyNew()
	self.object = object
	self.state = state
	return self
end
function FirewoodProcessorToggleUnloadEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId)
    self.state = streamReadBool(streamId)
    self:run(connection)
end
function FirewoodProcessorToggleUnloadEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object)
	streamWriteBool(streamId, self.state)
end
function FirewoodProcessorToggleUnloadEvent:run(connection)
	if self.object ~= nil then
		self.object:toggleUnload(self.state, true)
	end
	if not connection:getIsServer() then
		g_server:broadcastEvent(FirewoodProcessorToggleUnloadEvent.new(self.object, self.state), nil, connection, self.object)
	end
end
function FirewoodProcessorToggleUnloadEvent.sendEvent(vehicle, state, noEventSend)
	if noEventSend == nil or noEventSend == false then
		if g_server ~= nil then
			g_server:broadcastEvent(FirewoodProcessorToggleUnloadEvent.new(vehicle, state), nil, nil, vehicle)
		else
			g_client:getServerConnection():sendEvent(FirewoodProcessorToggleUnloadEvent.new(vehicle, state))
		end
	end
end
