--	author:		WoodClaw3 by fruktor, Changes to WoodClaw4 by rafftnix & fireandice
--	date:		WoodClaw4: 16.02.2014

-- Änderungen am Skript nur mit meiner Zustimmung!
-- Modification only with my permission!

WoodClaw4 = {};

function WoodClaw4.prerequisitesPresent(specializations)
    return true;
end;

function WoodClaw4:load(xmlFile)
	self.triggerCallback = WoodClaw4.triggerCallback;
	self.addHelpButtonTextWithCorrectMouseButtons = WoodClaw4.addHelpButtonTextWithCorrectMouseButtons;
	self.trunkDetectTriggerCallback = WoodClaw4.trunkDetectTriggerCallback;
	self.attachTrunks = WoodClaw4.attachTrunks;
	self.detachTrunks = WoodClaw4.detachTrunks;
	self.getIsClawClosed = WoodClaw4.getIsClawClosed;
	self.trunkRaycastCallback = WoodClaw4.trunkRaycastCallback;
	if self.stopDamageCalculation == nil then
		self.stopDamageCalculation = WoodClaw4.stopDamageCalculation;
	end;
	
	self.setFreeSwing = SpecializationUtil.callSpecializationsFunction("setFreeSwing");
	self.isSelectable = true;
	self.isWoodClaw = true;
	
	self.wc = {};
	
	function addCallbackToTrunkTriggers(node, save) 
		for a=0, getNumOfChildren(node)-1 do
			local triggerId = getChildAt(node, a);
			addTrigger(triggerId, "triggerCallback", self);
			save[triggerId] = true;
		end;
	end;
	self.wc.balanceNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.woodClaw4.balanceNode#index"));
	self.wc.leftTrunkTriggerGroup = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.woodClaw4.leftTrunkTrigger#triggerGroup"));
	self.wc.leftTriggers = {}
	self.wc.leftTriggerTrunks = {}
	addCallbackToTrunkTriggers(self.wc.leftTrunkTriggerGroup, self.wc.leftTriggers);
	
	self.wc.rightTrunkTriggerGroup = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.woodClaw4.rightTrunkTrigger#triggerGroup"));
	self.wc.rightTriggers = {}
	self.wc.rightTriggerTrunks = {}
	addCallbackToTrunkTriggers(self.wc.rightTrunkTriggerGroup, self.wc.rightTriggers);
	
	self.wc.trunkDetectTriggers = {}
	local trunkDetectTriggerIndicesStr = getXMLString(xmlFile, "vehicle.woodClaw4.trunkDetectTrigger#indices");
	local trunkDetectTriggerIndices = Utils.splitString(" ", trunkDetectTriggerIndicesStr);
	for k, index in pairs(trunkDetectTriggerIndices) do
		local nodeId = Utils.indexToObject(self.components, index);
		addTrigger(nodeId, "trunkDetectTriggerCallback", self);
		table.insert(self.wc.trunkDetectTriggers, nodeId);
	end;
	
	self.wc.middleTrunkTrigger = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.woodClaw4.middleTrunkTrigger#triggerIndex"));
	addTrigger(self.wc.middleTrunkTrigger, "triggerCallback", self);
	self.wc.middleTriggerTrunks = {}

	self.wc.triggerTrunksTotal = {}
	self.wc.trunkAttachCompIndex = getXMLInt(xmlFile, "vehicle.WoodClaw4#trunkAttachCompIndex");
	
	self.wc.trunksAttached = false;
	self.wc.attachedTrunks = {};
	self.wc.trunkPairJoints = {}
	self.wc.openTolerance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.WoodClaw4#openTolerance"), 0.05);
	self.wc.trunkJointNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.woodClaw4#trunkJointIndex"));
	local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.woodClaw4#rotationLimit"));
	self.wc.trunkJointRotLimit = {math.rad(Utils.getNoNil(x, 0)), math.rad(Utils.getNoNil(y, 0)), math.rad(Utils.getNoNil(z, 0))}
	
	for a=1, table.getn(self.movingTools) do
		local entry = self.movingTools[a];
		local baseName = string.format("vehicle.movingTools.movingTool(%d)", a-1);
		local rotMinClosesWoodClaw = getXMLBool(xmlFile, baseName.."#rotMinClosesWoodClaw");
		if rotMinClosesWoodClaw ~= nil then
			entry.rotMinClosesWoodClaw = rotMinClosesWoodClaw;
			entry.rotMinBackup = entry.rotMin;
			entry.rotMaxBackup = entry.rotMax;
			entry.rotBackup = entry.curRot[entry.rotationAxis];
		end;
		if self.wc.balanceNode ~= nil then
			local x, y, z = worldDirectionToLocal(self.wc.balanceNode, 0, 1, 0);
			local hypotenuse = math.sqrt(y^2 + z^2);
			if hypotenuse ~= 0 then
				local rx, ry, rz = getRotation(self.wc.balanceNode);
				rx = rx + math.asin(z / hypotenuse);
				setRotation(self.wc.balanceNode, rx, ry, rz);
			end;
		end;
	end;
	
	if hasXMLProperty(xmlFile, "vehicle.woodClaw4.freeSwingCmpJntIndex#indices") then
		self.wc.freeSwingCmpJntIndices = {};
		local str = getXMLString(xmlFile, "vehicle.woodClaw4.freeSwingCmpJntIndex#indices");
		local strT = Utils.splitString(" ",str);
		for i,t in pairs(strT) do
			table.insert(self.wc.freeSwingCmpJntIndices, tonumber(t));
		end;
		self.wc.freeSwingCmpJntIndex = self.wc.freeSwingCmpJntIndices[1];
		self.wc.freeSwingMovingToolIndex = getXMLInt(xmlFile, "vehicle.woodClaw4.freeSwingCmpJntIndex#movingToolIndex");
		
		self.wc.freeSwingRun = 0;
		self.wc.freeSwingRunThresh = 3000;
		self.wc.freeSwing = false;
		self.wc.freeSwingSet = true;
	end;
	
	self.wc.noCollisionComponents = {};
	if hasXMLProperty(xmlFile, "vehicle.woodClaw4.noCollisionComponents") then
		local str = getXMLString(xmlFile, "vehicle.woodClaw4.noCollisionComponents#indices");
		if str ~= nil and str ~= '' then
			local strT = Utils.splitString(" ",str);
			for i,t in pairs(strT) do
				local ncc = self.components[tonumber(t)].node;
				if ncc ~= nil then
					table.insert(self.wc.noCollisionComponents, ncc);
				else
					print("ERROR :: WoodClaw :: Wrong noCollisionComponent given!");
				end;
			end;
		end;
	end;
	
	if hasXMLProperty(xmlFile, "vehicle.woodClaw4.raycast") then
		self.wc.raycastNode1 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.woodClaw4.raycast#node1"));
		self.wc.raycastNode2 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.woodClaw4.raycast#node2"));
		self.wc.raycastTopNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.woodClaw4.raycast#topNode"));
		self.wc.raycastBottomNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.woodClaw4.raycast#bottomNode"));
		self.wc.raycastDistance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.woodClaw4.raycast#distance"), 0.01);
		self.wc.raycastFoundTrunk = false;
	end;
	self.wc.raycastErrorRatio = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.woodClaw4.raycast#errorRatio"), 5);
	
	self.wc.gui = {};
	self.wc.gui.x = 0.9;
	self.wc.gui.y = 0.2;
	self.wc.gui.w = 128/g_screenWidth;
	self.wc.gui.h = 128/g_screenHeight;
	self.wc.guiClawOpen = Overlay:new("clawOpen", Utils.getFilename(getXMLString(xmlFile,"vehicle.woodClaw4.gui.clawOpenImg#fileName"), self.baseDirectory), self.wc.gui.x, self.wc.gui.y, self.wc.gui.w, self.wc.gui.h);
	self.wc.guiClawClosed = Overlay:new("clawClosed", Utils.getFilename(getXMLString(xmlFile,"vehicle.woodClaw4.gui.clawClosedImg#fileName"), self.baseDirectory), self.wc.gui.x, self.wc.gui.y, self.wc.gui.w, self.wc.gui.h);	
	if self.wc.freeSwingCmpJntIndices ~= nil then
		self.wc.guiRotorFree = Overlay:new("clawRotorFree", Utils.getFilename(getXMLString(xmlFile,"vehicle.woodClaw4.gui.rotorFreeImg#fileName"), self.baseDirectory), self.wc.gui.x, self.wc.gui.y+self.wc.gui.h, self.wc.gui.w, self.wc.gui.h);
		self.wc.guiRotorLocked = Overlay:new("clawRotorUnfree", Utils.getFilename(getXMLString(xmlFile,"vehicle.woodClaw4.gui.rotorLockedImg#fileName"), self.baseDirectory), self.wc.gui.x, self.wc.gui.y+self.wc.gui.h, self.wc.gui.w, self.wc.gui.h);
	end;
