--
-- ExtendedDynamicMountAttacher
-- Class for baleforks which got more than one trigger (max 4 triggers, is there a way to dynamically add triggers?) 
--
-- @author  Stefan Maurus	
-- @date  2015-05-10
-- @version  1.0

-- Copyright © Stefan Maurus, www.stefanmaurus.de


ExtendedDynamicMountAttacher = {};

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

function ExtendedDynamicMountAttacher:load(xmlFile)
	self.dynamicMountAllTriggers = SpecializationUtil.callSpecializationsFunction("dynamicMountAllTriggers");

	self.dynamicMountTrigger1CallbackEnter = SpecializationUtil.callSpecializationsFunction("dynamicMountTrigger1CallbackEnter");
	self.dynamicMountTrigger2CallbackEnter = SpecializationUtil.callSpecializationsFunction("dynamicMountTrigger2CallbackEnter");
	self.dynamicMountTrigger3CallbackEnter = SpecializationUtil.callSpecializationsFunction("dynamicMountTrigger3CallbackEnter");
	self.dynamicMountTrigger4CallbackEnter = SpecializationUtil.callSpecializationsFunction("dynamicMountTrigger4CallbackEnter");
	
	self.dynamicMountTrigger1CallbackLeave = SpecializationUtil.callSpecializationsFunction("dynamicMountTrigger1CallbackLeave");
	self.dynamicMountTrigger2CallbackLeave = SpecializationUtil.callSpecializationsFunction("dynamicMountTrigger2CallbackLeave");
	self.dynamicMountTrigger3CallbackLeave = SpecializationUtil.callSpecializationsFunction("dynamicMountTrigger3CallbackLeave");
	self.dynamicMountTrigger4CallbackLeave = SpecializationUtil.callSpecializationsFunction("dynamicMountTrigger4CallbackLeave");	

	self.addMountedObjectToAnother = SpecializationUtil.callSpecializationsFunction("addMountedObjectToAnother");
	
	self.addDynamicMountedObject = SpecializationUtil.callSpecializationsFunction("addDynamicMountedObject");
	self.updateDynamicMountedObjects = SpecializationUtil.callSpecializationsFunction("updateDynamicMountedObjects");
	self.removeDynamicMountedObject = SpecializationUtil.callSpecializationsFunction("removeDynamicMountedObject");
	
	self.removeFromPending = SpecializationUtil.callSpecializationsFunction("removeFromPending");
	self.addPendingObject = SpecializationUtil.callSpecializationsFunction("addPendingObject");
	
	self.mountDynamicWM = ExtendedDynamicMountAttacher.mountDynamicWM;
	self.mountBaleToBale = ExtendedDynamicMountAttacher.mountBaleToBale;
	
	self.groundContactForce = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.dynamicMountAttacher#force"), 2);
	self.allowStacking = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.dynamicMountAttacher#allowStacking"), false);
	self.stackHeight = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.dynamicMountAttacher#stackHeight"), 0);
	self.mountAttacherDirty = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.dynamicMountAttacher#isActiveDirty"), false);
	self.mountAttacherAnimationDirty = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.dynamicMountAttacher#isAnimationDirty"), false);
	self.mountAttacherMovingtoolDirty = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.dynamicMountAttacher#isMovingToolDirty"), false);
	self.mountDependsOnTwoTriggers = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.dynamicMountAttacher#mountDependsOnTwoTriggers"), false);
	self.forkIsInverted = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.dynamicMountAttacher#forkIsInverted"), false);
	
	self.dynamicMountTriggers = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.dynamicMountAttacher.trigger(%d)", i);
		if not hasXMLProperty(xmlFile, key) then
			break;
		end;
		if self.isServer then
			local attacherTriggerTriggerNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#enterTrigger"));
			local leaveTriggerTriggerNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#leaveTrigger"));
			local attacherTriggerTriggerNode2 = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#enterTrigger2"));
			local leaveTriggerTriggerNode2 = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#leaveTrigger2"));				
			local attacherTriggerRootNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#rootNode"));
			local attacherTriggerJointNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#jointNode"));
			
			local timeToAttach = Utils.getNoNil(getXMLFloat(xmlFile, key.."#timeToAttach"), 500);
			addTrigger(attacherTriggerTriggerNode, string.format("dynamicMountTrigger%dCallbackEnter", i+1), self);
			addTrigger(leaveTriggerTriggerNode, string.format("dynamicMountTrigger%dCallbackLeave", i+1), self);
			
			addTrigger(attacherTriggerTriggerNode2, string.format("dynamicMountTrigger%dCallbackEnter2", i+1), self);
			addTrigger(leaveTriggerTriggerNode2, string.format("dynamicMountTrigger%dCallbackLeave2", i+1), self);
			
			local dynamicMountAttacherTrigger = {triggerNode=attacherTriggerTriggerNode, triggerNode2=attacherTriggerTriggerNode2, rootNode=attacherTriggerRootNode, jointNode=attacherTriggerJointNode, leaveTrigger=leaveTriggerTriggerNode, leaveTrigger2=leaveTriggerTriggerNode2};

			table.insert(self.dynamicMountTriggers, {dynamicMountAttacherTrigger=dynamicMountAttacherTrigger, timeToAttach=timeToAttach});
		end;
		i=i+1;
	end;
	
	self.dynamicMountedObjectsOnAnother={};
	
	self.dynamicMountedObjects={};
	self.dynamicMountedTypes={};	
	self.dynamicMountedTriggers={};

	self.pendingDynamicMountObjects={};
	self.pendingDynamicMountTriggers={};	
	self.pendingDynamicMountTimes = {};
	
	self.debugMode = false;