end;

function WoodClaw4:delete()
	for nodeId, v in pairs(self.wc.leftTriggers) do
		removeTrigger(nodeId);
	end;
	for nodeId, v in pairs(self.wc.rightTriggers) do
		removeTrigger(nodeId);
	end;
	
	removeTrigger(self.wc.middleTrunkTrigger);
	
	for a=1, table.getn(self.wc.trunkDetectTriggers) do
		removeTrigger(self.wc.trunkDetectTriggers[a]);
	end;

	if self.wc.guiClawOpen ~= nil then
		self.wc.guiClawOpen:delete();
	end;
	if self.wc.guiClawClosed ~= nil then
		self.wc.guiClawClosed:delete();
	end;
	if self.wc.freeSwingCmpJntIndices ~= nil then
		if self.wc.guiRotorFree ~= nil then
			self.wc.guiRotorFree:delete();
		end;
		if self.wc.guiRotorLocked ~= nil then
			self.wc.guiRotorLocked:delete();
		end;
	end;
end

function WoodClaw4:readStream(streamId, connection)
	self.wc.trunksAttached = streamReadBool(streamId);
end;

function WoodClaw4:writeStream(streamId, connection)	
	streamWriteBool(streamId, self.wc.trunksAttached)
end;

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

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

function WoodClaw4:update(dt)	
	if self:getIsActiveForInput() and not self:hasInputConflictWithSelection() then
		if self.wc.freeSwingCmpJntIndices ~= nil then
			if InputBinding.hasEvent(InputBinding.RELEASE_CLAW) then
				self:setFreeSwing(not self.wc.freeSwing);
			end;
		end;
	end;

	if self.isServer and self:getIsActive() then
		-- check wether the claw is opening or not
		local opened = true;		
		local closing = false;
		local opening = false;
		for a=1, table.getn(self.movingTools) do
			local entry = self.movingTools[a];
			if entry.rotMinClosesWoodClaw ~= nil then
				if entry.rotMinClosesWoodClaw then
					if entry.curRot[entry.rotationAxis] < entry.rotBackup then
						closing = true;
					elseif entry.curRot[entry.rotationAxis] > entry.rotBackup then
						opening = true;
					end;
					
					if entry.curRot[entry.rotationAxis] < entry.rotMax - math.rad(1) then
						opened = false;
					end;
				else
					if entry.curRot[entry.rotationAxis] < entry.rotBackup then
						opening = true;
					elseif entry.curRot[entry.rotationAxis] > entry.rotBackup then
						closing = true;
					end;
					
					if entry.curRot[entry.rotationAxis] > entry.rotMin + math.rad(1) then
						opened = false;
					end;
				end;
			end;
			entry.rotBackup = entry.curRot[entry.rotationAxis];
		end;
		
		local trunksInAllTriggers = false;
		if self.wc.leftTriggerTrunks[1] ~= nil and self.wc.leftTriggerTrunks[1].isTrunk then
			if self.wc.rightTriggerTrunks[1] ~= nil and self.wc.rightTriggerTrunks[1].isTrunk then
				if self.wc.middleTriggerTrunks[1] ~= nil then
					trunksInAllTriggers = true;
				end;
			end;
		end;
		
		-- check the claw for empty spaces with a raycast
		local numRCFails = 0;
		if self.wc.raycastNode1 ~= nil and self.wc.raycastNode2 ~= nil then
			local x1, y1, z1 = getWorldTranslation(self.wc.raycastNode1);
			local x2, y2, z2 = getWorldTranslation(self.wc.raycastNode2);
			local xt, yt, zt = getWorldTranslation(self.wc.raycastTopNode);
			local xb, yb, zb = getWorldTranslation(self.wc.raycastBottomNode);
			
			-- horizontal vector
			local dist = Utils.vector3Length(x1-x2, y1-y2, z1-z2);
			local rcStartX, rcStartY, rcStartZ = worldToLocal(self.wc.raycastTopNode, x1, y1, z1);
			local offsetX = 0
			if rcStartX >= 0 then
				offsetX = -1;
			else
				offsetX = 1;
			end;
			
			-- vertical vector
			local dirX, dirY, dirZ = localDirectionToWorld(self.wc.raycastTopNode, 0, -1, 0);
			local _, raycastLenght, _ = worldToLocal(self.wc.raycastTopNode, xb, yb, zb);
			raycastLenght = math.abs(raycastLenght);
			
			local checkedDist = 0;
			while checkedDist < dist do
				local rx, ry, rz = localToWorld(self.wc.raycastTopNode, rcStartX+(checkedDist*offsetX), 0, rcStartZ);				
				self.wc.raycastFoundTrunk = false;
				raycastAll(rx, ry, rz, dirX*raycastLenght, dirY*raycastLenght, dirZ*raycastLenght, "trunkRaycastCallback", raycastLenght, self);
				if not self.wc.raycastFoundTrunk then
					numRCFails = numRCFails + 1;
				end;
				checkedDist = checkedDist + self.wc.raycastDistance;				
				if Vehicle.debugRendering then
					if not self.wc.raycastFoundTrunk then
						drawDebugLine(rx, ry, rz, 1, 0, 1, rx+dirX*raycastLenght, ry+dirY*raycastLenght, rz+dirZ*raycastLenght, 1, 0, 0);
					else
						drawDebugLine(rx, ry, rz, 0, 1, 1, rx+dirX*raycastLenght, ry+dirY*raycastLenght, rz+dirZ*raycastLenght, 0, 1, 0);
					end;
					drawDebugPoint(x1, y1, z1, 1, 0, 0, 1);
					drawDebugPoint(x2, y2, z2, 1, 0, 0, 1);
					drawDebugPoint(xt, yt, zt, 1, 0, 0, 1);
					drawDebugPoint(xb, yb, zb, 1, 0, 0, 1);
				end;
			end;
		end;

		if table.getn(self.wc.triggerTrunksTotal) > 0 then
			if not self.wc.trunksAttached then
				if trunksInAllTriggers and numRCFails <= (self.wc.raycastErrorRatio*table.getn(self.wc.triggerTrunksTotal)) and closing and not opened then
					self:attachTrunks();
				end;
			else
				if opened or ((not trunksInAllTriggers or numRCFails > (self.wc.raycastErrorRatio*1.5)) and opening) then
					self:detachTrunks();
				end;
			end;
		end;
	end;
	
	-- free swing
	if self.wc.freeSwingCmpJntIndex ~= nil then
		if not self.wc.freeSwingSet then -- we have to set the swing free/unfree only once
			self.wc.freeSwingSet = true;
			if self.wc.freeSwing then
				if self.isServer then
					for i,t in pairs(self.wc.freeSwingCmpJntIndices) do
						setJointRotationLimit(self.componentJoints[t].jointIndex, 1, true, -math.rad(180), math.rad(180));
					end;
				end;				
			else
				local dx,dy,dz = localDirectionToWorld( self.components[self.wc.trunkAttachCompIndex].node, 1,0,0 );
				local lx,ly,lz = worldDirectionToLocal( self.componentJoints[self.wc.freeSwingCmpJntIndex].jointNode, dx,dy,dz );

				local alpha = math.atan2(lz, lx);
				local rx,ry,rz = getRotation(self.componentJoints[self.wc.freeSwingCmpJntIndex].jointNode);
				
				setRotation(self.componentJoints[self.wc.freeSwingCmpJntIndex].jointNode, 0, ry-alpha,0);
				
				self.movingTools[self.wc.freeSwingMovingToolIndex].curRot[2] = ry-alpha;
				
				if self.isServer then
					for i,t in pairs(self.wc.freeSwingCmpJntIndices) do
						setJointRotationLimit(self.componentJoints[t].jointIndex, 1, true, 0, 0);
						setJointFrame(self.componentJoints[t].jointIndex, 0, self.componentJoints[t].jointNode);
					end;
				end;
			end;
		end;
	end;