end;

function ExtendedDynamicMountAttacher:delete()
    if self.isServer then
		for _,object in pairs(self.dynamicMountedObjects) do
			object:unmountDynamic();
		end
    end
	
	for _,trigger in pairs(self.dynamicMountTriggers) do
		removeTrigger(trigger.dynamicMountAttacherTrigger.triggerNode);
		removeTrigger(trigger.dynamicMountAttacherTrigger.leaveTrigger);
	end		
end;

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

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

function ExtendedDynamicMountAttacher:update(dt)
	local isDirty = self.mountAttacherDirty;
	
	if self.movingTools ~= nil and self.mountAttacherMovingtoolDirty then
		for i, v in ipairs(self.movingTools) do
			if v.lastRotSpeed ~= 0 or v.lastTransSpeed ~= 0 then
				isDirty = true;
			end;
		end;
	end;
	
	if self.animations ~= nil and self.mountAttacherAnimationDirty then
		for i, v in pairs(self.activeAnimations) do
			isDirty = true; break;
		end;	
	end;
	
	if isDirty then
		self:updateDynamicMountedObjects();
	end;
end;

function ExtendedDynamicMountAttacher:updateTick(dt)
    if self.isServer then
		for v,object in ipairs(self.pendingDynamicMountObjects) do
			if self.pendingDynamicMountTimes[v] ~= nil then
				self.pendingDynamicMountTimes[v] = self.pendingDynamicMountTimes[v] + dt;
			end;

			if not Utils.hasListElement(self.dynamicMountedObjects, object) then -- is not already mounted
				local trigger = self.dynamicMountTriggers[self.pendingDynamicMountTriggers[v]];
				
				if trigger.timeToAttach < self.pendingDynamicMountTimes[v] then
					if object.dynamicMountObject ~= nil then
						object:unmountDynamic(); 	
					end;
					
					if object:mountDynamic(self, trigger.dynamicMountAttacherTrigger.rootNode, trigger.dynamicMountAttacherTrigger.jointNode, DynamicMountUtil.TYPE_FORK, 10) then
						self:addDynamicMountedObject(object);
						table.insert(self.dynamicMountedTriggers, Utils.findListElementFirstIndex(self.dynamicMountTriggers, trigger, nil));
						table.insert(self.dynamicMountedTypes, DynamicMountUtil.TYPE_FORK);
						
						if self.allowStacking then
							local baleRaycastResult = {
								raycastCallback = function (self, transformId, x, y, z, distance)
									if getUserAttribute(transformId,"baleMeshIndex") ~= nil and transformId ~= self.baleBase then
										local alreadyFixed = false;
										for v,object in ipairs(self.slf.dynamicMountedObjectsOnAnother) do
											if g_currentMission:getNodeObject(transformId) == object then
												alreadyFixed = true;
											end;
										end;
										
										if not alreadyFixed then
											local object = g_currentMission:getNodeObject(transformId);
											if object.dynamicMountObject ~= nil then
												object:unmountDynamic(); 
											end;
											
											local bale = g_currentMission:getNodeObject(self.baleBase);
											if self.slf:mountBaleToBale(bale, object, self.baleBase, self.baleBase) then
												self.slf:addMountedObjectToAnother(object);
											end;
										end;
									end;
								end
							};
							baleRaycastResult.baleBase = object.nodeId;
							baleRaycastResult.slf = self;
							
							if getUserAttribute(object.nodeId,"isRoundbale") ~= nil then
								local si = math.sin(math.rad(45)) * object.baleDiameter / 2;
								local x, y, z = localToWorld(object.nodeId, si, si, 0);
								raycastAll(x, y, z, 0, 1, 0, "raycastCallback", self.stackHeight, baleRaycastResult, 16777216);
								local x, y, z = localToWorld(object.nodeId, -si, si, 0);
								raycastAll(x, y, z, 0, 1, 0, "raycastCallback", self.stackHeight, baleRaycastResult, 16777216);		
								
								local x, y, z = localToWorld(object.nodeId, si, -si, 0);
								raycastAll(x, y, z, 0, 1, 0, "raycastCallback", self.stackHeight, baleRaycastResult, 16777216);
								local x, y, z = localToWorld(object.nodeId, -si, -si, 0);
								raycastAll(x, y, z, 0, 1, 0, "raycastCallback", self.stackHeight, baleRaycastResult, 16777216);		
							else
								local x, y, z = getWorldTranslation(object.nodeId);
								raycastAll(x, y, z, 0, 1, 0, "raycastCallback", self.stackHeight, baleRaycastResult, 16777216);		
							end;
						end;
					end;
				end
			end
		end
		
		for v,object in ipairs(self.dynamicMountedObjectsOnAnother) do
			if object.dynamicMountObject ~= nil then
				if object.dynamicMountObject.dynamicMountObject == nil then
					removeJoint(object.dynamicMountJointIndex);
					object.dynamicMountJointIndex = nil;
					object.dynamicMountObjectActorId = nil;
					object.dynamicMountObject = nil;
					
					local index = Utils.findListElementFirstIndex(self.dynamicMountedObjectsOnAnother, object, nil);
					if index ~= nil then
						table.remove(self.dynamicMountedObjectsOnAnother, index);
					end;					
				end;
			end;
		end;
		
		for v,object in ipairs(self.dynamicMountedObjects) do
			local trigger = self.dynamicMountTriggers[self.dynamicMountedTriggers[v]];
			
			local objectRaycastResult = {
				raycastCallback = function (self, transformId, x, y, z, distance)
					if g_currentMission.nodeToVehicle[transformId] ~= self.baleforkId then
						if self.nodeId ~= transformId then
							if distance == 0 then
								distance = 10000;
							end;
							self.groundDistance = distance;
							if self.baleforkId.debugMode then
								drawDebugPoint(x, y, z, 0,1,0,1);
							end;
						end;
					end;
				end
			};
			objectRaycastResult.groundDistance = 10000;
			objectRaycastResult.nodeId = object.nodeId;
			objectRaycastResult.baleforkId = self;
			
			local baleRaycastResult = {
				raycastCallback = function (self, transformId, x, y, z, distance)
					if self.nodeId == transformId then
						self.groundDistance = distance;
					end;
				end
			};
			baleRaycastResult.nodeId = object.nodeId;
			baleRaycastResult.groundDistance = 0;
			
			local x,y,z = getWorldTranslation(object.nodeId);
			local h = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, y);
			
			raycastAll(x, h, z, 0, 1, 0, "raycastCallback", y-h, baleRaycastResult, 16777216);
			baleHigh = math.abs(y-(h+baleRaycastResult.groundDistance));
			
			raycastClosest(x, y-baleHigh+0.1, z, 0, -1, 0, "raycastCallback", 1, objectRaycastResult, 16781790);

			if self.debugMode then
				drawDebugLine(x, y, z,1,0,0,x,0,z,1,0,0);
				drawDebugPoint(x, y-baleHigh+0.1, z, 0,1,0,1);
				drawDebugPoint(x, y-baleHigh+0.1-objectRaycastResult.groundDistance, z, 0,0,1,1);
			end;
			
			local currentLimit = 0.20;
			local groundDistanceScanned = objectRaycastResult.groundDistance;
			
			if groundDistanceScanned > 0.2 then
				if object.baleDiameter == nil then
					local x = {}; local y = {}; local z = {};
					x[1], y[1], z[1] = localToWorld(object.nodeId, object.baleWidth/2, -object.baleHeight/2 , object.baleLength/2);
					x[2], y[2], z[2] = localToWorld(object.nodeId, object.baleWidth/2, -object.baleHeight/2 , -object.baleLength/2);
					x[3], y[3], z[3] = localToWorld(object.nodeId, -object.baleWidth/2, -object.baleHeight/2 , object.baleLength/2);
					x[4], y[4], z[4] = localToWorld(object.nodeId, -object.baleWidth/2, -object.baleHeight/2 , -object.baleLength/2);

					x[5], y[5], z[5] = localToWorld(object.nodeId, object.baleWidth/2, object.baleHeight/2 , object.baleLength/2);
					x[6], y[6], z[6] = localToWorld(object.nodeId, object.baleWidth/2, object.baleHeight/2 , -object.baleLength/2);
					x[7], y[7], z[7] = localToWorld(object.nodeId, -object.baleWidth/2, object.baleHeight/2 , object.baleLength/2);
					x[8], y[8], z[8] = localToWorld(object.nodeId, -object.baleWidth/2, object.baleHeight/2 , -object.baleLength/2);			

					local l = math.min(y[1],y[2],y[3],y[4],y[5],y[6],y[7],y[8]);
					for i = 0, 8 do
						if y[i] == l then
							if self.debugMode then	
								drawDebugPoint(x[i], y[i], z[i], 1,0,0,1);
							end;
							raycastClosest(x[i], y[i], z[i], 0, -1, 0, "raycastCallback", 1, objectRaycastResult, 16781790);
							
							groundDistanceScanned = objectRaycastResult.groundDistance;
							currentLimit = 0.10;
						end;
					end;
				else
					local si_30 = math.sin(math.rad(30)) * (object.baleDiameter/2);
					local co_30 = math.cos(math.rad(30)) * (object.baleDiameter/2);
					
					local si_60 = math.sin(math.rad(60)) * (object.baleDiameter/2);
					local co_60 = math.cos(math.rad(60)) * (object.baleDiameter/2);
					
					local positions = {};
					
					positions[1] = {x=si_30,y=co_30};
					positions[2] = {x=si_30,y=-co_30};
					positions[3] = {x=-si_30,y=co_30};					
					positions[4] = {x=-si_30,y=-co_30};
					
					positions[5] = {x=object.baleDiameter/2,y=0};
					positions[6] = {x=0,y=object.baleDiameter/2};
					positions[7] = {x=-object.baleDiameter/2,y=0};
					positions[8] = {x=0,y=-object.baleDiameter/2};
					
					positions[9] = {x=si_60,y=co_60};
					positions[10] = {x=si_60,y=-co_60};
					positions[11] = {x=-si_60,y=co_60};					
					positions[12] = {x=-si_60,y=-co_60};
					
					for j = 1, 2 do
						local width = object.baleWidth/2-0.1;
						if j == 2 then
							width = -width;
						end;
						
						local lowestPositions = {};
						local x, y, z = localToWorld(object.nodeId,  positions[1].x, positions[1].y, width);
						lowestPositions[1] = {x=x, y=y, z=z}; 
						local x, y, z = localToWorld(object.nodeId,  positions[2].x, positions[2].y, width);
						lowestPositions[2] = {x=x, y=y, z=z}; 
						local x, y, z = localToWorld(object.nodeId,  positions[3].x, positions[3].y, width);
						lowestPositions[3] = {x=x, y=y, z=z}; 
						local x, y, z = localToWorld(object.nodeId,  positions[4].x, positions[4].y, width);
						lowestPositions[4] = {x=x, y=y, z=z}; 
						
						for i = 5, 12 do
							local x, y, z = localToWorld(object.nodeId,  positions[i].x, positions[i].y, width);
							local m = math.max(lowestPositions[1].y, lowestPositions[2].y, lowestPositions[3].y, lowestPositions[4].y);
							if y < m then
								for j = 1, 4 do
									if lowestPositions[j].y == m then
										lowestPositions[j] = {x=x, y=y, z=z}; 
									end;
								end;
							end;
						end;
						
						local lowestDis = 1000;
						
						for i = 1, 4 do
							if self.debugMode then
								drawDebugPoint(lowestPositions[i].x, lowestPositions[i].y, lowestPositions[i].z, 1,0,0,1);
							end;
							
							raycastClosest(lowestPositions[i].x, lowestPositions[i].y, lowestPositions[i].z, 0, -1, 0, "raycastCallback", 1, objectRaycastResult, 16781790);
							
							lowestDis = math.min(lowestDis, objectRaycastResult.groundDistance);
							
							groundDistanceScanned = objectRaycastResult.groundDistance;
						end;
						
						groundDistanceScanned = lowestDis;
						currentLimit = 0.10;
					end;
				end;
			end;
			
			local z = 1;
			if self.forkIsInverted then
				z = -1;
			end;
			
			local _, y, _ = getWorldTranslation(self.components[1].node);
			local _, y1, _ = localToWorld(self.components[1].node, 0, 0, z);

			local isNeg = y > y1+0.2;
			
			if groundDistanceScanned > currentLimit and not isNeg then
				if self.dynamicMountedTypes[v] == DynamicMountUtil.TYPE_FORK then
					removeJoint(object.dynamicMountJointIndex);
					object.dynamicMountJointIndex = nil;
					object.dynamicMountObjectActorId = nil;
					object.dynamicMountObject = nil;	

					local b = self:mountDynamicWM(object, trigger.dynamicMountAttacherTrigger.rootNode, trigger.dynamicMountAttacherTrigger.jointNode);

					self.dynamicMountedTypes[v] = DynamicMountUtil.TYPE_AUTO_ATTACH_XYZ;
				end;
			else
				if self.dynamicMountedTypes[v] == DynamicMountUtil.TYPE_AUTO_ATTACH_XYZ then
					removeJoint(object.dynamicMountJointIndex);
					object.dynamicMountJointIndex = nil;
					object.dynamicMountObjectActorId = nil;
					object.dynamicMountObject = nil;	
					
					local b = object:mountDynamic(self, trigger.dynamicMountAttacherTrigger.rootNode, trigger.dynamicMountAttacherTrigger.jointNode, DynamicMountUtil.TYPE_FORK, self.groundContactForce);
					
					self.dynamicMountedTypes[v] = DynamicMountUtil.TYPE_FORK;
				end;
			end;
		end;
    end