end;

function WoodClaw4:updateTick(dt)
	if self.wc.balanceNode ~= nil and self:getIsActive() then -- update balance node
		local x, y, z = worldDirectionToLocal(self.wc.balanceNode, 0, 1, 0);
		local hypotenuse = math.sqrt(y^2 + z^2);
		if hypotenuse ~= 0 then
			local rx, ry, rz = getRotation(self.wc.balanceNode);
			rx = rx + math.asin(z / hypotenuse);
			setRotation(self.wc.balanceNode, rx, ry, rz);
		end;
	end;
end;

function WoodClaw4:draw()
	if self.wc.freeSwingCmpJntIndex ~= nil and self:getIsActiveForInput() then
		if self.wc.freeSwing then
			self:addHelpButtonTextWithCorrectMouseButtons("RELEASE_CLAW", InputBinding.RELEASE_CLAW, "setSwingUnfree");
		else
			self:addHelpButtonTextWithCorrectMouseButtons("RELEASE_CLAW", InputBinding.RELEASE_CLAW, "setSwingFree");
		end;
	end;
	
	if self.wc.trunksAttached then
		self.wc.guiClawClosed:render();
	else
		self.wc.guiClawOpen:render();
	end;
	if self.wc.freeSwingCmpJntIndices ~= nil then
		if not self.wc.freeSwing then
			self.wc.guiRotorLocked:render();
		else
			self.wc.guiRotorFree:render();
		end;
	end;