end;

function ExtendedDynamicMountAttacher:addMountedObjectToAnother(object)
	if not Utils.hasListElement(self.dynamicMountedObjectsOnAnother, object) then
		table.insert(self.dynamicMountedObjectsOnAnother, object);
	end;
end

function ExtendedDynamicMountAttacher:addDynamicMountedObject(object)
	if not Utils.hasListElement(self.dynamicMountedObjects, object) then
		table.insert(self.dynamicMountedObjects, object);
	end;
	
	for i = 0, 4 do 
		local index = Utils.findListElementFirstIndex(self.pendingDynamicMountObjects, object, nil);
		if index ~= nil then
			self:removeFromPending(index);
		end;	
		if not self.mountDependsOnTwoTriggers then
			break;
		end;
	end;
end

function ExtendedDynamicMountAttacher:updateDynamicMountedObjects()
	if self.isServer then
		for i,object in ipairs(self.dynamicMountedObjects) do
			setJointFrame(object.dynamicMountJointIndex, 0, object.jointNode);
		end;
	end;
end

function ExtendedDynamicMountAttacher:removeDynamicMountedObject(object, isDeleting)
	local index = Utils.findListElementFirstIndex(self.dynamicMountedObjects, object, nil);
	if index ~= nil then
		table.remove(self.dynamicMountedObjects, index);
		table.remove(self.dynamicMountedTriggers, index);	
		table.remove(self.dynamicMountedTypes, index);			
	end;

    if isDeleting then
		for i = 0, 4 do 
			local index = Utils.findListElementFirstIndex(self.pendingDynamicMountObjects, object, nil);
			if index ~= nil then
				self:removeFromPending(index);
			end;
			if not self.mountDependsOnTwoTriggers then
				break;
			end;
		end;
    end
end

function ExtendedDynamicMountAttacher:draw()
	if self.debugMode then
		for i,object in ipairs(self.dynamicMountedTypes) do
			renderText(0.5, 0.05*i, 0.02, i.." "..tostring(object));
		end;

		for i,object in ipairs(self.dynamicMountedTriggers) do
			renderText(0.4, 0.05*i, 0.02, i.." "..tostring(object));
		end;

		for i,object in ipairs(self.dynamicMountedObjects) do
			renderText(0.3, 0.05*i, 0.02, i.." "..tostring(object));
		end;
		
		for i,object in ipairs(self.pendingDynamicMountObjects) do
			renderText(0.2, 0.05*i, 0.02, i.." "..tostring(object));
		end;
		
		for i,object in ipairs(self.pendingDynamicMountTriggers) do
			renderText(0.1, 0.05*i, 0.02, tostring(object));
		end;
		
		for i,v in ipairs(self.pendingDynamicMountTimes) do
			renderText(0.02, 0.05*i, 0.02, v);
		end;
		
		for i,v in ipairs(self.dynamicMountedObjectsOnAnother) do
			renderText(0.06, 0.05*i, 0.02, v);
		end;		
	end;
end;

function ExtendedDynamicMountAttacher:dynamicMountAllTriggers(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId, i)
    if onEnter and getUserAttribute(otherActorId,"baleMeshIndex") ~= nil then
        local object = Utils.getNoNil(g_currentMission:getNodeObject(otherActorId), g_currentMission.nodeToVehicle[otherActorId]); --get object
		
        if object ~= nil and object.getSupportsMountDynamic ~= nil and object:getSupportsMountDynamic() and object.lastMoveTime ~= nil then -- is bale?
			if not Utils.hasListElement(self.pendingDynamicMountObjects, object) and not Utils.hasListElement(self.dynamicMountedObjects, object) then -- not already pending
				self:addPendingObject(object, i);
			elseif Utils.hasListElement(self.pendingDynamicMountObjects, object) and self.mountDependsOnTwoTriggers then
				local index = Utils.findListElementFirstIndex(self.pendingDynamicMountObjects, object, nil);
				if self.pendingDynamicMountTriggers[index] ~= i then
					self:addPendingObject(object, i);
				end;
			end;
        end
    elseif onLeave then
        local object = Utils.getNoNil(g_currentMission:getNodeObject(otherActorId), g_currentMission.nodeToVehicle[otherActorId]); --get object
		
        if object ~= nil then
			for c,objectPending in ipairs(self.pendingDynamicMountObjects) do
				if objectPending == object then
					self:removeFromPending(c);
				end;
			end
			
			local index = Utils.findListElementFirstIndex(self.dynamicMountedObjects, object, nil);
			if index ~= nil and self.dynamicMountedTriggers[index] == i then -- remove it it is mounted and leaves the right trigger
				self:removeDynamicMountedObject(object, false); 
				object:unmountDynamic();
			end;
        end
    end