end;

function WoodClaw4:addHelpButtonTextWithCorrectMouseButtons(inputBindingName, inputBinding, text)
	local inputBindingActionIndex = InputBinding[inputBindingName];
	local isMouseButton = false;
	local buttonName = nil;
	
	if inputBindingActionIndex ~= nil then
		if InputBinding.actions[inputBindingActionIndex] ~= nil then
			local buttonIndex = InputBinding.actions[inputBindingActionIndex].mouseButtons[1];
			if buttonIndex ~= nil then
				if buttonIndex == 1 then
					isMouseButton = true;
					buttonName = g_i18n:getText("leftMouseButton");
				end;
				if buttonIndex == 2 then
					isMouseButton = true;
					buttonName = g_i18n:getText("middleMouseButton");
				end;
				if buttonIndex == 3 then
					isMouseButton = true;
					buttonName = g_i18n:getText("rightMouseButton");
				end;
			end;
		end;
	end;
	
	if isMouseButton then
		g_currentMission:addExtraPrintText(buttonName..": "..g_i18n:getText(text));
	else
		g_currentMission:addHelpButtonText(g_i18n:getText(text), inputBinding);
	end;
end;	

function WoodClaw4:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay)
	if g_currentMission.treeManager ~= nil then
		local trunk = g_currentMission.treeManager.nodeIdToTrunk[otherId];
		if trunk ~= nil then
			if onEnter then
				if self.wc.rightTriggers[triggerId] then
					table.insert(self.wc.rightTriggerTrunks, trunk);
				elseif self.wc.leftTriggers[triggerId] then
					table.insert(self.wc.leftTriggerTrunks, trunk);
				elseif triggerId == self.wc.middleTrunkTrigger then
					table.insert(self.wc.middleTriggerTrunks, trunk);
				end;
			elseif onLeave then
				if self.wc.rightTriggers[triggerId] then
					for a=1, table.getn(self.wc.rightTriggerTrunks) do
						if self.wc.rightTriggerTrunks[a] == trunk then
							table.remove(self.wc.rightTriggerTrunks, a);
							break;
						end;
					end;
				elseif self.wc.leftTriggers[triggerId] then
					for a=1, table.getn(self.wc.leftTriggerTrunks) do
						if self.wc.leftTriggerTrunks[a] == trunk then
							table.remove(self.wc.leftTriggerTrunks, a);
							break;
						end;
					end;
				elseif triggerId == self.wc.middleTrunkTrigger then
					for a=1, table.getn(self.wc.middleTriggerTrunks) do
						if trunk == self.wc.middleTriggerTrunks[a] then
							table.remove(self.wc.middleTriggerTrunks, a);
							break;
						end;
					end;
				end;
			end;
		end;
	end;