end;

--enter trigger only calls if a object enters

function ExtendedDynamicMountAttacher:dynamicMountTrigger1CallbackEnter(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
    self:dynamicMountAllTriggers(triggerId, otherActorId, onEnter, false, false, otherShapeId, 1);
end;

function ExtendedDynamicMountAttacher:dynamicMountTrigger2CallbackEnter(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
    self:dynamicMountAllTriggers(triggerId, otherActorId, onEnter, false, false, otherShapeId, 2);
end;

function ExtendedDynamicMountAttacher:dynamicMountTrigger3CallbackEnter(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
    self:dynamicMountAllTriggers(triggerId, otherActorId, onEnter, false, false, otherShapeId, 3);
end;

function ExtendedDynamicMountAttacher:dynamicMountTrigger4CallbackEnter(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
    self:dynamicMountAllTriggers(triggerId, otherActorId, onEnter, false, false, otherShapeId, 4);
end;

--leave triggers only call if a object leaves

function ExtendedDynamicMountAttacher:dynamicMountTrigger1CallbackLeave(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
    self:dynamicMountAllTriggers(triggerId, otherActorId, false, onLeave, false, otherShapeId, 1);
end;

function ExtendedDynamicMountAttacher:dynamicMountTrigger2CallbackLeave(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
    self:dynamicMountAllTriggers(triggerId, otherActorId, false, onLeave, false, otherShapeId, 2);
end;

function ExtendedDynamicMountAttacher:dynamicMountTrigger3CallbackLeave(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
    self:dynamicMountAllTriggers(triggerId, otherActorId, false, onLeave, false, otherShapeId, 3);
end;

function ExtendedDynamicMountAttacher:dynamicMountTrigger4CallbackLeave(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
    self:dynamicMountAllTriggers(triggerId, otherActorId, false, onLeave, false, otherShapeId, 4);
end;

function ExtendedDynamicMountAttacher:removeFromPending(index)
	table.remove(self.pendingDynamicMountObjects, index); --remove pending object if it leaves trigger
	table.remove(self.pendingDynamicMountTriggers, index); --remove trigger index if it leaves trigger
	table.remove(self.pendingDynamicMountTimes, index);  -- remove counter
end;

function ExtendedDynamicMountAttacher:addPendingObject(object, trigger)
	table.insert(self.pendingDynamicMountObjects, object); -- add object to pending objects
	table.insert(self.pendingDynamicMountTriggers, trigger); -- add trigger index
	table.insert(self.pendingDynamicMountTimes, 0); -- reset trigger counter
end;

function ExtendedDynamicMountAttacher:mountDynamicWM(object, objectActorId, jointNode)
    if not object:getSupportsMountDynamic() or object.mountObject ~= nil or object.dynamicMountObject ~= nil then
        return false;
    end
	
    local constr = JointConstructor:new();
    constr:setActors(objectActorId, object.nodeId);
    constr:setJointTransforms(jointNode, jointNode);

    constr:setRotationLimit(0, 0, 0);
    constr:setRotationLimit(1, 0, 0);
    constr:setRotationLimit(2, 0, 0);

	constr:setTranslationLimit(0, true, -0, 0);
	constr:setTranslationLimit(1, true, -0, 0);
	constr:setTranslationLimit(2, true, -0, 0);
	constr:setEnableCollision(true);

    object.dynamicMountJointIndex = constr:finalize();
    object.dynamicMountObjectActorId = objectActorId;
    object.dynamicMountObject = self;
	object.jointNode = jointNode;
	
    return true;
end

function ExtendedDynamicMountAttacher:mountBaleToBale(bale1, object, objectActorId, jointNode)
    if object.mountObject ~= nil or object.dynamicMountObject ~= nil then
		object:unmountDynamic();  
    end
	
    local constr = JointConstructor:new();
    constr:setActors(objectActorId, object.nodeId);
    constr:setJointTransforms(jointNode, jointNode);

    constr:setRotationLimit(0, 0, 0);
    constr:setRotationLimit(1, 0, 0);
    constr:setRotationLimit(2, 0, 0);

	constr:setTranslationLimit(0, true, -0, 0);
	constr:setTranslationLimit(1, true, -0, 0);
	constr:setTranslationLimit(2, true, -0, 0);
	constr:setEnableCollision(true);

    object.dynamicMountJointIndex = constr:finalize();
    object.dynamicMountObjectActorId = objectActorId;
    object.dynamicMountObject = bale1;
	object.jointNode = jointNode;
	
    return true;
end