end;

function WoodClaw4:trunkDetectTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay)
	if g_currentMission.treeManager ~= nil then
		local trunk = g_currentMission.treeManager.nodeIdToTrunk[otherId];
		if trunk ~= nil and not trunk.isRoot then
			if onEnter then
				insert = true;
				for a=1, table.getn(self.wc.triggerTrunksTotal) do
					if trunk == self.wc.triggerTrunksTotal[a] then
						insert = false;
						break;
					end;
				end;
				if insert then
					trunk.woodClaw = self;
					table.insert(self.wc.triggerTrunksTotal, trunk);
				end;	
			end;	

			if onLeave then 
				for a=1, table.getn(self.wc.triggerTrunksTotal) do		
					if trunk == self.wc.triggerTrunksTotal[a] then
						table.remove(self.wc.triggerTrunksTotal, a);
						trunk.woodClaw = nil;
					end;
				end;
			end;
		end;
	end;
end;

function WoodClaw4:attachTrunks(noEventSend) 
	if self.isServer then
		g_server:broadcastEvent(WoodClawAttachEvent:new(self, true), nil, nil, object); 
	end;
	
	if self.isServer then
		for a=1, table.getn(self.wc.triggerTrunksTotal) do 
			local trunk = self.wc.triggerTrunksTotal[a];
			if trunk.woodClaw == self then
				local sx, sy, sz = getScale(trunk.visNode);
				local constr = JointConstructor:new();
				
				constr:setActors(self.components[self.wc.trunkAttachCompIndex].node, trunk.nodeId);
				constr:setJointTransforms(self.wc.trunkJointNode, self.wc.trunkJointNode);

				for i=1, 3 do
					local rotLimit = self.wc.trunkJointRotLimit[i];
					constr:setRotationLimit(i-1, -rotLimit, rotLimit);
					constr:setTranslationLimit(i-1, true, 0, 0);
				end;

				if trunk.trunkJointIndices == nil then
					trunk.trunkJointIndices = {};
				end;			
				local e = {joint = constr:finalize(), attacher = self};
				table.insert(trunk.trunkJointIndices, e);
				table.insert(self.wc.attachedTrunks, trunk);
				trunk.isAttached = true;	
			end;					
		end;
		
		-- trunks need to be attached to set collision pairs
		for a=1, table.getn(self.wc.triggerTrunksTotal) do
			for b=1, table.getn(self.wc.triggerTrunksTotal) do
				if a ~= b then
					local joint = {}
					joint.actor1 = self.wc.triggerTrunksTotal[a].nodeId;
					joint.actor2 = self.wc.triggerTrunksTotal[b].nodeId;
					
					local constr = JointConstructor:new();
				
					constr:setActors(joint.actor1, joint.actor2);
					constr:setJointTransforms(self.wc.trunkJointNode, self.wc.trunkJointNode);

					for i=1, 3 do
						constr:setRotationLimit(i-1, 0, 0);
						constr:setTranslationLimit(i-1, true, 0, 0);
					end;
					
					setPairCollision(joint.actor1, joint.actor2, false);

					joint.jointIndex = constr:finalize();
					table.insert(self.wc.trunkPairJoints, joint);
				end;
			end;
		end;
		
		for i, ncc in pairs(self.wc.noCollisionComponents) do
			for i,t1 in pairs(self.wc.attachedTrunks) do
				setPairCollision(t1.nodeId, ncc, false);
			end;		
		end;
	end;
		
	for a=1, table.getn(self.movingTools) do
		local entry = self.movingTools[a];
		if entry.rotMinClosesWoodClaw ~= nil then
			local x, y, z = getRotation(entry.node);
			local tab = {x, y, z}
			
			if entry.rotMinClosesWoodClaw then
				entry.rotMin = tab[entry.rotationAxis];
			else
				entry.rotMax = tab[entry.rotationAxis];
			end;
		end;
	end;
		
	self.wc.trunksAttached = true;
end;

function WoodClaw4:detachTrunks(noEventSend)
	if self.isServer then
		g_server:broadcastEvent(WoodClawAttachEvent:new(self, false), nil, nil, object); 
	end;
	
	if self.isServer then
		for i, ncc in pairs(self.wc.noCollisionComponents) do
			for i,t1 in pairs(self.wc.attachedTrunks) do
				setPairCollision(t1.nodeId, ncc, true);
			end;		
		end;
	
		while table.getn(self.wc.attachedTrunks) > 0 do
			for i,trunk in pairs(self.wc.attachedTrunks) do
				for i,j in pairs(trunk.trunkJointIndices) do
					if j.attacher == self then
						removeJoint(j.joint);
						table.remove(trunk.trunkJointIndices, i);
						break;
					end;
				end;						
			
				trunk.isAttached = false;
				table.remove(self.wc.attachedTrunks, i);
			end;
		end;
		
		for a=1, table.getn(self.wc.trunkPairJoints) do
			local joint = self.wc.trunkPairJoints[a];
			setPairCollision(joint.actor1, joint.actor2, true);
			removeJoint(joint.jointIndex);
		end;
		
		self.wc.trunkPairJoints = {}
	end;
	
	self.wc.trunksAttached = false;	
	
	g_currentMission.treeManager.forrestModTutorial:checkShowTutorial("woodChipVsSawMill");
	
	for a=1, table.getn(self.movingTools) do
		local entry = self.movingTools[a];
		if entry.rotMinClosesWoodClaw ~= nil then
			entry.rotMin = entry.rotMinBackup;
			entry.rotMax = entry.rotMaxBackup;
		end;
	end;
end;

function WoodClaw4:setFreeSwing(state, noEventSend)
	if not noEventSend then
		SetFreeSwingEvent.sendEvent(self, state, noEventSend);
	end;
	self.wc.freeSwing = state;
	self.wc.freeSwingSet = false;
end;

function WoodClaw4:getIsClawClosed()
	local isClosed = true;
	
	for a=1, table.getn(self.movingTools) do
		local entry = self.movingTools[a];
		if entry.rotMinClosesWoodClaw ~= nil then
			if entry.rotMinClosesWoodClaw then
				if entry.rotMin ~= entry.curRot[entry.rotationAxis] then
					isClosed = false;
				end;
			else
				if entry.rotMax ~= entry.curRot[entry.rotationAxis] then
					isClosed = false;
				end;
			end;
		end;
	end;

	return isClosed;
end;

function WoodClaw4:stopDamageCalculation(objectId, otherObjectId, isStart, normalForce, tangentialForce, vehicle)
	if g_currentMission.treeManager ~= nil then
		if g_currentMission.treeManager.nodeIdToTrunk ~= nil then
			if g_currentMission.treeManager.nodeIdToTrunk[otherObjectId] ~= nil then
				return false;
			end;
		end;
	end;
	return true;
end;

function WoodClaw4:trunkRaycastCallback(hitObjectId, x, y, z, distance, nx, ny, nz)
	if g_currentMission.treeManager ~= nil then
		if g_currentMission.treeManager.nodeIdToTrunk[hitObjectId] ~= nil then
			if g_currentMission.treeManager.nodeIdToTrunk[hitObjectId].woodClaw ~= nil then
				self.wc.raycastFoundTrunk = true;
			end;
		end;
	end;
end;