LUADOC - Farming Simulator 22

Script v1_7_1_0

Engine v1_7_1_0

Foundation Reference

AttacherJoints

Description
Specialization for vehicles with attacherJoints allowing attaching/coupling of tools
Functions

actionEventAttach

Description
Definition
actionEventAttach()
Code
4247function AttacherJoints.actionEventAttach(self, actionName, inputValue, callbackState, isAnalog)
4248 -- attach or detach something
4249 local info = self.spec_attacherJoints.attachableInfo
4250 if info.attachable ~= nil then
4251 -- attach
4252 local attachAllowed, warning = info.attachable:isAttachAllowed(self:getActiveFarm(), info.attacherVehicle)
4253 if attachAllowed then
4254 if self.isServer then
4255 self:attachImplementFromInfo(info)
4256 else
4257 g_client:getServerConnection():sendEvent(VehicleAttachRequestEvent.new(info))
4258 end
4259 else
4260 if warning ~= nil then
4261 g_currentMission:showBlinkingWarning(warning, 2000)
4262 end
4263 end
4264 else
4265 -- detach
4266 local object = self:getSelectedVehicle()
4267 if object ~= nil and object ~= self and object.isDetachAllowed ~= nil then
4268 local detachAllowed, warning, showWarning = object:isDetachAllowed()
4269 if detachAllowed then
4270 object:startDetachProcess()
4271 elseif showWarning == nil or showWarning then
4272 g_currentMission:showBlinkingWarning(warning or self.spec_attacherJoints.texts.detachNotAllowed, 2000)
4273 end
4274 end
4275 end
4276end

actionEventDetach

Description
Definition
actionEventDetach()
Code
4280function AttacherJoints.actionEventDetach(self, actionName, inputValue, callbackState, isAnalog)
4281 -- detach
4282 local object = self:getSelectedVehicle()
4283 if object ~= nil and object ~= self and object.isDetachAllowed ~= nil then
4284 local detachAllowed, warning, showWarning = object:isDetachAllowed()
4285 if detachAllowed then
4286 object:startDetachProcess()
4287 elseif showWarning == nil or showWarning then
4288 g_currentMission:showBlinkingWarning(warning or self.spec_attacherJoints.texts.detachNotAllowed, 2000)
4289 end
4290 end
4291end

actionEventLowerAllImplements

Description
Definition
actionEventLowerAllImplements()
Code
4304function AttacherJoints.actionEventLowerAllImplements(self, actionName, inputValue, callbackState, isAnalog)
4305 self:startAttacherJointCombo(true)
4306
4307 self.rootVehicle:raiseStateChange(Vehicle.STATE_CHANGE_LOWER_ALL_IMPLEMENTS)
4308end

actionEventLowerImplement

Description
Definition
actionEventLowerImplement()
Code
4295function AttacherJoints.actionEventLowerImplement(self, actionName, inputValue, callbackState, isAnalog)
4296 -- self is the implement object to lower, so we call the function on the attacher vehicle
4297 if self.getAttacherVehicle ~= nil then
4298 self:getAttacherVehicle():handleLowerImplementEvent()
4299 end
4300end

activateAttachments

Description
Call "activate" on all attachments
Definition
activateAttachments()
Code
2758function AttacherJoints:activateAttachments()
2759 local spec = self.spec_attacherJoints
2760
2761 for _,v in pairs(spec.attachedImplements) do
2762 if v.object ~= nil then
2763 v.object:activate()
2764 end
2765 end
2766end

addChildVehicles

Description
Inserts all child vehicles into the given table
Definition
addChildVehicles(table vehicles)
Arguments
tablevehicleschild vehicles table
Code
3475function AttacherJoints:addChildVehicles(superFunc, vehicles)
3476 local spec = self.spec_attacherJoints
3477
3478 for _, implement in pairs(spec.attachedImplements) do
3479 local object = implement.object
3480 if object ~= nil and object.addChildVehicles ~= nil then
3481 object:addChildVehicles(vehicles)
3482 end
3483 end
3484
3485 return superFunc(self, vehicles)
3486end

additionalAttachmentLoaded

Description
Called after the additional attachment was loaded
Definition
additionalAttachmentLoaded()
Code
2654function AttacherJoints:additionalAttachmentLoaded(vehicle, vehicleLoadState, asyncCallbackArguments)
2655 local offset = {0, 0, 0}
2656 if vehicle.getInputAttacherJoints ~= nil then
2657 local inputAttacherJoints = vehicle:getInputAttacherJoints()
2658 if inputAttacherJoints[asyncCallbackArguments[2]] ~= nil then
2659 offset = inputAttacherJoints[asyncCallbackArguments[2]].jointOrigOffsetComponent
2660 end
2661 end
2662
2663 local x, y, z = localToWorld(asyncCallbackArguments[3], unpack(offset))
2664 local dirX, _, dirZ = localDirectionToWorld(asyncCallbackArguments[3], 1, 0, 0)
2665 local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ)
2666 local terrainY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z)
2667
2668 vehicle:setAbsolutePosition(x, math.max(y, terrainY + 0.05), z, 0, yRot, 0)
2669 self:attachImplement(vehicle, asyncCallbackArguments[2], asyncCallbackArguments[1], true, nil, nil, true)
2670 vehicle:setIsAdditionalAttachment(asyncCallbackArguments[4], true)
2671 if vehicle.addDirtAmount ~= nil and asyncCallbackArguments[5] ~= nil and asyncCallbackArguments[5].getDirtAmount ~= nil then
2672 vehicle:addDirtAmount(asyncCallbackArguments[5]:getDirtAmount())
2673 end
2674
2675 self.rootVehicle:updateSelectableObjects()
2676 self.rootVehicle:setSelectedVehicle(asyncCallbackArguments[5] or self)
2677end

addToPhysics

Description
Add to physics
Definition
addToPhysics()
Return Values
booleansuccesssuccess
Code
3413function AttacherJoints:addToPhysics(superFunc)
3414 local spec = self.spec_attacherJoints
3415
3416 if not superFunc(self) then
3417 return false
3418 end
3419
3420 for _, implement in pairs(spec.attachedImplements) do
3421 if not implement.object.spec_attachable.isHardAttached then
3422 self:createAttachmentJoint(implement)
3423 end
3424 end
3425
3426 return true
3427end

addVehicleToAIImplementList

Description
Definition
addVehicleToAIImplementList()
Code
3507function AttacherJoints:addVehicleToAIImplementList(superFunc, list)
3508 superFunc(self, list)
3509
3510 for _, implement in pairs(self:getAttachedImplements()) do
3511 local object = implement.object
3512 if object ~= nil and object.addVehicleToAIImplementList ~= nil then
3513 object:addVehicleToAIImplementList(list)
3514 end
3515 end
3516end

attachableAddToolCameras

Description
Definition
attachableAddToolCameras()
Code
3578function AttacherJoints:attachableAddToolCameras(superFunc)
3579 local spec = self.spec_attacherJoints
3580 superFunc(self)
3581
3582 for _,implement in pairs(spec.attachedImplements) do
3583 local object = implement.object
3584 if object ~= nil and object.attachableAddToolCameras ~= nil then
3585 object:attachableAddToolCameras()
3586 end
3587 end
3588end

attachableRemoveToolCameras

Description
Definition
attachableRemoveToolCameras()
Code
3592function AttacherJoints:attachableRemoveToolCameras(superFunc)
3593 local spec = self.spec_attacherJoints
3594 superFunc(self)
3595
3596 for _,implement in pairs(spec.attachedImplements) do
3597 local object = implement.object
3598 if object ~= nil and object.attachableRemoveToolCameras ~= nil then
3599 object:attachableRemoveToolCameras()
3600 end
3601 end
3602end

attachAdditionalAttachment

Description
Creates and attaches additional attachment
Definition
attachAdditionalAttachment()
Code
2592function AttacherJoints:attachAdditionalAttachment(jointDesc, inputJointDesc, object)
2593 if jointDesc.additionalAttachment.attacherJointDirection ~= nil and inputJointDesc.additionalAttachment.filename ~= nil then
2594 local storeItem = g_storeManager:getItemByXMLFilename(inputJointDesc.additionalAttachment.filename)
2595 if storeItem ~= nil then
2596 local targetDirection = -jointDesc.additionalAttachment.attacherJointDirection
2597 local attacherJoint, attacherJointIndex
2598 for index, attacherJointToCheck in ipairs(self:getAttacherJoints()) do
2599 if attacherJointToCheck.additionalAttachment.attacherJointDirection == targetDirection then
2600 if attacherJointToCheck.jointIndex ~= 0 then
2601 attacherJoint = nil
2602 break
2603 else
2604 if attacherJointToCheck.jointType == inputJointDesc.additionalAttachment.jointType then
2605 attacherJoint = attacherJointToCheck
2606 attacherJointIndex = index
2607 end
2608 end
2609 end
2610 end
2611
2612 if attacherJoint ~= nil then
2613 local x, y, z = localToWorld(attacherJoint.jointTransform, 0, 0, 0)
2614 local dirX, _, dirZ = localDirectionToWorld(attacherJoint.jointTransform, 1, 0, 0)
2615 local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ)
2616 local location = {x=x, y=y, z=z, yRot=yRot}
2617
2618 jointDesc.additionalAttachment.currentAttacherJointIndex = attacherJointIndex
2619 local asyncCallbackArguments = {attacherJointIndex,
2620 inputJointDesc.additionalAttachment.inputAttacherJointIndex,
2621 attacherJoint.jointTransform,
2622 inputJointDesc.additionalAttachment.needsLowering,
2623 object}
2624
2625 local vehicle = VehicleLoadingUtil.loadVehicle(storeItem.xmlFilename, location, false, 0, Vehicle.PROPERTY_STATE_NONE, self:getActiveFarm(), {}, nil, AttacherJoints.additionalAttachmentLoaded, self, asyncCallbackArguments)
2626 if vehicle ~= nil and vehicle.setIsAdditionalAttachment ~= nil then
2627 vehicle:setIsAdditionalAttachment(inputJointDesc.additionalAttachment.needsLowering, false)
2628 else
2629 Logging.warning("Failed to load additional attachment '%s'.", storeItem.xmlFilename)
2630 end
2631 end
2632 end
2633 end
2634end

attachImplementFromInfo

Description
Definition
attachImplementFromInfo()
Code
1637function AttacherJoints:attachImplementFromInfo(info)
1638 if info.attachable ~= nil then
1639 local attacherJoints = info.attacherVehicle.spec_attacherJoints.attacherJoints
1640 if attacherJoints[info.attacherVehicleJointDescIndex].jointIndex == 0 and info.attachable.isAddedToMission then
1641 if info.attachable:getActiveInputAttacherJointDescIndex() ~= nil then
1642 if info.attachable:getAllowMultipleAttachments() then
1643 info.attachable:resolveMultipleAttachments()
1644 else
1645 return false
1646 end
1647 end
1648
1649 -- do not allow multiple implements in the same direction for mobile
1650 if GS_IS_MOBILE_VERSION then
1651 local attacherJointDirection = attacherJoints[info.attacherVehicleJointDescIndex].additionalAttachment.attacherJointDirection
1652 if attacherJointDirection ~= nil then
1653 local attachedImplements = info.attacherVehicle:getAttachedImplements()
1654 for i=1, #attachedImplements do
1655 local jointDesc = attacherJoints[attachedImplements[i].jointDescIndex]
1656
1657 if attacherJointDirection == jointDesc.additionalAttachment.attacherJointDirection then
1658 return false
1659 end
1660 end
1661 end
1662 end
1663
1664 info.attacherVehicle:attachImplement(info.attachable, info.attachableJointDescIndex, info.attacherVehicleJointDescIndex)
1665 return true
1666 end
1667 end
1668
1669 return false
1670end

calculateAttacherJointMoveUpperLowerAlpha

Description
Calculate move upper and lower alpha of attacher joint
Definition
calculateAttacherJointMoveUpperLowerAlpha(table jointDesc, table object, boolean initial)
Arguments
tablejointDescjoint desc of used attacher
tableobjectobject of attached vehicle
booleaninitialinitial call to reset
Code
1318function AttacherJoints:calculateAttacherJointMoveUpperLowerAlpha(jointDesc, object, initial)
1319 local objectAttacherJoint = object.spec_attachable.attacherJoint
1320
1321 if jointDesc.allowsLowering then
1322
1323 local lowerDistanceToGround = jointDesc.lowerDistanceToGround
1324 local upperDistanceToGround = jointDesc.upperDistanceToGround
1325
1326 local upperAlpha
1327 local lowerAlpha
1328
1329 if #objectAttacherJoint.heightNodes > 0 and jointDesc.rotationNode ~= nil then
1330 local checkData = self.spec_attacherJoints.groundHeightNodeCheckData
1331 if initial then
1332 checkData.heightNodes = objectAttacherJoint.heightNodes
1333 checkData.jointDesc = jointDesc
1334 checkData.objectAttacherJoint = objectAttacherJoint
1335 checkData.object = object
1336 checkData.index = -1
1337
1338 lowerDistanceToGround = jointDesc.lowerDistanceToGround
1339 upperDistanceToGround = jointDesc.upperDistanceToGround
1340
1341 for i=1, #objectAttacherJoint.heightNodes do
1342 local heightNode = objectAttacherJoint.heightNodes[i]
1343 local offX, offY, offZ = localToLocal(heightNode.node, heightNode.attacherJointNode, 0, 0, 0)
1344
1345 self:updateAttacherJointRotationNodes(jointDesc, 1)
1346 setRotation(jointDesc.jointTransform, unpack(jointDesc.jointOrigRot))
1347 local _, y, _ = localToLocal(jointDesc.jointTransform, jointDesc.rootNode, 0, 0, 0)
1348 local delta = jointDesc.lowerDistanceToGround - y
1349 local _, hy, _ = localToLocal(jointDesc.jointTransform, jointDesc.rootNode, offX, offY, offZ)
1350 lowerDistanceToGround = hy + delta
1351
1352 self:updateAttacherJointRotationNodes(jointDesc, 0)
1353 _, y, _ = localToLocal(jointDesc.jointTransform, jointDesc.rootNode, 0, 0, 0)
1354 delta = jointDesc.upperDistanceToGround - y
1355 _, hy, _ = localToLocal(jointDesc.jointTransform, jointDesc.rootNode, offX, offY, offZ)
1356 upperDistanceToGround = hy + delta
1357 end
1358 else
1359 if (jointDesc.moveAlpha or 0) > 0 then
1360 if checkData.index == -1 then
1361 checkData.index = 1
1362
1363 checkData.minDistance = math.huge
1364 checkData.hit = false
1365 self:doGroundHeightNodeCheck()
1366 end
1367
1368 if checkData.isDirty then
1369 checkData.isDirty = false
1370 self:doGroundHeightNodeCheck()
1371 end
1372
1373 if jointDesc.upperAlpha ~= nil and checkData.upperAlpha ~= nil then
1374 upperAlpha = jointDesc.upperAlpha * 0.9 + checkData.upperAlpha * 0.1
1375 lowerAlpha = jointDesc.lowerAlpha * 0.9 + checkData.lowerAlpha * 0.1
1376 else
1377 upperAlpha = checkData.upperAlpha
1378 lowerAlpha = checkData.lowerAlpha
1379 end
1380 else
1381 upperAlpha = jointDesc.upperAlpha
1382 lowerAlpha = jointDesc.lowerAlpha
1383 end
1384 end
1385 end
1386
1387 upperAlpha = upperAlpha or MathUtil.clamp((objectAttacherJoint.upperDistanceToGround - upperDistanceToGround) / (lowerDistanceToGround - upperDistanceToGround), 0, 1)
1388 lowerAlpha = lowerAlpha or MathUtil.clamp((objectAttacherJoint.lowerDistanceToGround - upperDistanceToGround) / (lowerDistanceToGround - upperDistanceToGround), 0, 1)
1389
1390 if initial then
1391 local checkData = self.spec_attacherJoints.groundHeightNodeCheckData
1392 checkData.upperAlpha = upperAlpha
1393 checkData.lowerAlpha = lowerAlpha
1394 end
1395
1396 if objectAttacherJoint.allowsLowering and jointDesc.allowsLowering then
1397 return upperAlpha, lowerAlpha
1398 else
1399 if objectAttacherJoint.isDefaultLowered then
1400 return lowerAlpha,lowerAlpha
1401 else
1402 return upperAlpha,upperAlpha
1403 end
1404 end
1405 end
1406
1407 if objectAttacherJoint.isDefaultLowered then
1408 return 1,1
1409 else
1410 return 0,0
1411 end
1412end

callFunctionOnAllImplements

Description
Definition
callFunctionOnAllImplements()
Code
2745function AttacherJoints:callFunctionOnAllImplements(functionName, ...)
2746 for _, implement in pairs(self:getAttachedImplements()) do
2747 local vehicle = implement.object
2748 if vehicle ~= nil then
2749 if vehicle[functionName] ~= nil then
2750 vehicle[functionName](vehicle, ...)
2751 end
2752 end
2753 end
2754end

collectAIAgentAttachments

Description
Definition
collectAIAgentAttachments()
Code
3520function AttacherJoints:collectAIAgentAttachments(superFunc, aiDrivableVehicle)
3521 superFunc(self, aiDrivableVehicle)
3522
3523 for _, implement in pairs(self:getAttachedImplements()) do
3524 local object = implement.object
3525 if object ~= nil and object.collectAIAgentAttachments ~= nil then
3526 object:collectAIAgentAttachments(aiDrivableVehicle)
3527 aiDrivableVehicle:startNewAIAgentAttachmentChain()
3528 end
3529 end
3530end

createAttachmentJoint

Description
Create attacher joint between vehicle and implement
Definition
createAttachmentJoint(table implement, boolean noSmoothAttach)
Arguments
tableimplementimplement to attach
booleannoSmoothAttachdont use smooth attach
Code
1926function AttacherJoints:createAttachmentJoint(implement, noSmoothAttach)
1927
1928 local spec = self.spec_attacherJoints
1929 local jointDesc = spec.attacherJoints[implement.jointDescIndex]
1930 local objectAttacherJoint = implement.object.spec_attachable.attacherJoint
1931
1932 if self.isServer and objectAttacherJoint ~= nil then
1933 if (getRigidBodyType(jointDesc.rootNode) ~= RigidBodyType.DYNAMIC and getRigidBodyType(jointDesc.rootNode) ~= RigidBodyType.KINEMATIC)
1934 or (getRigidBodyType(objectAttacherJoint.rootNode) ~= RigidBodyType.DYNAMIC and getRigidBodyType(objectAttacherJoint.rootNode) ~= RigidBodyType.KINEMATIC) then
1935 return
1936 end
1937
1938 local xNew = jointDesc.jointOrigTrans[1] + jointDesc.jointPositionOffset[1]
1939 local yNew = jointDesc.jointOrigTrans[2] + jointDesc.jointPositionOffset[2]
1940 local zNew = jointDesc.jointOrigTrans[3] + jointDesc.jointPositionOffset[3]
1941
1942 -- transform offset position to world coord and to jointTransform coord to get position offset dependend on angle and position
1943 local x,y,z = localToWorld(getParent(jointDesc.jointTransform), xNew, yNew, zNew)
1944 local x1,y1,z1 = worldToLocal(jointDesc.jointTransform, x,y,z)
1945
1946 -- move jointTransform to offset pos
1947 setTranslation(jointDesc.jointTransform, xNew, yNew, zNew)
1948
1949 -- transform it to implement position and angle
1950 x,y,z = localToWorld(objectAttacherJoint.node,x1,y1,z1)
1951 local x2,y2,z2 = worldToLocal(getParent(objectAttacherJoint.node), x,y,z)
1952 setTranslation(objectAttacherJoint.node, x2,y2, z2)
1953
1954
1955 local constr = JointConstructor.new()
1956 constr:setActors(jointDesc.rootNode, objectAttacherJoint.rootNode)
1957 constr:setJointTransforms(jointDesc.jointTransform, objectAttacherJoint.node)
1958 --constr:setBreakable(20, 10)
1959
1960 implement.jointRotLimit = {}
1961 implement.jointTransLimit = {}
1962
1963 implement.lowerRotLimit = {}
1964 implement.lowerTransLimit = {}
1965
1966 implement.upperRotLimit = {}
1967 implement.upperTransLimit = {}
1968
1969 if noSmoothAttach == nil or not noSmoothAttach then
1970 local dx,dy,dz = localToLocal(objectAttacherJoint.node, jointDesc.jointTransform, 0,0,0)
1971 local _,y,z = localDirectionToLocal(objectAttacherJoint.node, jointDesc.jointTransform, 0,1,0)
1972 local rX = math.atan2(z,y)
1973 local x,_,z = localDirectionToLocal(objectAttacherJoint.node, jointDesc.jointTransform, 0,0,1)
1974 local rY = math.atan2(x,z)
1975 local x,y,_ = localDirectionToLocal(objectAttacherJoint.node, jointDesc.jointTransform, 1,0,0)
1976 local rZ = math.atan2(y,x)
1977 implement.attachingTransLimit = { math.abs(dx), math.abs(dy), math.abs(dz) }
1978 implement.attachingRotLimit = { math.abs(rX), math.abs(rY), math.abs(rZ) }
1979 implement.attachingTransLimitSpeed = {}
1980 implement.attachingRotLimitSpeed = {}
1981 for i=1,3 do
1982 implement.attachingTransLimitSpeed[i] = implement.attachingTransLimit[i] / AttacherJoints.SMOOTH_ATTACH_TIME
1983 implement.attachingRotLimitSpeed[i] = implement.attachingRotLimit[i] / AttacherJoints.SMOOTH_ATTACH_TIME
1984 end
1985 implement.attachingIsInProgress = true
1986 else
1987 implement.attachingTransLimit = { 0,0,0 }
1988 implement.attachingRotLimit = { 0,0,0 }
1989 end
1990
1991 implement.rotLimitThreshold = objectAttacherJoint.rotLimitThreshold or 0
1992 implement.transLimitThreshold = objectAttacherJoint.transLimitThreshold or 0
1993
1994 for i=1, 3 do
1995 local rotLimit, transLimit = AttacherJoints.updateAttacherJointLimits(implement, jointDesc, objectAttacherJoint, i)
1996
1997 local limitRot = rotLimit
1998 local limitTrans = transLimit
1999 if noSmoothAttach == nil or not noSmoothAttach then
2000 limitRot = math.max(rotLimit, implement.attachingRotLimit[i])
2001 limitTrans = math.max(transLimit, implement.attachingTransLimit[i])
2002 end
2003
2004 local rotLimitDown, rotLimitUp = -limitRot, limitRot
2005 if i == 3 then
2006 if jointDesc.lockDownRotLimit then
2007 rotLimitDown = math.min(-implement.attachingRotLimit[i], 0)
2008 end
2009 if jointDesc.lockUpRotLimit then
2010 rotLimitUp = math.max(implement.attachingRotLimit[i], 0)
2011 end
2012 end
2013 constr:setRotationLimit(i-1, rotLimitDown, rotLimitUp)
2014 implement.jointRotLimit[i] = limitRot
2015
2016 local transLimitDown, transLimitUp = -limitTrans, limitTrans
2017 if i == 2 then
2018 if jointDesc.lockDownTransLimit then
2019 transLimitDown = math.min(-implement.attachingTransLimit[i], 0)
2020 end
2021 if jointDesc.lockUpTransLimit then
2022 transLimitUp = math.max(implement.attachingTransLimit[i], 0)
2023 end
2024 end
2025 constr:setTranslationLimit(i-1, true, transLimitDown, transLimitUp)
2026 implement.jointTransLimit[i] = limitTrans
2027 end
2028
2029 if jointDesc.enableCollision then
2030 constr:setEnableCollision(true)
2031 else
2032 for _, component in pairs(self.components) do
2033 if component.node ~= jointDesc.rootNodeBackup and not component.collideWithAttachables then
2034 setPairCollision(component.node, objectAttacherJoint.rootNode, false)
2035 end
2036 end
2037 end
2038
2039 local springX = math.max(jointDesc.rotLimitSpring[1], objectAttacherJoint.rotLimitSpring[1])
2040 local springY = math.max(jointDesc.rotLimitSpring[2], objectAttacherJoint.rotLimitSpring[2])
2041 local springZ = math.max(jointDesc.rotLimitSpring[3], objectAttacherJoint.rotLimitSpring[3])
2042 local dampingX = math.max(jointDesc.rotLimitDamping[1], objectAttacherJoint.rotLimitDamping[1])
2043 local dampingY = math.max(jointDesc.rotLimitDamping[2], objectAttacherJoint.rotLimitDamping[2])
2044 local dampingZ = math.max(jointDesc.rotLimitDamping[3], objectAttacherJoint.rotLimitDamping[3])
2045 local forceLimitX = Utils.getMaxJointForceLimit(jointDesc.rotLimitForceLimit[1], objectAttacherJoint.rotLimitForceLimit[1])
2046 local forceLimitY = Utils.getMaxJointForceLimit(jointDesc.rotLimitForceLimit[2], objectAttacherJoint.rotLimitForceLimit[2])
2047 local forceLimitZ = Utils.getMaxJointForceLimit(jointDesc.rotLimitForceLimit[3], objectAttacherJoint.rotLimitForceLimit[3])
2048 constr:setRotationLimitSpring(springX, dampingX, springY, dampingY, springZ, dampingZ)
2049 constr:setRotationLimitForceLimit(forceLimitX, forceLimitY, forceLimitZ)
2050
2051 local springX = math.max(jointDesc.transLimitSpring[1], objectAttacherJoint.transLimitSpring[1])
2052 local springY = math.max(jointDesc.transLimitSpring[2], objectAttacherJoint.transLimitSpring[2])
2053 local springZ = math.max(jointDesc.transLimitSpring[3], objectAttacherJoint.transLimitSpring[3])
2054 local dampingX = math.max(jointDesc.transLimitDamping[1], objectAttacherJoint.transLimitDamping[1])
2055 local dampingY = math.max(jointDesc.transLimitDamping[2], objectAttacherJoint.transLimitDamping[2])
2056 local dampingZ = math.max(jointDesc.transLimitDamping[3], objectAttacherJoint.transLimitDamping[3])
2057 local forceLimitX = Utils.getMaxJointForceLimit(jointDesc.transLimitForceLimit[1], objectAttacherJoint.transLimitForceLimit[1])
2058 local forceLimitY = Utils.getMaxJointForceLimit(jointDesc.transLimitForceLimit[2], objectAttacherJoint.transLimitForceLimit[2])
2059 local forceLimitZ = Utils.getMaxJointForceLimit(jointDesc.transLimitForceLimit[3], objectAttacherJoint.transLimitForceLimit[3])
2060 constr:setTranslationLimitSpring(springX, dampingX, springY, dampingY, springZ, dampingZ)
2061 constr:setTranslationLimitForceLimit(forceLimitX, forceLimitY, forceLimitZ)
2062
2063 jointDesc.jointIndex = constr:finalize()
2064
2065 -- restore implement attacher joint position (to ensure correct bottom arm alignment)
2066 setTranslation(objectAttacherJoint.node, unpack(objectAttacherJoint.jointOrigTrans))
2067 else
2068 -- set joint index to '1' on client side, so we can check if something is attached
2069 jointDesc.jointIndex = 1
2070 end
2071end

deactivateAttachments

Description
Call "deactivate" on all attachments
Definition
deactivateAttachments()
Code
2770function AttacherJoints:deactivateAttachments()
2771 local spec = self.spec_attacherJoints
2772
2773 for _,v in pairs(spec.attachedImplements) do
2774 if v.object ~= nil then
2775 v.object:deactivate()
2776 end
2777 end
2778end

deactivateAttachmentsLights

Description
Call "deactivateLights" on all attachments
Definition
deactivateAttachmentsLights()
Code
2782function AttacherJoints:deactivateAttachmentsLights()
2783 local spec = self.spec_attacherJoints
2784
2785 for _,v in pairs(spec.attachedImplements) do
2786 if v.object ~= nil and v.object.deactivateLights ~= nil then
2787 v.object:deactivateLights()
2788 end
2789 end
2790end

detachAdditionalAttachment

Description
Creates and attaches additional attachment
Definition
detachAdditionalAttachment()
Code
2638function AttacherJoints:detachAdditionalAttachment(jointDesc, inputJointDesc)
2639 if jointDesc.additionalAttachment.currentAttacherJointIndex ~= nil and inputJointDesc.additionalAttachment.filename ~= nil then
2640 local implement = self:getImplementByJointDescIndex(jointDesc.additionalAttachment.currentAttacherJointIndex)
2641 if implement ~= nil then
2642 if implement.object:getIsAdditionalAttachment() then
2643 -- on the exit the vehicle will be removed by BaseMission.delete
2644 if not g_currentMission.isExitingGame then
2645 g_currentMission:removeVehicle(implement.object)
2646 end
2647 end
2648 end
2649 end
2650end

detachAttachedImplement

Description
Definition
detachAttachedImplement()
Code
2477function AttacherJoints:detachAttachedImplement()
2478 if self:getCanToggleAttach() then
2479 AttacherJoints.actionEventAttach(self)
2480 end
2481end

detachImplement

Description
Detach implement
Definition
detachImplement(integer implementIndex, boolean noEventSend)
Arguments
integerimplementIndexindex of implement in self.attachedImplements
booleannoEventSendno event send
Return Values
booleansuccesssuccess
Code
2216function AttacherJoints:detachImplement(implementIndex, noEventSend)
2217 local spec = self.spec_attacherJoints
2218
2219 if noEventSend == nil or noEventSend == false then
2220 if g_server ~= nil then
2221 g_server:broadcastEvent(VehicleDetachEvent.new(self, spec.attachedImplements[implementIndex].object), nil, nil, self)
2222 else
2223 -- Send detach request to server and return
2224 local implement = spec.attachedImplements[implementIndex]
2225 if implement.object ~= nil then
2226 g_client:getServerConnection():sendEvent(VehicleDetachEvent.new(self, implement.object))
2227 end
2228 return
2229 end
2230 end
2231
2232 local implement = spec.attachedImplements[implementIndex]
2233
2234 SpecializationUtil.raiseEvent(self, "onPreDetachImplement", implement)
2235 implement.object:preDetach(self, implement)
2236
2237 local jointDesc
2238 if implement.object ~= nil then
2239 jointDesc = spec.attacherJoints[implement.jointDescIndex]
2240 if jointDesc.transNode ~= nil then
2241 setTranslation(jointDesc.transNode, unpack(jointDesc.transNodeOrgTrans))
2242
2243 if jointDesc.transNodeDependentBottomArm ~= nil then
2244 local bottomArmJointDesc = jointDesc.transNodeDependentBottomArmAttacherJoint
2245 local interpolator = ValueInterpolator.new(bottomArmJointDesc.bottomArm.interpolatorKey, bottomArmJointDesc.bottomArm.interpolatorGet, bottomArmJointDesc.bottomArm.interpolatorSet, {bottomArmJointDesc.bottomArm.rotX, bottomArmJointDesc.bottomArm.rotY, bottomArmJointDesc.bottomArm.rotZ}, AttacherJoints.SMOOTH_ATTACH_TIME * 2)
2246 if interpolator ~= nil then
2247 interpolator:setDeleteListenerObject(self)
2248 interpolator:setFinishedFunc(bottomArmJointDesc.bottomArm.interpolatorFinished, bottomArmJointDesc.bottomArm)
2249 bottomArmJointDesc.bottomArm.bottomArmInterpolating = true
2250 end
2251 end
2252 end
2253 if not implement.object.spec_attachable.isHardAttached then
2254 if self.isServer then
2255 if jointDesc.jointIndex ~= 0 then
2256 removeJoint(jointDesc.jointIndex)
2257 end
2258
2259 if not jointDesc.enableCollision then
2260 for _, component in pairs(self.components) do
2261 if component.node ~= jointDesc.rootNodeBackup and not component.collideWithAttachables then
2262 local attacherJoint = implement.object:getActiveInputAttacherJoint()
2263 setPairCollision(component.node, attacherJoint.rootNode, true)
2264 end
2265 end
2266 end
2267 end
2268 end
2269 jointDesc.jointIndex = 0
2270 end
2271
2272 if not jointDesc.delayedObjectChanges or jointDesc.bottomArm == nil then
2273 ObjectChangeUtil.setObjectChanges(jointDesc.changeObjects, false, self, self.setMovingToolDirty)
2274 end
2275
2276 for i=1, #jointDesc.hideVisuals do
2277 local node = jointDesc.hideVisuals[i]
2278
2279 local allowedToShow = true
2280 local attacherJoints = spec.hideVisualNodeToAttacherJoints[node]
2281 if attacherJoints ~= nil then
2282 for j=1, #attacherJoints do
2283 if attacherJoints[j].jointIndex ~= 0 then
2284 allowedToShow = false
2285 end
2286 end
2287 end
2288
2289 if allowedToShow then
2290 setVisibility(node, true)
2291 end
2292 end
2293
2294 for i=1, #jointDesc.visualNodes do
2295 local node = jointDesc.visualNodes[i]
2296
2297 local hideNode = false
2298 local attacherJoints = spec.hideVisualNodeToAttacherJoints[node]
2299 if attacherJoints ~= nil then
2300 for j=1, #attacherJoints do
2301 if attacherJoints[j].jointIndex ~= 0 then
2302 hideNode = true
2303 end
2304 end
2305 end
2306
2307 if hideNode then
2308 setVisibility(node, false)
2309 end
2310 end
2311
2312 if implement.object ~= nil then
2313 local object = implement.object
2314
2315 if object.spec_attachable.isHardAttached then
2316 self:hardDetachImplement(implement)
2317 end
2318
2319 if self.isClient then
2320 if jointDesc.topArm ~= nil then
2321 setRotation(jointDesc.topArm.rotationNode, jointDesc.topArm.rotX, jointDesc.topArm.rotY, jointDesc.topArm.rotZ)
2322 if jointDesc.topArm.translationNode ~= nil then
2323 setTranslation(jointDesc.topArm.translationNode, 0, 0, 0)
2324 end
2325 if jointDesc.topArm.scaleNode ~= nil then
2326 setScale(jointDesc.topArm.scaleNode, 1, 1, 1)
2327 end
2328 if jointDesc.topArm.toggleVisibility then
2329 setVisibility(jointDesc.topArm.rotationNode, false)
2330 end
2331 end
2332 if jointDesc.bottomArm ~= nil then
2333 local interpolator = ValueInterpolator.new(jointDesc.bottomArm.interpolatorKey, jointDesc.bottomArm.interpolatorGet, jointDesc.bottomArm.interpolatorSet, {jointDesc.bottomArm.rotX, jointDesc.bottomArm.rotY, jointDesc.bottomArm.rotZ}, nil, jointDesc.bottomArm.resetSpeed)
2334 if interpolator ~= nil then
2335 interpolator:setDeleteListenerObject(self)
2336 interpolator:setFinishedFunc(jointDesc.bottomArm.interpolatorFinished, jointDesc.bottomArm)
2337 jointDesc.bottomArm.bottomArmInterpolating = true
2338
2339 if jointDesc.delayedObjectChanges then
2340 interpolator:setFinishedFunc(function()
2341 jointDesc.bottomArm.interpolatorFinished(jointDesc.bottomArm)
2342 ObjectChangeUtil.setObjectChanges(jointDesc.changeObjects, false, self, self.setMovingToolDirty)
2343 end)
2344 end
2345 end
2346
2347 jointDesc.bottomArm.lastDirection[1], jointDesc.bottomArm.lastDirection[2], jointDesc.bottomArm.lastDirection[3] = 0, 0, 0
2348
2349 if jointDesc.bottomArm.translationNode ~= nil then
2350 setTranslation(jointDesc.bottomArm.translationNode, 0, 0, 0)
2351 end
2352 if jointDesc.bottomArm.toolbar ~= nil then
2353 setVisibility(jointDesc.bottomArm.toolbar, false)
2354 end
2355 if jointDesc.bottomArm.toggleVisibility then
2356 setVisibility(jointDesc.bottomArm.rotationNode, false)
2357 end
2358 end
2359 end
2360 -- restore original translation
2361 setTranslation(jointDesc.jointTransform, unpack(jointDesc.jointOrigTrans))
2362 local attacherJoint = object:getActiveInputAttacherJoint()
2363 setTranslation(attacherJoint.node, unpack(attacherJoint.jointOrigTrans))
2364 if jointDesc.rotationNode ~= nil then
2365 setRotation(jointDesc.rotationNode, jointDesc.rotX, jointDesc.rotY, jointDesc.rotZ)
2366 end
2367
2368 SpecializationUtil.raiseEvent(self, "onPostDetachImplement", implementIndex)
2369 object:postDetach(implementIndex)
2370
2371 self:detachAdditionalAttachment(jointDesc, attacherJoint)
2372 end
2373
2374 table.remove(spec.attachedImplements, implementIndex)
2375
2376 self:playDetachSound(jointDesc)
2377
2378 spec.wasInAttachRange = nil
2379
2380 self:updateVehicleChain()
2381 implement.object:updateVehicleChain()
2382
2383 local data = {attacherVehicle=self, attachedVehicle=implement.object}
2384 implement.object:raiseStateChange(Vehicle.STATE_CHANGE_DETACH, data)
2385 local rootVehicle = self.rootVehicle
2386 rootVehicle:raiseStateChange(Vehicle.STATE_CHANGE_DETACH, data)
2387
2388 self.rootVehicle:updateSelectableObjects()
2389 if GS_IS_MOBILE_VERSION then
2390 -- for mobile we select the next vehicle that can be detached, if non available we select the root
2391 local nextImplement = next(spec.attachedImplements)
2392 if spec.attachedImplements[nextImplement] ~= nil then
2393 self.rootVehicle:setSelectedVehicle(spec.attachedImplements[nextImplement].object, nil, true)
2394 else
2395 self.rootVehicle:setSelectedVehicle(self, nil, true)
2396 end
2397 else
2398 self.rootVehicle:setSelectedVehicle(self, nil, true)
2399 end
2400 self.rootVehicle:requestActionEventUpdate() -- do action event update independent of a successful selection (important since we cannot select every vehicle)
2401 implement.object:updateSelectableObjects()
2402 implement.object:setSelectedVehicle(implement.object, nil, true)
2403 implement.object:requestActionEventUpdate() -- do action event update independent of a successful selection (important since we cannot select every vehicle)
2404
2405 return true
2406end

detachImplementByObject

Description
Detach implement by object of implement
Definition
detachImplementByObject(table object, boolean noEventSend)
Arguments
tableobjectobject of implement to detach
booleannoEventSendno event send
Return Values
booleansuccesssuccess
Code
2413function AttacherJoints:detachImplementByObject(object, noEventSend)
2414 local spec = self.spec_attacherJoints
2415
2416 for i,implement in ipairs(spec.attachedImplements) do
2417 if implement.object == object then
2418 self:detachImplement(i, noEventSend)
2419 break
2420 end
2421 end
2422
2423 return true
2424end

detachingIsPossible

Description
Returns true if it is possible to detach selected implement
Definition
detachingIsPossible()
Return Values
booleanpossibleToDetachpossible to detach selected implement
Code
2576function AttacherJoints:detachingIsPossible()
2577 local implement = self:getImplementByObject(self:getSelectedVehicle())
2578 if implement ~= nil then
2579 local object = implement.object
2580 if object ~= nil and object.attacherVehicle ~= nil and object:isDetachAllowed() then
2581 local implementIndex = object.attacherVehicle:getImplementIndexByObject(object)
2582 if implementIndex ~= nil then
2583 return true
2584 end
2585 end
2586 end
2587 return false
2588end

doGroundHeightNodeCheck

Description
Starts the next step in the ground height node check (raycast)
Definition
doGroundHeightNodeCheck()
Code
1416function AttacherJoints:doGroundHeightNodeCheck()
1417 local checkData = self.spec_attacherJoints.groundHeightNodeCheckData
1418
1419 local heightNode = checkData.heightNodes[checkData.index]
1420 if heightNode ~= nil and checkData.object:getIsAttacherJointHeightNodeActive(heightNode) then
1421 local offX, offY, offZ = localToLocal(heightNode.node, heightNode.attacherJointNode, 0, 0, 0)
1422
1423 self:updateAttacherJointRotationNodes(checkData.jointDesc, 1)
1424 local lWx, lWy, lWz = localToWorld(checkData.jointDesc.jointTransformOrig, offX, offY, offZ)
1425
1426 self:updateAttacherJointRotationNodes(checkData.jointDesc, 0)
1427 local uWx, uWy, uWz = localToWorld(checkData.jointDesc.jointTransformOrig, offX, offY, offZ)
1428
1429 --#debug if VehicleDebug.state == VehicleDebug.DEBUG then
1430 --#debug DebugUtil.drawDebugGizmoAtWorldPos(lWx, lWy, lWz, 0, 0, 1, 0, 1, 0, "", false)
1431 --#debug DebugUtil.drawDebugGizmoAtWorldPos(uWx, uWy, uWz, 0, 0, 1, 0, 1, 0, "", false)
1432 --#debug drawDebugLine(uWx, uWy, uWz, 0, 1, 0, lWx, lWy, lWz, 1, 0, 0, true)
1433 --#debug end
1434
1435 local dirX, dirY, dirZ = lWx - uWx, lWy - uWy, lWz - uWz
1436 local distance = MathUtil.vector3Length(dirX, dirY, dirZ)
1437 dirX, dirY, dirZ = MathUtil.vector3Normalize(dirX, dirY, dirZ)
1438
1439 checkData.currentRaycastDistance = distance
1440 checkData.currentRaycastWorldPos[1] = uWx
1441 checkData.currentRaycastWorldPos[2] = uWy
1442 checkData.currentRaycastWorldPos[3] = uWz
1443 checkData.currentRaycastWorldDir[1] = dirX
1444 checkData.currentRaycastWorldDir[2] = dirY
1445 checkData.currentRaycastWorldDir[3] = dirZ
1446
1447 checkData.currentJointTransformPos[1], checkData.currentJointTransformPos[2], checkData.currentJointTransformPos[3] = getWorldTranslation(checkData.jointDesc.jointTransform)
1448
1449 -- as real raycast distance we also add the lower distance to ground
1450 distance = distance + checkData.objectAttacherJoint.lowerDistanceToGround
1451 checkData.minDistance = math.min(checkData.minDistance, distance)
1452 raycastAll(uWx, uWy, uWz, dirX, dirY, dirZ, "groundHeightNodeCheckCallback", distance, self, CollisionMask.TERRAIN, false, true)
1453
1454 self:updateAttacherJointRotationNodes(checkData.jointDesc, checkData.jointDesc.moveAlpha or 0)
1455 else
1456 checkData.index = checkData.index + 1
1457 if checkData.index > #checkData.heightNodes then
1458 self:finishGroundHeightNodeCheck()
1459 else
1460 checkData.isDirty = true
1461 end
1462 end
1463end

findVehicleInAttachRange

Description
Definition
findVehicleInAttachRange()
Code
4129function AttacherJoints.findVehicleInAttachRange()
4130 log("function 'AttacherJoints.findVehicleInAttachRange' is deprecated. Use 'AttacherJoints.updateVehiclesInAttachRange' instead. Valid output of this function is now up to 5 frames delayed, if parameter 4 is not 'true'.")
4131end

finishGroundHeightNodeCheck

Description
Called to finish the ground height node check and calculate the upper and lower alpha based on the results
Definition
finishGroundHeightNodeCheck()
Code
1467function AttacherJoints:finishGroundHeightNodeCheck()
1468 local checkData = self.spec_attacherJoints.groundHeightNodeCheckData
1469
1470 if checkData.minDistance ~= math.huge then
1471 if not checkData.hit then
1472 checkData.raycastDistance = checkData.currentRaycastDistance
1473
1474 checkData.raycastWorldPos[1] = checkData.currentRaycastWorldPos[1]
1475 checkData.raycastWorldPos[2] = checkData.currentRaycastWorldPos[2]
1476 checkData.raycastWorldPos[3] = checkData.currentRaycastWorldPos[3]
1477
1478 checkData.raycastWorldDir[1] = checkData.currentRaycastWorldDir[1]
1479 checkData.raycastWorldDir[2] = checkData.currentRaycastWorldDir[2]
1480 checkData.raycastWorldDir[3] = checkData.currentRaycastWorldDir[3]
1481
1482 checkData.jointTransformPos[1] = checkData.currentJointTransformPos[1]
1483 checkData.jointTransformPos[2] = checkData.currentJointTransformPos[2]
1484 checkData.jointTransformPos[3] = checkData.currentJointTransformPos[3]
1485 end
1486
1487 local upperAlpha = (checkData.minDistance - checkData.objectAttacherJoint.upperDistanceToGround) / checkData.raycastDistance
1488 local lowerAlpha = (checkData.minDistance - checkData.objectAttacherJoint.lowerDistanceToGround) / checkData.raycastDistance
1489
1490 local uWx, uWy, uWz = checkData.raycastWorldPos[1], checkData.raycastWorldPos[2], checkData.raycastWorldPos[3]
1491 local dirX, dirY, dirZ = checkData.raycastWorldDir[1], checkData.raycastWorldDir[2], checkData.raycastWorldDir[3]
1492
1493 -- correction since we raycast straight to lower point, but rotate in a circle
1494 local x1, y1, z1 = uWx + dirX * checkData.raycastDistance * lowerAlpha, uWy + dirY * checkData.raycastDistance * lowerAlpha, uWz + dirZ * checkData.raycastDistance * lowerAlpha
1495
1496 local x3, y3, z3 = checkData.jointTransformPos[1], checkData.jointTransformPos[2], checkData.jointTransformPos[3]
1497 local straightToCenter = MathUtil.vector3Length(x1-x3, y1-y3, z1-z3)
1498 local circleToCenter = MathUtil.vector3Length(uWx-x3, uWy-y3, uWz-z3)
1499 local straightOffset = circleToCenter - straightToCenter
1500
1501 local _, h1, h2
1502 _, h1, _ = worldToLocal(self.rootNode, x1, y1, z1)
1503 _, h2, _ = worldToLocal(self.rootNode, uWx, uWy, uWz)
1504
1505 local angle = math.atan(straightOffset / (h2 - h1))
1506 local offset = straightOffset * math.sin(angle)
1507 lowerAlpha = (checkData.minDistance - checkData.objectAttacherJoint.lowerDistanceToGround - offset) / checkData.raycastDistance
1508
1509 checkData.lowerAlpha = MathUtil.clamp(lowerAlpha, 0, 1)
1510 checkData.upperAlpha = MathUtil.clamp(upperAlpha, 0, 1)
1511 end
1512
1513 checkData.index = -1
1514end

getAirConsumerUsage

Description
Returns air consumer usage of attached vehicles
Definition
getAirConsumerUsage()
Return Values
floatusageair usage
Code
3491function AttacherJoints:getAirConsumerUsage(superFunc)
3492 local spec = self.spec_attacherJoints
3493 local usage = superFunc(self)
3494
3495 for _, implement in pairs(spec.attachedImplements) do
3496 local object = implement.object
3497 if object ~= nil and object.getAttachbleAirConsumerUsage ~= nil then
3498 usage = usage + object:getAttachbleAirConsumerUsage()
3499 end
3500 end
3501
3502 return usage
3503end

getAreControlledActionsAllowed

Description
Returns if controlled actions are allowed
Definition
getAreControlledActionsAllowed()
Return Values
booleanallowallow controlled actions
stringwarningnot allowed warning
Code
3777function AttacherJoints:getAreControlledActionsAllowed(superFunc)
3778 local allowed, warning = superFunc(self)
3779 if not allowed then
3780 return false, warning
3781 end
3782
3783 local spec = self.spec_attacherJoints
3784 for _, implement in pairs(spec.attachedImplements) do
3785 local object = implement.object
3786 if object ~= nil then
3787 if object.getAreControlledActionsAllowed ~= nil then
3788 allowed, warning = object:getAreControlledActionsAllowed()
3789 if not allowed then
3790 return false, warning
3791 end
3792 end
3793 end
3794 end
3795
3796 return true, warning
3797end

getAttachedImplements

Description
Definition
getAttachedImplements()
Code
1094function AttacherJoints:getAttachedImplements()
1095 return self.spec_attacherJoints.attachedImplements
1096end

getAttacherJointByJointDescIndex

Description
Definition
getAttacherJointByJointDescIndex()
Code
1106function AttacherJoints:getAttacherJointByJointDescIndex(jointDescIndex)
1107 return self.spec_attacherJoints.attacherJoints[jointDescIndex]
1108end

getAttacherJointCompatibility

Description
Definition
getAttacherJointCompatibility()
Code
4045function AttacherJoints.getAttacherJointCompatibility(vehicle, attacherJoint, inputAttacherVehicle, inputAttacherJoint)
4046 if inputAttacherJoint.forcedAttachingDirection ~= 0 and attacherJoint.additionalAttachment.attacherJointDirection ~= nil then
4047 if inputAttacherJoint.forcedAttachingDirection ~= attacherJoint.additionalAttachment.attacherJointDirection then
4048 return false
4049 end
4050 end
4051
4052 if attacherJoint.subTypes ~= nil then
4053 if inputAttacherJoint.subTypes == nil then
4054 return false, vehicle.spec_attacherJoints.texts.warningToolNotCompatible
4055 end
4056
4057 local found = false
4058 for i=1, #attacherJoint.subTypes do
4059 for j=1, #inputAttacherJoint.subTypes do
4060 if attacherJoint.subTypes[i] == inputAttacherJoint.subTypes[j] then
4061 found = true
4062 break
4063 end
4064 end
4065 end
4066
4067 if not found then
4068 if attacherJoint.subTypeShowWarning then
4069 return false, vehicle.spec_attacherJoints.texts.warningToolNotCompatible
4070 end
4071
4072 return false
4073 end
4074 else
4075 if inputAttacherJoint.subTypes ~= nil then
4076 if inputAttacherJoint.subTypeShowWarning then
4077 return false, vehicle.spec_attacherJoints.texts.warningToolNotCompatible
4078 end
4079
4080 return false
4081 end
4082 end
4083
4084 if attacherJoint.brandRestrictions ~= nil then
4085 local found = false
4086 for i=1, #attacherJoint.brandRestrictions do
4087 if inputAttacherVehicle.brand ~= nil then
4088 if inputAttacherVehicle.brand == attacherJoint.brandRestrictions[i] then
4089 found = true
4090 break
4091 end
4092 end
4093 end
4094
4095 if not found then
4096 local brandString = ""
4097 for i=1, #attacherJoint.brandRestrictions do
4098 if i > 1 then
4099 brandString = brandString .. ", "
4100 end
4101 brandString = brandString .. attacherJoint.brandRestrictions[i].title
4102 end
4103
4104 return false, string.format(vehicle.spec_attacherJoints.texts.warningToolBrandNotCompatible, brandString)
4105 end
4106 end
4107
4108 if attacherJoint.vehicleRestrictions ~= nil then
4109 local found = false
4110 for i=1, #attacherJoint.vehicleRestrictions do
4111 if inputAttacherVehicle.configFileName:find(attacherJoint.vehicleRestrictions[i]) ~= nil then
4112 found = true
4113 break
4114 end
4115 end
4116
4117 if not found then
4118 return false, vehicle.spec_attacherJoints.texts.warningToolNotCompatible
4119 end
4120 end
4121
4122 return true
4123end

getAttacherJointDescFromObject

Description
Definition
getAttacherJointDescFromObject()
Code
1148function AttacherJoints:getAttacherJointDescFromObject(object)
1149 local spec = self.spec_attacherJoints
1150 for _,attachedImplement in pairs(spec.attachedImplements) do
1151 if attachedImplement.object == object then
1152 return spec.attacherJoints[attachedImplement.jointDescIndex]
1153 end
1154 end
1155end

getAttacherJointIndexByNode

Description
Definition
getAttacherJointIndexByNode()
Code
1112function AttacherJoints:getAttacherJointIndexByNode(node)
1113 local spec = self.spec_attacherJoints
1114 for i=1, #spec.attacherJoints do
1115 local attacherJoint = spec.attacherJoints[i]
1116 if attacherJoint.jointTransform == node then
1117 return i
1118 end
1119 end
1120
1121 return nil
1122end

getAttacherJointIndexFromImplementIndex

Description
Definition
getAttacherJointIndexFromImplementIndex()
Code
1159function AttacherJoints:getAttacherJointIndexFromImplementIndex(implementIndex)
1160 local spec = self.spec_attacherJoints
1161 local attachedImplement = spec.attachedImplements[implementIndex]
1162 if attachedImplement ~= nil then
1163 return attachedImplement.jointDescIndex
1164 end
1165 return nil
1166end

getAttacherJointIndexFromObject

Description
Definition
getAttacherJointIndexFromObject()
Code
1137function AttacherJoints:getAttacherJointIndexFromObject(object)
1138 local spec = self.spec_attacherJoints
1139 for _,attachedImplement in pairs(spec.attachedImplements) do
1140 if attachedImplement.object == object then
1141 return attachedImplement.jointDescIndex
1142 end
1143 end
1144end

getAttacherJoints

Description
Definition
getAttacherJoints()
Code
1100function AttacherJoints:getAttacherJoints()
1101 return self.spec_attacherJoints.attacherJoints
1102end

getCanSteerAttachable

Description
Returns if the vehicle can control the steering axles of the given attachable
Definition
getCanSteerAttachable(table jointDesc)
Arguments
tablejointDescjoint desc
Return Values
booleansuccesssuccess
Code
2517function AttacherJoints:getCanSteerAttachable(attachable)
2518 local jointDesc = self:getAttacherJointDescFromObject(attachable)
2519 if jointDesc ~= nil then
2520 if jointDesc.steeringBarLeftNode ~= nil or jointDesc.steeringBarRightNode ~= nil or jointDesc.steeringBarForceUsage then
2521 return true
2522 end
2523 end
2524
2525 return false
2526end

getCanToggleAttach

Description
Definition
getCanToggleAttach()
Code
2449function AttacherJoints:getCanToggleAttach()
2450 return true
2451end

getConnectionHoseConfigIndex

Description
Definition
getConnectionHoseConfigIndex()
Code
3801function AttacherJoints:getConnectionHoseConfigIndex(superFunc)
3802 local index = superFunc(self)
3803 index = self.xmlFile:getValue("vehicle.attacherJoints#connectionHoseConfigId", index)
3804
3805 if self.configurations["attacherJoint"] ~= nil then
3806 local configKey = string.format("vehicle.attacherJoints.attacherJointConfigurations.attacherJointConfiguration(%d)", self.configurations["attacherJoint"] - 1)
3807 index = self.xmlFile:getValue(configKey .. "#connectionHoseConfigId", index)
3808 end
3809
3810 return index
3811end

getDirectionSnapAngle

Description
Definition
getDirectionSnapAngle()
Code
3547function AttacherJoints:getDirectionSnapAngle(superFunc)
3548 local spec = self.spec_attacherJoints
3549 local maxAngle = superFunc(self)
3550
3551 for _, implement in pairs(spec.attachedImplements) do
3552 local object = implement.object
3553 if object ~= nil and object.getDirectionSnapAngle ~= nil then
3554 maxAngle = math.max(maxAngle + object:getDirectionSnapAngle())
3555 end
3556 end
3557
3558 return maxAngle
3559end

getFillLevelInformation

Description
Definition
getFillLevelInformation()
Code
3563function AttacherJoints:getFillLevelInformation(superFunc, display)
3564 local spec = self.spec_attacherJoints
3565
3566 superFunc(self, display)
3567
3568 for _, implement in pairs(spec.attachedImplements) do
3569 local object = implement.object
3570 if object ~= nil and object.getFillLevelInformation ~= nil then
3571 object:getFillLevelInformation(display)
3572 end
3573 end
3574end

getImplementByJointDescIndex

Description
Returns implement by jointDescIndex
Definition
getImplementByJointDescIndex(integer jointDescIndex)
Arguments
integerjointDescIndexjoint desc index
Return Values
tableimplementimplement
Code
2699function AttacherJoints:getImplementByJointDescIndex(jointDescIndex)
2700 local spec = self.spec_attacherJoints
2701
2702 for i, implement in pairs(spec.attachedImplements) do
2703 if implement.jointDescIndex == jointDescIndex then
2704 return implement
2705 end
2706 end
2707
2708 return nil
2709end

getImplementByObject

Description
Returns implement by object
Definition
getImplementByObject(table object)
Arguments
tableobjectobject of attached implement
Return Values
tableimplementimplement
Code
2731function AttacherJoints:getImplementByObject(object)
2732 local spec = self.spec_attacherJoints
2733
2734 for i, implement in pairs(spec.attachedImplements) do
2735 if implement.object == object then
2736 return implement
2737 end
2738 end
2739
2740 return nil
2741end

getImplementFromAttacherJointIndex

Description
Definition
getImplementFromAttacherJointIndex()
Code
1126function AttacherJoints:getImplementFromAttacherJointIndex(attacherJointIndex)
1127 local spec = self.spec_attacherJoints
1128 for _,attachedImplement in pairs(spec.attachedImplements) do
1129 if attachedImplement.jointDescIndex == attacherJointIndex then
1130 return attachedImplement
1131 end
1132 end
1133end

getImplementIndexByJointDescIndex

Description
Returns implement index in 'self.attachedImplements' by jointDescIndex
Definition
getImplementIndexByJointDescIndex(integer jointDescIndex)
Arguments
integerjointDescIndexjoint desc index
Return Values
integerindexindex of implement
Code
2683function AttacherJoints:getImplementIndexByJointDescIndex(jointDescIndex)
2684 local spec = self.spec_attacherJoints
2685
2686 for i, implement in pairs(spec.attachedImplements) do
2687 if implement.jointDescIndex == jointDescIndex then
2688 return i
2689 end
2690 end
2691
2692 return nil
2693end

getImplementIndexByObject

Description
Returns implement index in 'self.attachedImplements' by object
Definition
getImplementIndexByObject(table object)
Arguments
tableobjectobject of attached implement
Return Values
integerindexindex of implement
Code
2715function AttacherJoints:getImplementIndexByObject(object)
2716 local spec = self.spec_attacherJoints
2717
2718 for i, implement in pairs(spec.attachedImplements) do
2719 if implement.object == object then
2720 return i
2721 end
2722 end
2723
2724 return nil
2725end

getIsAttacherJointHeightNodeActive

Description
Definition
getIsAttacherJointHeightNodeActive()
Code
3698function AttacherJoints:getIsAttacherJointHeightNodeActive(superFunc, heightNode)
3699 for _, jointIndex in ipairs(heightNode.disablingAttacherJointIndices) do
3700 if self:getImplementFromAttacherJointIndex(jointIndex) ~= nil then
3701 return false
3702 end
3703 end
3704
3705 return superFunc(self, heightNode)
3706end

getIsAttachingAllowed

Description
Definition
getIsAttachingAllowed()
Code
2496function AttacherJoints:getIsAttachingAllowed(attacherJoint)
2497 if attacherJoint.jointIndex ~= 0 then
2498 return false
2499 end
2500
2501 if attacherJoint.disabledByAttacherJoints ~= nil and #attacherJoint.disabledByAttacherJoints > 0 then
2502 for i=1, #attacherJoint.disabledByAttacherJoints do
2503 local jointIndex = attacherJoint.disabledByAttacherJoints[i]
2504 if self:getImplementByJointDescIndex(jointIndex) ~= nil then
2505 return false
2506 end
2507 end
2508 end
2509
2510 return true
2511end

getIsAutomaticShiftingAllowed

Description
Definition
getIsAutomaticShiftingAllowed()
Code
3636function AttacherJoints:getIsAutomaticShiftingAllowed(superFunc)
3637 local spec = self.spec_attacherJoints
3638 local lastSpeed = self:getLastSpeed()
3639 for _, implement in pairs(spec.attachedImplements) do
3640 if lastSpeed < 2 then
3641 if implement.attachingIsInProgress then
3642 return false
3643 else
3644 local jointDescIndex = implement.jointDescIndex
3645 local jointDesc = spec.attacherJoints[jointDescIndex]
3646 if jointDesc.isMoving then
3647 return false
3648 end
3649 end
3650 end
3651
3652 local object = implement.object
3653 if object ~= nil and object.getIsAutomaticShiftingAllowed ~= nil then
3654 if not object:getIsAutomaticShiftingAllowed() then
3655 return false
3656 end
3657 end
3658 end
3659
3660 return superFunc(self)
3661end

getIsDashboardGroupActive

Description
Definition
getIsDashboardGroupActive()
Code
3677function AttacherJoints:getIsDashboardGroupActive(superFunc, group)
3678 local hasAttachment = #group.attacherJointIndices == 0
3679 for _, jointIndex in ipairs(group.attacherJointIndices) do
3680 if self:getImplementFromAttacherJointIndex(jointIndex) ~= nil then
3681 hasAttachment = true
3682 end
3683 end
3684
3685 return superFunc(self, group) and hasAttachment
3686end

getIsFoldAllowed

Description
Definition
getIsFoldAllowed()
Code
3737function AttacherJoints:getIsFoldAllowed(superFunc, direction, onAiTurnOn)
3738 local spec = self.spec_attacherJoints
3739 for attacherJointIndex, attacherJoint in ipairs(spec.attacherJoints) do
3740 if not attacherJoint.allowFoldingWhileAttached then
3741 if attacherJoint.jointIndex ~= 0 then
3742 return false, spec.texts.warningFoldingAttacherJoint
3743 end
3744 end
3745 end
3746
3747 return superFunc(self, direction, onAiTurnOn)
3748end

getIsHardAttachAllowed

Description
Returns if attacher joint supports hard attach
Definition
getIsHardAttachAllowed(integer jointDescIndex)
Arguments
integerjointDescIndexindex of joint
Return Values
booleansupportsHardAttachattacher joint supports hard attach
Code
2839function AttacherJoints:getIsHardAttachAllowed(jointDescIndex)
2840 local spec = self.spec_attacherJoints
2841
2842 return spec.attacherJoints[jointDescIndex].supportsHardAttach
2843end

getIsReadyForAutomatedTrainTravel

Description
Definition
getIsReadyForAutomatedTrainTravel()
Code
3620function AttacherJoints:getIsReadyForAutomatedTrainTravel(superFunc)
3621 local spec = self.spec_attacherJoints
3622 for _,implement in pairs(spec.attachedImplements) do
3623 local object = implement.object
3624 if object ~= nil and object.getIsReadyForAutomatedTrainTravel ~= nil then
3625 if not object:getIsReadyForAutomatedTrainTravel() then
3626 return false
3627 end
3628 end
3629 end
3630
3631 return superFunc(self)
3632end

getIsWheelFoliageDestructionAllowed

Description
Returns true if foliage destruction is allowed
Definition
getIsWheelFoliageDestructionAllowed()
Return Values
booleanisAllowedtfoliage destruction is allowed
Code
3753function AttacherJoints:getIsWheelFoliageDestructionAllowed(superFunc, wheel)
3754 if not superFunc(self, wheel) then
3755 return false
3756 end
3757
3758 local spec = self.spec_attacherJoints
3759 for _,implement in pairs(spec.attachedImplements) do
3760 local object = implement.object
3761 if object ~= nil then
3762 if object.getBlockFoliageDestruction ~= nil then
3763 if object:getBlockFoliageDestruction() then
3764 return false
3765 end
3766 end
3767 end
3768 end
3769
3770 return true
3771end

getJointMoveDown

Description
Returns the current joint move down state
Definition
getJointMoveDown(integer jointDescIndex)
Arguments
integerjointDescIndexindex of joint desc
Return Values
booleanmoveDownmove down
Code
2826function AttacherJoints:getJointMoveDown(jointDescIndex)
2827 local jointDesc = self.spec_attacherJoints.attacherJoints[jointDescIndex]
2828 if jointDesc.allowsLowering then
2829 return jointDesc.moveDown
2830 end
2831
2832 return false
2833end

getObjectFromImplementIndex

Description
Definition
getObjectFromImplementIndex()
Code
1170function AttacherJoints:getObjectFromImplementIndex(implementIndex)
1171 local spec = self.spec_attacherJoints
1172 local attachedImplement = spec.attachedImplements[implementIndex]
1173 if attachedImplement ~= nil then
1174 return attachedImplement.object
1175 end
1176 return nil
1177end

getPowerTakeOffConfigIndex

Description
Definition
getPowerTakeOffConfigIndex()
Code
3815function AttacherJoints:getPowerTakeOffConfigIndex(superFunc)
3816 local index = superFunc(self)
3817 index = self.xmlFile:getValue("vehicle.attacherJoints#powerTakeOffConfigId", index)
3818
3819 if self.configurations["attacherJoint"] ~= nil then
3820 local configKey = string.format("vehicle.attacherJoints.attacherJointConfigurations.attacherJointConfiguration(%d)", self.configurations["attacherJoint"] - 1)
3821 index = self.xmlFile:getValue(configKey .. "#powerTakeOffConfigId", index)
3822 end
3823
3824 return index
3825end

getSelectedImplement

Description
Definition
getSelectedImplement()
Code
2434function AttacherJoints:getSelectedImplement()
2435 local spec = self.spec_attacherJoints
2436
2437 -- check if implement is still attached
2438 if spec.selectedImplement ~= nil then
2439 if spec.selectedImplement.object:getAttacherVehicle() ~= self then
2440 return nil
2441 end
2442 end
2443
2444 return spec.selectedImplement
2445end

getShowDetachAttachedImplement

Description
Definition
getShowDetachAttachedImplement()
Code
2455function AttacherJoints:getShowDetachAttachedImplement()
2456 if self:getIsAIActive() then
2457 return false
2458 end
2459
2460 local spec = self.spec_attacherJoints
2461 local info = spec.attachableInfo
2462
2463 if info.attacherVehicle == nil then
2464 local selectedVehicle = self:getSelectedVehicle()
2465 if selectedVehicle ~= nil and not selectedVehicle.isDeleted then
2466 if selectedVehicle.getAttacherVehicle ~= nil and selectedVehicle:getAttacherVehicle() ~= nil then
2467 return true
2468 end
2469 end
2470 end
2471
2472 return false
2473end

getTotalMass

Description
Returns total mass of vehicle (optional including attached vehicles)
Definition
getTotalMass(boolean onlyGivenVehicle)
Arguments
booleanonlyGivenVehicleuse only the given vehicle, if false or nil it includes all attachables
Return Values
floattotalMasstotal mass
Code
3456function AttacherJoints:getTotalMass(superFunc, onlyGivenVehicle)
3457 local spec = self.spec_attacherJoints
3458 local mass = superFunc(self)
3459
3460 if onlyGivenVehicle == nil or not onlyGivenVehicle then
3461 for _, implement in pairs(spec.attachedImplements) do
3462 local object = implement.object
3463 if object ~= nil then
3464 mass = mass + object:getTotalMass(onlyGivenVehicle)
3465 end
3466 end
3467 end
3468
3469 return mass
3470end

groundHeightNodeCheckCallback

Description
Callback used when raycast hits an object.
Definition
groundHeightNodeCheckCallback(integer hitObjectId, float x, float y, float z, float distance, float nx, float ny, float nz, integer subShapeIndex, integer shapeId, boolean isLast)
Arguments
integerhitObjectIdscenegraph object id
floatxworld x hit position
floatyworld y hit position
floatzworld z hit position
floatdistancedistance at which the cast hit the object
floatnxnormal x direction
floatnynormal y direction
floatnznormal z direction
integersubShapeIndexsub shape index
integershapeIdid of shape
booleanisLastis last hit
Return Values
boolreturnfalse to stop raycast
Code
1530function AttacherJoints:groundHeightNodeCheckCallback(hitObjectId, x, y, z, distance, nx, ny, nz, subShapeIndex, shapeId, isLast)
1531 if self.isDeleted then
1532 return
1533 end
1534
1535 local checkData = self.spec_attacherJoints.groundHeightNodeCheckData
1536
1537 if hitObjectId ~= 0 then
1538 if getRigidBodyType(hitObjectId) == RigidBodyType.STATIC then
1539 if distance < checkData.minDistance then
1540 --#debug if VehicleDebug.state == VehicleDebug.DEBUG then
1541 --#debug DebugUtil.drawDebugGizmoAtWorldPos(x, y, z, 0, 0, 1, 0, 1, 0, "", false)
1542 --#debug end
1543
1544 checkData.raycastDistance = checkData.currentRaycastDistance
1545 checkData.minDistance = distance
1546 checkData.hit = true
1547
1548 checkData.raycastWorldPos[1] = checkData.currentRaycastWorldPos[1]
1549 checkData.raycastWorldPos[2] = checkData.currentRaycastWorldPos[2]
1550 checkData.raycastWorldPos[3] = checkData.currentRaycastWorldPos[3]
1551
1552 checkData.raycastWorldDir[1] = checkData.currentRaycastWorldDir[1]
1553 checkData.raycastWorldDir[2] = checkData.currentRaycastWorldDir[2]
1554 checkData.raycastWorldDir[3] = checkData.currentRaycastWorldDir[3]
1555
1556 checkData.jointTransformPos[1] = checkData.currentJointTransformPos[1]
1557 checkData.jointTransformPos[2] = checkData.currentJointTransformPos[2]
1558 checkData.jointTransformPos[3] = checkData.currentJointTransformPos[3]
1559 end
1560 else
1561 if not isLast then
1562 return true
1563 end
1564 end
1565 end
1566
1567 checkData.index = checkData.index + 1
1568 if checkData.index > #checkData.heightNodes then
1569 self:finishGroundHeightNodeCheck()
1570 else
1571 checkData.isDirty = true
1572 end
1573
1574 return false
1575end

handleLowerImplementByAttacherJointIndex

Description
Definition
handleLowerImplementByAttacherJointIndex()
Code
1071function AttacherJoints:handleLowerImplementByAttacherJointIndex(attacherJointIndex, direction)
1072 if attacherJointIndex ~= nil then
1073 local implement = self:getImplementByJointDescIndex(attacherJointIndex)
1074 if implement ~= nil then
1075 local object = implement.object
1076 local attacherJoints = self:getAttacherJoints()
1077 local attacherJoint = attacherJoints[attacherJointIndex]
1078
1079 local allowsLowering, warning = object:getAllowsLowering()
1080 if allowsLowering and attacherJoint.allowsLowering then
1081 if direction == nil then
1082 direction = not attacherJoint.moveDown
1083 end
1084 self:setJointMoveDown(implement.jointDescIndex, direction, false)
1085 elseif not allowsLowering and warning ~= nil then
1086 g_currentMission:showBlinkingWarning(warning, 2000)
1087 end
1088 end
1089 end
1090end

handleLowerImplementEvent

Description
Definition
handleLowerImplementEvent()
Code
1053function AttacherJoints:handleLowerImplementEvent(vehicle)
1054 local implement = self:getImplementByObject(vehicle or self:getSelectedVehicle())
1055 if implement ~= nil then
1056 local object = implement.object
1057 if object ~= nil and object.getAttacherVehicle ~= nil then
1058
1059 local attacherVehicle = object:getAttacherVehicle()
1060 if attacherVehicle ~= nil then
1061
1062 local attacherJointIndex = attacherVehicle:getAttacherJointIndexFromObject(object)
1063 attacherVehicle:handleLowerImplementByAttacherJointIndex(attacherJointIndex)
1064 end
1065 end
1066 end
1067end

hardAttachImplement

Description
Hard attach implement
Definition
hardAttachImplement(table implement)
Arguments
tableimplementimplement to attach
Code
2076function AttacherJoints:hardAttachImplement(implement)
2077 local spec = self.spec_attacherJoints
2078
2079 local implements = {}
2080 local attachedImplements
2081 if implement.object.getAttachedImplements ~= nil then
2082 attachedImplements = implement.object:getAttachedImplements()
2083 end
2084 if attachedImplements ~= nil then
2085 for i=#attachedImplements, 1, -1 do
2086 local impl = attachedImplements[i]
2087 local object = impl.object
2088 local jointDescIndex = impl.jointDescIndex
2089 local jointDesc = implement.object.spec_attacherJoints.attacherJoints[jointDescIndex]
2090 local inputJointDescIndex = object.spec_attachable.inputAttacherJointDescIndex
2091 local moveDown = jointDesc.moveDown
2092 table.insert(implements, 1, {object=object, implementIndex=i, jointDescIndex=jointDescIndex, inputJointDescIndex=inputJointDescIndex, moveDown=moveDown})
2093 implement.object:detachImplement(1, true)
2094 end
2095 end
2096
2097 local attacherJoint = spec.attacherJoints[implement.jointDescIndex]
2098 local implementJoint = implement.object.spec_attachable.attacherJoint
2099
2100 local baseVehicleComponentNode = self:getParentComponent(attacherJoint.jointTransform)
2101 local attachedVehicleComponentNode = implement.object:getParentComponent(implement.object.spec_attachable.attacherJoint.node)
2102
2103 -- remove all components from physics
2104 local currentVehicle = self
2105 while currentVehicle ~= nil do
2106 currentVehicle:removeFromPhysics()
2107 currentVehicle = currentVehicle.attacherVehicle
2108 end
2109 implement.object:removeFromPhysics()
2110
2111 -- set valid baseVehicle compound
2112 if spec.attacherVehicle == nil then
2113 setIsCompound(baseVehicleComponentNode, true)
2114 end
2115 -- set attachedVehicle to compound child
2116 setIsCompoundChild(attachedVehicleComponentNode, true)
2117
2118 -- set direction and local position
2119 local dirX, dirY, dirZ = localDirectionToLocal(attachedVehicleComponentNode, implementJoint.node, 0, 0, 1)
2120 local upX, upY, upZ = localDirectionToLocal(attachedVehicleComponentNode, implementJoint.node, 0, 1, 0)
2121 setDirection(attachedVehicleComponentNode, dirX, dirY, dirZ, upX, upY, upZ)
2122 local x,y,z = localToLocal(attachedVehicleComponentNode, implementJoint.node, 0, 0, 0)
2123 setTranslation(attachedVehicleComponentNode, x, y, z)
2124 link(attacherJoint.jointTransform, attachedVehicleComponentNode)
2125
2126 -- link visual and set to correct position
2127 if implementJoint.visualNode ~= nil and attacherJoint.jointTransformVisual ~= nil then
2128 local dirX, dirY, dirZ = localDirectionToLocal(implementJoint.visualNode, implementJoint.node, 0, 0, 1)
2129 local upX, upY, upZ = localDirectionToLocal(implementJoint.visualNode, implementJoint.node, 0, 1, 0)
2130 setDirection(implementJoint.visualNode, dirX, dirY, dirZ, upX, upY, upZ)
2131 local x,y,z = localToLocal(implementJoint.visualNode, implementJoint.node, 0, 0, 0)
2132 setTranslation(implementJoint.visualNode, x, y, z)
2133 link(attacherJoint.jointTransformVisual, implementJoint.visualNode)
2134 end
2135
2136 implement.object.spec_attachable.isHardAttached = true
2137
2138 -- add to physics again
2139 local currentVehicle = self
2140 while currentVehicle ~= nil do
2141 currentVehicle:addToPhysics()
2142 currentVehicle = currentVehicle.attacherVehicle
2143 end
2144
2145 -- set new joint rootNodes
2146 for _, attacherJoint in pairs(implement.object.spec_attacherJoints.attacherJoints) do
2147 attacherJoint.rootNode = self.rootNode
2148 end
2149
2150 for _, impl in pairs(implements) do
2151 implement.object:attachImplement(impl.object, impl.inputJointDescIndex, impl.jointDescIndex, true, impl.implementIndex, impl.moveDown, true)
2152 end
2153
2154 if self.isServer then
2155 self:raiseDirtyFlags(self.vehicleDirtyFlag)
2156 end
2157
2158 return true
2159end

hardDetachImplement

Description
Hard detach implement
Definition
hardDetachImplement(table implement)
Arguments
tableimplementimplement to detach
Code
2164function AttacherJoints:hardDetachImplement(implement)
2165 -- restore original joint rootNode
2166 for _, attacherJoint in pairs(implement.object.spec_attacherJoints.attacherJoints) do
2167 attacherJoint.rootNode = attacherJoint.rootNodeBackup
2168 end
2169
2170 local implementJoint = implement.object.spec_attachable.attacherJoint
2171
2172 local attachedVehicleComponentNode = implement.object:getParentComponent(implementJoint.node)
2173
2174 local currentVehicle = self
2175 while currentVehicle ~= nil do
2176 currentVehicle:removeFromPhysics()
2177 currentVehicle = currentVehicle.attacherVehicle
2178 end
2179 --implement.object:removeFromPhysics()
2180
2181 setIsCompound(attachedVehicleComponentNode, true)
2182
2183 local x,y,z = getWorldTranslation(attachedVehicleComponentNode)
2184 setTranslation(attachedVehicleComponentNode, x,y,z)
2185 local dirX, dirY, dirZ = localDirectionToWorld(implement.object.rootNode, 0, 0, 1)
2186 local upX, upY, upZ = localDirectionToWorld(implement.object.rootNode, 0, 1, 0)
2187 setDirection(attachedVehicleComponentNode, dirX, dirY, dirZ, upX, upY, upZ)
2188 link(getRootNode(), attachedVehicleComponentNode)
2189
2190 if implementJoint.visualNode ~= nil and getParent(implementJoint.visualNode) ~= implementJoint.visualNodeData.parent then
2191 link(implementJoint.visualNodeData.parent, implementJoint.visualNode, implementJoint.visualNodeData.index)
2192 setRotation(implementJoint.visualNode, implementJoint.visualNodeData.rotation[1], implementJoint.visualNodeData.rotation[2], implementJoint.visualNodeData.rotation[3])
2193 setTranslation(implementJoint.visualNode, implementJoint.visualNodeData.translation[1], implementJoint.visualNodeData.translation[2], implementJoint.visualNodeData.translation[3])
2194 end
2195
2196 local currentVehicle = self
2197 while currentVehicle ~= nil do
2198 currentVehicle:addToPhysics()
2199 currentVehicle = currentVehicle.attacherVehicle
2200 end
2201 implement.object:addToPhysics()
2202 implement.object.spec_attachable.isHardAttached = false
2203
2204 if self.isServer then
2205 self:raiseDirtyFlags(self.vehicleDirtyFlag)
2206 end
2207
2208 return true
2209end

initSpecialization

Description
Definition
initSpecialization()
Code
23function AttacherJoints.initSpecialization()
24 g_configurationManager:addConfigurationType("attacherJoint", g_i18n:getText("configuration_attacherJoint"), "attacherJoints", nil, nil, nil, ConfigurationUtil.SELECTOR_MULTIOPTION)
25
26 Vehicle.registerStateChange("ATTACH")
27 Vehicle.registerStateChange("DETACH")
28 Vehicle.registerStateChange("LOWER_ALL_IMPLEMENTS")
29
30 local schema = Vehicle.xmlSchema
31 schema:setXMLSpecializationType("AttacherJoints")
32
33 AttacherJoints.registerAttacherJointXMLPaths(schema, "vehicle.attacherJoints.attacherJoint(?)")
34 AttacherJoints.registerAttacherJointXMLPaths(schema, "vehicle.attacherJoints.attacherJointConfigurations.attacherJointConfiguration(?).attacherJoint(?)")
35
36 ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.attacherJoints.attacherJointConfigurations.attacherJointConfiguration(?)")
37 SoundManager.registerSampleXMLPaths(schema, "vehicle.attacherJoints.sounds", "hydraulic")
38 SoundManager.registerSampleXMLPaths(schema, "vehicle.attacherJoints.sounds", "attach")
39 SoundManager.registerSampleXMLPaths(schema, "vehicle.attacherJoints.sounds", "detach")
40
41 schema:register(XMLValueType.FLOAT, "vehicle.attacherJoints#comboDuration", "Combo duration", 2)
42
43 schema:register(XMLValueType.INT, "vehicle.attacherJoints#connectionHoseConfigId", "Connection hose configuration index to use")
44 schema:register(XMLValueType.INT, "vehicle.attacherJoints#powerTakeOffConfigId", "Power take off configuration index to use")
45 schema:register(XMLValueType.INT, "vehicle.attacherJoints.attacherJointConfigurations.attacherJointConfiguration(?)#connectionHoseConfigId", "Connection hose configuration index to use")
46 schema:register(XMLValueType.INT, "vehicle.attacherJoints.attacherJointConfigurations.attacherJointConfiguration(?)#powerTakeOffConfigId", "Power take off configuration index to use")
47
48 schema:register(XMLValueType.FLOAT, "vehicle.attacherJoints#maxUpdateDistance", "Max. distance to vehicle root to update attacher joint graphics", AttacherJoints.DEFAULT_MAX_UPDATE_DISTANCE)
49
50 schema:register(XMLValueType.VECTOR_N, Dashboard.GROUP_XML_KEY .. "#attacherJointIndices", "Attacher joint indices")
51
52 schema:register(XMLValueType.VECTOR_N, Attachable.INPUT_ATTACHERJOINT_XML_KEY .. ".heightNode(?)#disablingAttacherJointIndices", "Attacher joint indices that disable heigth node if something is attached")
53 schema:register(XMLValueType.VECTOR_N, Attachable.INPUT_ATTACHERJOINT_CONFIG_XML_KEY .. ".heightNode(?)#disablingAttacherJointIndices", "Attacher joint indices that disable heigth node if something is attached")
54
55 schema:setXMLSpecializationType()
56
57 local schemaSavegame = Vehicle.xmlSchemaSavegame
58 schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).attacherJoints#comboDirection", "Current combo direction")
59
60 schemaSavegame:register(XMLValueType.INT, "vehicles.attachments(?)#rootVehicleId", "Root vehicle id")
61 schemaSavegame:register(XMLValueType.INT, "vehicles.attachments(?).attachment(?)#attachmentId", "Attachment vehicle id")
62 schemaSavegame:register(XMLValueType.INT, "vehicles.attachments(?).attachment(?)#inputJointDescIndex", "Index of input attacher joint", 1)
63 schemaSavegame:register(XMLValueType.INT, "vehicles.attachments(?).attachment(?)#jointIndex", "Index of attacher joint")
64 schemaSavegame:register(XMLValueType.BOOL, "vehicles.attachments(?).attachment(?)#moveDown", "Attachment lowered or lifted")
65end

isDetachAllowed

Description
Returns true if detach is allowed
Definition
isDetachAllowed()
Return Values
booleandetachAlloweddetach is allowed
Code
3711function AttacherJoints:isDetachAllowed(superFunc)
3712 local detachAllowed, warning, showWarning = superFunc(self)
3713 if not detachAllowed then
3714 return detachAllowed, warning, showWarning
3715 end
3716
3717 local spec = self.spec_attacherJoints
3718 for attacherJointIndex, attacherJoint in ipairs(spec.attacherJoints) do
3719 if not attacherJoint.allowDetachingWhileLifted then
3720 if not attacherJoint.moveDown then
3721 local implement = self:getImplementByJointDescIndex(attacherJointIndex)
3722 if implement ~= nil then
3723 local inputAttacherJoint = implement.object:getInputAttacherJointByJointDescIndex(implement.inputJointDescIndex)
3724 if inputAttacherJoint ~= nil and not inputAttacherJoint.forceAllowDetachWhileLifted then
3725 return false, string.format(spec.texts.lowerImplementFirst, implement.object.typeDesc)
3726 end
3727 end
3728 end
3729 end
3730 end
3731
3732 return true
3733end

loadAttacherJointFromXML

Description
Load attacher joint from xml
Definition
loadAttacherJointFromXML(table attacherJoint, integer fileId, string baseName, integer index)
Arguments
tableattacherJointattacherJoint
integerfileIdxml file id
stringbaseNamebaseName
integerindexindex of attacher joint
Code
2851function AttacherJoints:loadAttacherJointFromXML(attacherJoint, xmlFile, baseName, index)
2852 local spec = self.spec_attacherJoints
2853
2854 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#index", baseName .. "#node") -- FS17
2855 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#indexVisual", baseName .. "#nodeVisual") -- FS17
2856 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#ptoOutputNode", "vehicle.powerTakeOffs.output") -- FS17 to FS19
2857 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#lowerDistanceToGround", baseName..".distanceToGround#lower") -- FS17 to FS19
2858 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#upperDistanceToGround", baseName..".distanceToGround#upper") -- FS17 to FS19
2859 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#rotationNode", baseName..".rotationNode#node") -- FS17 to FS19
2860 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#upperRotation", baseName..".rotationNode#upperRotation") -- FS17 to FS19
2861 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#lowerRotation", baseName..".rotationNode#lowerRotation") -- FS17 to FS19
2862 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#startRotation", baseName..".rotationNode#startRotation") -- FS17 to FS19
2863 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#rotationNode2", baseName..".rotationNode2#node") -- FS17 to FS19
2864 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#upperRotation2", baseName..".rotationNode2#upperRotation") -- FS17 to FS19
2865 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#lowerRotation2", baseName..".rotationNode2#lowerRotation") -- FS17 to FS19
2866 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#transNode", baseName..".transNode#node") -- FS17 to FS19
2867 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#transNodeMinY", baseName..".transNode#minY") -- FS17 to FS19
2868 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#transNodeMaxY", baseName..".transNode#maxY") -- FS17 to FS19
2869 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#transNodeHeight", baseName..".transNode#height") -- FS17 to FS19
2870
2871
2872 local node = xmlFile:getValue(baseName.. "#node", nil, self.components, self.i3dMappings)
2873 if node == nil then
2874 Logging.xmlWarning(self.xmlFile, "Missing node for attacherJoint '%s'", baseName)
2875 return false
2876 end
2877
2878 attacherJoint.jointTransform = node
2879 attacherJoint.jointComponent = self:getParentComponent(attacherJoint.jointTransform)
2880
2881 attacherJoint.jointTransformVisual = xmlFile:getValue(baseName .. "#nodeVisual", nil, self.components, self.i3dMappings)
2882 attacherJoint.supportsHardAttach = xmlFile:getValue(baseName.."#supportsHardAttach", true)
2883
2884 attacherJoint.jointOrigOffsetComponent = { localToLocal(attacherJoint.jointComponent, attacherJoint.jointTransform, 0, 0, 0) }
2885 attacherJoint.jointOrigDirOffsetComponent = { localDirectionToLocal(attacherJoint.jointComponent, attacherJoint.jointTransform, 0, 0, 1) }
2886
2887 attacherJoint.jointTransformOrig = createTransformGroup("jointTransformOrig")
2888 link(getParent(node), attacherJoint.jointTransformOrig)
2889 setTranslation(attacherJoint.jointTransformOrig, getTranslation(node))
2890 setRotation(attacherJoint.jointTransformOrig, getRotation(node))
2891
2892 local jointTypeStr = xmlFile:getValue(baseName.. "#jointType")
2893 local jointType
2894 if jointTypeStr ~= nil then
2895 jointType = AttacherJoints.jointTypeNameToInt[jointTypeStr]
2896 if jointType == nil then
2897 Logging.xmlWarning(self.xmlFile, "Invalid jointType '%s' for attacherJoint '%s'!", tostring(jointTypeStr), baseName)
2898 end
2899 end
2900 if jointType == nil then
2901 jointType = AttacherJoints.JOINTTYPE_IMPLEMENT
2902 end
2903 attacherJoint.jointType = jointType
2904
2905 local subTypeStr = xmlFile:getValue(baseName.. ".subType#name")
2906 attacherJoint.subTypes = string.split(subTypeStr, " ")
2907 if #attacherJoint.subTypes == 0 then
2908 attacherJoint.subTypes = nil
2909 end
2910
2911 local brandRestrictionStr = xmlFile:getValue(baseName.. ".subType#brandRestriction")
2912 attacherJoint.brandRestrictions = string.split(brandRestrictionStr, " ")
2913 if #attacherJoint.brandRestrictions == 0 then
2914 attacherJoint.brandRestrictions = nil
2915 else
2916 for i=1, #attacherJoint.brandRestrictions do
2917 local brand = g_brandManager:getBrandByName(attacherJoint.brandRestrictions[i])
2918 if brand ~= nil then
2919 attacherJoint.brandRestrictions[i] = brand
2920 else
2921 Logging.xmlError(xmlFile, "Unknown brand '%s' in '%s'", attacherJoint.brandRestrictions[i], baseName.. ".subType#brandRestriction")
2922 attacherJoint.brandRestrictions = nil
2923 break
2924 end
2925 end
2926 end
2927
2928 local vehicleRestrictionStr = xmlFile:getValue(baseName.. ".subType#vehicleRestriction")
2929 attacherJoint.vehicleRestrictions = string.split(vehicleRestrictionStr, " ")
2930 if #attacherJoint.vehicleRestrictions == 0 then
2931 attacherJoint.vehicleRestrictions = nil
2932 end
2933
2934 attacherJoint.subTypeShowWarning = xmlFile:getValue(baseName.. ".subType#subTypeShowWarning", true)
2935
2936 attacherJoint.allowsJointLimitMovement = xmlFile:getValue(baseName.."#allowsJointLimitMovement", true)
2937 attacherJoint.allowsLowering = xmlFile:getValue(baseName.."#allowsLowering", true)
2938 attacherJoint.isDefaultLowered = xmlFile:getValue(baseName.."#isDefaultLowered", false)
2939
2940 attacherJoint.allowDetachingWhileLifted = xmlFile:getValue(baseName.."#allowDetachingWhileLifted", true)
2941 attacherJoint.allowFoldingWhileAttached = xmlFile:getValue(baseName.."#allowFoldingWhileAttached", true)
2942
2943 if jointType == AttacherJoints.JOINTTYPE_TRAILER or jointType == AttacherJoints.JOINTTYPE_TRAILERLOW then
2944 attacherJoint.allowsLowering = false
2945 end
2946
2947 attacherJoint.canTurnOnImplement = xmlFile:getValue(baseName.."#canTurnOnImplement", true)
2948
2949 local rotationNode = xmlFile:getValue(baseName.. ".rotationNode#node", nil, self.components, self.i3dMappings)
2950 if rotationNode ~= nil then
2951 attacherJoint.rotationNode = rotationNode
2952
2953 attacherJoint.lowerRotation = xmlFile:getValue(baseName..".rotationNode#lowerRotation", "0 0 0", true)
2954 attacherJoint.upperRotation = xmlFile:getValue(baseName..".rotationNode#upperRotation", {getRotation(rotationNode)}, true)
2955 attacherJoint.rotX, attacherJoint.rotY, attacherJoint.rotZ = xmlFile:getValue(baseName..".rotationNode#startRotation", {getRotation(rotationNode)})
2956
2957 local lowerValues = {attacherJoint.lowerRotation[1], attacherJoint.lowerRotation[2], attacherJoint.lowerRotation[3]}
2958 local upperValues = {attacherJoint.upperRotation[1], attacherJoint.upperRotation[2], attacherJoint.upperRotation[3]}
2959
2960 for i=1, 3 do
2961 local l = lowerValues[i]
2962 local u = upperValues[i]
2963
2964 if l > u then
2965 upperValues[i] = l
2966 lowerValues[i] = u
2967 end
2968 end
2969
2970 attacherJoint.rotX = MathUtil.clamp(attacherJoint.rotX, lowerValues[1], upperValues[1])
2971 attacherJoint.rotY = MathUtil.clamp(attacherJoint.rotY, lowerValues[2], upperValues[2])
2972 attacherJoint.rotZ = MathUtil.clamp(attacherJoint.rotZ, lowerValues[3], upperValues[3])
2973 end
2974
2975 local rotationNode2 = xmlFile:getValue(baseName.. ".rotationNode2#node", nil, self.components, self.i3dMappings)
2976 if rotationNode2 ~= nil then
2977 attacherJoint.rotationNode2 = rotationNode2
2978
2979 local defaultLowerRotation2 = {-attacherJoint.lowerRotation[1], -attacherJoint.lowerRotation[2], -attacherJoint.lowerRotation[3]}
2980 attacherJoint.lowerRotation2 = xmlFile:getValue(baseName..".rotationNode2#lowerRotation", defaultLowerRotation2, true)
2981
2982 local defaultUpperRotation2 = {-attacherJoint.upperRotation[1], -attacherJoint.upperRotation[2], -attacherJoint.upperRotation[3]}
2983 attacherJoint.upperRotation2 = xmlFile:getValue(baseName..".rotationNode2#upperRotation", defaultUpperRotation2, true)
2984 end
2985
2986 attacherJoint.transNode = xmlFile:getValue(baseName..".transNode#node", nil, self.components, self.i3dMappings)
2987 if attacherJoint.transNode ~= nil then
2988 attacherJoint.transNodeOrgTrans = {getTranslation(attacherJoint.transNode)}
2989 attacherJoint.transNodeHeight = xmlFile:getValue(baseName..".transNode#height", 0.12)
2990 attacherJoint.transNodeMinY = xmlFile:getValue(baseName..".transNode#minY")
2991 attacherJoint.transNodeMaxY = xmlFile:getValue(baseName..".transNode#maxY")
2992
2993 attacherJoint.transNodeDependentBottomArm = xmlFile:getValue(baseName..".transNode.dependentBottomArm#node", nil, self.components, self.i3dMappings)
2994 attacherJoint.transNodeDependentBottomArmThreshold = xmlFile:getValue(baseName..".transNode.dependentBottomArm#threshold", math.huge)
2995 attacherJoint.transNodeDependentBottomArmRotation = xmlFile:getValue(baseName..".transNode.dependentBottomArm#rotation", "0 0 0", true)
2996 end
2997
2998 -- lowerDistanceToGround is a mandatory attribute if a rotationNode is available
2999 if (attacherJoint.rotationNode ~= nil or attacherJoint.transNode ~= nil) and xmlFile:getValue(baseName..".distanceToGround#lower") == nil then
3000 Logging.xmlWarning(self.xmlFile, "Missing '.distanceToGround#lower' for attacherJoint '%s'. Use console command 'gsVehicleAnalyze' to get correct values!", baseName)
3001 end
3002 attacherJoint.lowerDistanceToGround = xmlFile:getValue(baseName..".distanceToGround#lower", 0.7)
3003
3004 -- upperDistanceToGround is a mandatory attribute if a rotationNode is available
3005 if (attacherJoint.rotationNode ~= nil or attacherJoint.transNode ~= nil) and xmlFile:getValue(baseName..".distanceToGround#upper") == nil then
3006 Logging.xmlWarning(self.xmlFile, "Missing '.distanceToGround#upper' for attacherJoint '%s'. Use console command 'gsVehicleAnalyze' to get correct values!", baseName)
3007 end
3008 attacherJoint.upperDistanceToGround = xmlFile:getValue(baseName..".distanceToGround#upper", 1.0)
3009
3010 if attacherJoint.lowerDistanceToGround > attacherJoint.upperDistanceToGround then
3011 Logging.xmlWarning(self.xmlFile, "distanceToGround#lower may not be larger than distanceToGround#upper for attacherJoint '%s'. Switching values!", baseName)
3012 local copy = attacherJoint.lowerDistanceToGround
3013 attacherJoint.lowerDistanceToGround = attacherJoint.upperDistanceToGround
3014 attacherJoint.upperDistanceToGround = copy
3015 end
3016
3017 attacherJoint.lowerRotationOffset = xmlFile:getValue(baseName.."#lowerRotationOffset", 0)
3018 attacherJoint.upperRotationOffset = xmlFile:getValue(baseName.."#upperRotationOffset", 0)
3019
3020 attacherJoint.lockDownRotLimit = xmlFile:getValue(baseName.."#lockDownRotLimit", false)
3021 attacherJoint.lockUpRotLimit = xmlFile:getValue(baseName.."#lockUpRotLimit", false)
3022 -- only use translimit in +y. Set -y to 0
3023 attacherJoint.lockDownTransLimit = xmlFile:getValue(baseName.."#lockDownTransLimit", true)
3024 attacherJoint.lockUpTransLimit = xmlFile:getValue(baseName.."#lockUpTransLimit", false)
3025
3026 local lowerRotLimitStr = "20 20 20"
3027 if jointType ~= AttacherJoints.JOINTTYPE_IMPLEMENT then
3028 lowerRotLimitStr = "0 0 0"
3029 end
3030 local lx, ly, lz = xmlFile:getValue(baseName.."#lowerRotLimit", lowerRotLimitStr)
3031 attacherJoint.lowerRotLimit = {}
3032 attacherJoint.lowerRotLimit[1] = math.abs(Utils.getNoNil(lx, 20))
3033 attacherJoint.lowerRotLimit[2] = math.abs(Utils.getNoNil(ly, 20))
3034 attacherJoint.lowerRotLimit[3] = math.abs(Utils.getNoNil(lz, 20))
3035 local ux, uy, uz = xmlFile:getValue(baseName.."#upperRotLimit")
3036 attacherJoint.upperRotLimit = {}
3037 attacherJoint.upperRotLimit[1] = math.abs(Utils.getNoNil(Utils.getNoNil(ux, lx), 20))
3038 attacherJoint.upperRotLimit[2] = math.abs(Utils.getNoNil(Utils.getNoNil(uy, ly), 20))
3039 attacherJoint.upperRotLimit[3] = math.abs(Utils.getNoNil(Utils.getNoNil(uz, lz), 20))
3040
3041 local lowerTransLimitStr = "0.5 0.5 0.5"
3042 if jointType ~= AttacherJoints.JOINTTYPE_IMPLEMENT then
3043 lowerTransLimitStr = "0 0 0"
3044 end
3045 lx, ly, lz = xmlFile:getValue(baseName.."#lowerTransLimit", lowerTransLimitStr)
3046 attacherJoint.lowerTransLimit = {}
3047 attacherJoint.lowerTransLimit[1] = math.abs(Utils.getNoNil(lx, 0))
3048 attacherJoint.lowerTransLimit[2] = math.abs(Utils.getNoNil(ly, 0))
3049 attacherJoint.lowerTransLimit[3] = math.abs(Utils.getNoNil(lz, 0))
3050 ux, uy, uz = xmlFile:getValue(baseName.."#upperTransLimit")
3051 attacherJoint.upperTransLimit = {}
3052 attacherJoint.upperTransLimit[1] = math.abs(Utils.getNoNil(Utils.getNoNil(ux, lx), 0))
3053 attacherJoint.upperTransLimit[2] = math.abs(Utils.getNoNil(Utils.getNoNil(uy, ly), 0))
3054 attacherJoint.upperTransLimit[3] = math.abs(Utils.getNoNil(Utils.getNoNil(uz, lz), 0))
3055
3056
3057 attacherJoint.jointPositionOffset = xmlFile:getValue(baseName.."#jointPositionOffset", "0 0 0", true)
3058
3059 attacherJoint.rotLimitSpring = xmlFile:getValue( baseName.."#rotLimitSpring", "0 0 0", true)
3060 attacherJoint.rotLimitDamping = xmlFile:getValue( baseName.."#rotLimitDamping", "1 1 1", true)
3061 attacherJoint.rotLimitForceLimit = xmlFile:getValue( baseName.."#rotLimitForceLimit", "-1 -1 -1", true)
3062
3063 attacherJoint.transLimitSpring = xmlFile:getValue( baseName.."#transLimitSpring", "0 0 0", true)
3064 attacherJoint.transLimitDamping = xmlFile:getValue( baseName.."#transLimitDamping", "1 1 1", true)
3065 attacherJoint.transLimitForceLimit = xmlFile:getValue( baseName.."#transLimitForceLimit", "-1 -1 -1", true)
3066
3067 attacherJoint.moveDefaultTime = xmlFile:getValue(baseName.."#moveTime", 0.5) * 1000
3068 attacherJoint.moveTime = attacherJoint.moveDefaultTime
3069
3070 attacherJoint.disabledByAttacherJoints = xmlFile:getValue(baseName.."#disabledByAttacherJoints", nil, true)
3071
3072 attacherJoint.enableCollision = xmlFile:getValue(baseName.."#enableCollision", false)
3073
3074 local topArmFilename = xmlFile:getValue(baseName.. ".topArm#filename")
3075 if topArmFilename ~= nil then
3076 local baseNode = xmlFile:getValue(baseName.. ".topArm#baseNode", nil, self.components, self.i3dMappings)
3077 if baseNode ~= nil then
3078 topArmFilename = Utils.getFilename(topArmFilename, self.baseDirectory)
3079 local arguments = {
3080 xmlFile = xmlFile,
3081 baseName = baseName,
3082 attacherJoint = attacherJoint,
3083 baseNode = baseNode
3084 }
3085
3086 attacherJoint.sharedLoadRequestIdTopArm = self:loadSubSharedI3DFile(topArmFilename, false, false, self.onTopArmI3DLoaded, self, arguments)
3087 end
3088 else
3089 local topArmRotationNode = xmlFile:getValue(baseName.. ".topArm#rotationNode", nil, self.components, self.i3dMappings)
3090 local translationNode = xmlFile:getValue(baseName.. ".topArm#translationNode", nil, self.components, self.i3dMappings)
3091 local referenceNode = xmlFile:getValue(baseName.. ".topArm#referenceNode", nil, self.components, self.i3dMappings)
3092 if topArmRotationNode ~= nil then
3093 local topArm = {}
3094 topArm.rotationNode = topArmRotationNode
3095 topArm.rotX, topArm.rotY, topArm.rotZ = getRotation(topArmRotationNode)
3096 if translationNode ~= nil and referenceNode ~= nil then
3097 topArm.translationNode = translationNode
3098
3099 local x,y,z = getTranslation(translationNode)
3100 if math.abs(x) >= 0.0001 or math.abs(y) >= 0.0001 or math.abs(z) >= 0.0001 then
3101 Logging.xmlWarning(self.xmlFile, "TopArm translation of attacherJoint '%s' is not 0/0/0!", baseName)
3102 end
3103 topArm.referenceDistance = calcDistanceFrom(referenceNode, translationNode)
3104 end
3105 topArm.zScale = MathUtil.sign(xmlFile:getValue(baseName.. ".topArm#zScale", 1))
3106 topArm.toggleVisibility = xmlFile:getValue(baseName.. ".topArm#toggleVisibility", false)
3107 if topArm.toggleVisibility then
3108 setVisibility(topArm.rotationNode, false)
3109 end
3110
3111 attacherJoint.topArm = topArm
3112 end
3113 end
3114
3115 local bottomArmRotationNode = xmlFile:getValue(baseName.. ".bottomArm#rotationNode", nil, self.components, self.i3dMappings)
3116 local translationNode = xmlFile:getValue(baseName.. ".bottomArm#translationNode", nil, self.components, self.i3dMappings)
3117 local referenceNode = xmlFile:getValue(baseName.. ".bottomArm#referenceNode", nil, self.components, self.i3dMappings)
3118 if bottomArmRotationNode ~= nil then
3119 local bottomArm = {}
3120 bottomArm.rotationNode = bottomArmRotationNode
3121 bottomArm.rotationNodeDir = createTransformGroup("rotationNodeDirTemp")
3122 link(getParent(bottomArmRotationNode), bottomArm.rotationNodeDir)
3123 setTranslation(bottomArm.rotationNodeDir, getTranslation(bottomArmRotationNode))
3124 setRotation(bottomArm.rotationNodeDir, getRotation(bottomArmRotationNode))
3125 bottomArm.lastDirection = {0, 0, 0}
3126 bottomArm.rotX, bottomArm.rotY, bottomArm.rotZ = xmlFile:getValue(baseName..".bottomArm#startRotation", {getRotation(bottomArmRotationNode)})
3127
3128 bottomArm.interpolatorGet = function()
3129 return getRotation(bottomArm.rotationNode)
3130 end
3131 bottomArm.interpolatorSet = function(x, y, z)
3132 setRotation(bottomArm.rotationNode, x, y, z)
3133 if self.setMovingToolDirty ~= nil then
3134 self:setMovingToolDirty(bottomArm.rotationNode)
3135 end
3136 end
3137 bottomArm.interpolatorFinished = function(_)
3138 bottomArm.bottomArmInterpolating = false
3139 end
3140
3141 bottomArm.interpolatorKey = bottomArmRotationNode .. "rotation"
3142 bottomArm.bottomArmInterpolating = false
3143
3144 if translationNode ~= nil and referenceNode ~= nil then
3145 bottomArm.translationNode = translationNode
3146
3147 local x,y,z = getTranslation(translationNode)
3148 if math.abs(x) >= 0.0001 or math.abs(y) >= 0.0001 or math.abs(z) >= 0.0001 then
3149 Logging.xmlWarning(self.xmlFile, "BottomArm translation of attacherJoint '%s' is not 0/0/0!", baseName)
3150 end
3151 bottomArm.referenceDistance = calcDistanceFrom(referenceNode, translationNode)
3152 end
3153 bottomArm.zScale = MathUtil.sign(xmlFile:getValue(baseName.. ".bottomArm#zScale", 1))
3154 bottomArm.lockDirection = xmlFile:getValue(baseName.. ".bottomArm#lockDirection", true)
3155 bottomArm.resetSpeed = xmlFile:getValue(baseName.. ".bottomArm#resetSpeed", 45)
3156
3157 bottomArm.toggleVisibility = xmlFile:getValue(baseName.. ".bottomArm#toggleVisibility", false)
3158 if bottomArm.toggleVisibility then
3159 setVisibility(bottomArm.rotationNode, false)
3160 end
3161
3162 if jointType == AttacherJoints.JOINTTYPE_IMPLEMENT then
3163 local toolbarI3dFilename = Utils.getFilename(xmlFile:getValue(baseName.. ".toolbar#filename", "$data/shared/assets/toolbar.i3d"), self.baseDirectory)
3164 local arguments = {
3165 bottomArm = bottomArm,
3166 referenceNode = referenceNode
3167 }
3168 bottomArm.sharedLoadRequestIdToolbar = self:loadSubSharedI3DFile(toolbarI3dFilename, false, false, self.onBottomArmToolbarI3DLoaded, self, arguments)
3169 end
3170
3171 attacherJoint.bottomArm = bottomArm
3172 end
3173
3174 if self.isClient then
3175 attacherJoint.sampleAttach = g_soundManager:loadSampleFromXML(xmlFile, baseName, "attachSound", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
3176 attacherJoint.sampleDetach = g_soundManager:loadSampleFromXML(xmlFile, baseName, "detachSound", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
3177 end
3178
3179 attacherJoint.steeringBarLeftNode = xmlFile:getValue(baseName.. ".steeringBars#leftNode", nil, self.components, self.i3dMappings)
3180 attacherJoint.steeringBarRightNode = xmlFile:getValue(baseName.. ".steeringBars#rightNode", nil, self.components, self.i3dMappings)
3181 attacherJoint.steeringBarForceUsage = xmlFile:getValue(baseName.. ".steeringBars#forceUsage", true)
3182
3183 attacherJoint.visualNodes = xmlFile:getValue(baseName.. ".visuals#nodes", nil, self.components, self.i3dMappings, true)
3184 for i=1, #attacherJoint.visualNodes do
3185 local visualNode = attacherJoint.visualNodes[i]
3186
3187 if spec.visualNodeToAttacherJoints[visualNode] == nil then
3188 spec.visualNodeToAttacherJoints[visualNode] = {}
3189 end
3190
3191 table.insert(spec.visualNodeToAttacherJoints[visualNode], attacherJoint)
3192 end
3193
3194 attacherJoint.hideVisuals = xmlFile:getValue(baseName.. ".visuals#hide", nil, self.components, self.i3dMappings, true)
3195 for i=1, #attacherJoint.hideVisuals do
3196 local hideNode = attacherJoint.hideVisuals[i]
3197
3198 if spec.hideVisualNodeToAttacherJoints[hideNode] == nil then
3199 spec.hideVisualNodeToAttacherJoints[hideNode] = {}
3200 end
3201
3202 table.insert(spec.hideVisualNodeToAttacherJoints[hideNode], attacherJoint)
3203 end
3204
3205 attacherJoint.changeObjects = {}
3206 ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, baseName, attacherJoint.changeObjects, self.components, self)
3207 ObjectChangeUtil.setObjectChanges(attacherJoint.changeObjects, false, self, self.setMovingToolDirty, true)
3208 attacherJoint.delayedObjectChanges = xmlFile:getValue(baseName.."#delayedObjectChanges", true)
3209 attacherJoint.delayedObjectChangesOnAttach = xmlFile:getValue(baseName.."#delayedObjectChangesOnAttach", false)
3210
3211 attacherJoint.additionalAttachment = {}
3212 attacherJoint.additionalAttachment.attacherJointDirection = xmlFile:getValue(baseName..".additionalAttachment#attacherJointDirection")
3213
3214 attacherJoint.rootNode = xmlFile:getValue(baseName.."#rootNode", self.components[1].node, self.components, self.i3dMappings)
3215 attacherJoint.rootNodeBackup = attacherJoint.rootNode
3216 attacherJoint.jointIndex = 0
3217
3218 attacherJoint.comboTime = xmlFile:getValue(baseName .. "#comboTime")
3219
3220 local schemaKey = baseName.. ".schema"
3221 if xmlFile:hasProperty(schemaKey) then
3222 local x, y = xmlFile:getValue(schemaKey .. "#position")
3223 local liftedOffsetX, liftedOffsetY = xmlFile:getValue(schemaKey.."#liftedOffset", "0 5")
3224
3225 self.schemaOverlay:addAttacherJoint(x, y,
3226 xmlFile:getValue(schemaKey .. "#rotation", 0),
3227 xmlFile:getValue(schemaKey .. "#invertX", false),
3228 liftedOffsetX, liftedOffsetY)
3229 else
3230 Logging.xmlWarning(self.xmlFile, "Missing schema overlay attacherJoint '%s'!", baseName)
3231 end
3232
3233 return true
3234end

loadAttacherJointHeightNode

Description
Definition
loadAttacherJointHeightNode()
Code
3690function AttacherJoints:loadAttacherJointHeightNode(superFunc, xmlFile, key, heightNode, attacherJointNode)
3691 heightNode.disablingAttacherJointIndices = xmlFile:getValue(key .. "#disablingAttacherJointIndices", "", true)
3692
3693 return superFunc(self, xmlFile, key, heightNode, attacherJointNode)
3694end

loadAttachmentsFinished

Description
Called after all attachments were loaded
Definition
loadAttachmentsFinished()
Code
1036function AttacherJoints:loadAttachmentsFinished()
1037 -- apply loaded selection from savegame after all implemented were attached
1038 if self.rootVehicle == self then
1039 if self.loadedSelectedObjectIndex ~= nil then
1040 local object = self.selectableObjects[self.loadedSelectedObjectIndex]
1041 if object ~= nil then
1042 self:setSelectedObject(object, self.loadedSubSelectedObjectIndex or 1)
1043 end
1044
1045 self.loadedSelectedObjectIndex = nil
1046 self.loadedSubSelectedObjectIndex = nil
1047 end
1048 end
1049end

loadAttachmentsFromXMLFile

Description
Loads attachment from key from xml
Definition
loadAttachmentsFromXMLFile(table xmlFile, string key, table idsToVehicle)
Arguments
tablexmlFilexml file object
stringkeykey
tableidsToVehicleid to vehicle mapping
Code
994function AttacherJoints:loadAttachmentsFromXMLFile(xmlFile, key, idsToVehicle)
995 local spec = self.spec_attacherJoints
996
997 local i = 0
998 while true do
999 local attachmentKey = string.format("%s.attachment(%d)", key, i)
1000 if not xmlFile:hasProperty(attachmentKey) then
1001 break
1002 end
1003
1004 local attachmentId = xmlFile:getValue(attachmentKey.."#attachmentId")
1005 local jointIndex = xmlFile:getValue(attachmentKey.."#jointIndex")
1006 local inputJointDescIndex = xmlFile:getValue(attachmentKey.."#inputJointDescIndex", 1)
1007 if attachmentId ~= nil and jointIndex ~= nil then
1008 local attachment = idsToVehicle[attachmentId]
1009
1010 local inputAttacherJoints
1011 if attachment ~= nil and attachment.getInputAttacherJoints ~= nil then
1012 inputAttacherJoints = attachment:getInputAttacherJoints()
1013 end
1014 local inputAttacherJoint
1015 if inputAttacherJoints ~= nil then
1016 inputAttacherJoint = inputAttacherJoints[inputJointDescIndex]
1017 end
1018
1019 if inputAttacherJoint ~= nil and spec.attacherJoints[jointIndex] ~= nil and spec.attacherJoints[jointIndex].jointIndex == 0 then
1020 local moveDown = xmlFile:getValue(attachmentKey.."#moveDown")
1021
1022 self:attachImplement(attachment, inputJointDescIndex, jointIndex, true, nil, moveDown, true, true)
1023
1024 if moveDown ~= nil then
1025 self:setJointMoveDown(jointIndex, moveDown, true)
1026 end
1027 end
1028 end
1029
1030 i = i + 1
1031 end
1032end

loadDashboardGroupFromXML

Description
Definition
loadDashboardGroupFromXML()
Code
3665function AttacherJoints:loadDashboardGroupFromXML(superFunc, xmlFile, key, group)
3666 if not superFunc(self, xmlFile, key, group) then
3667 return false
3668 end
3669
3670 group.attacherJointIndices = xmlFile:getValue(key .. "#attacherJointIndices", "", true)
3671
3672 return true
3673end

onActivate

Description
Called on activate
Definition
onActivate()
Code
3880function AttacherJoints:onActivate()
3881 self:activateAttachments()
3882end

onBeaconLightsVisibilityChanged

Description
Definition
onBeaconLightsVisibilityChanged()
Code
3975function AttacherJoints:onBeaconLightsVisibilityChanged(visibility)
3976 local spec = self.spec_attacherJoints
3977 for _, implement in pairs(spec.attachedImplements) do
3978 local vehicle = implement.object
3979 if vehicle ~= nil and vehicle.setBeaconLightsVisibility ~= nil then
3980 vehicle:setBeaconLightsVisibility(visibility, true, true)
3981 end
3982 end
3983end

onBottomArmToolbarI3DLoaded

Description
Called when toolbar was loaded
Definition
onBottomArmToolbarI3DLoaded(integer i3dNode, table args)
Arguments
integeri3dNodetop arm i3d node
tableargsasync arguments
Code
3346function AttacherJoints:onBottomArmToolbarI3DLoaded(i3dNode, failedReason, args)
3347 local bottomArm = args.bottomArm
3348 local referenceNode = args.referenceNode
3349
3350 if i3dNode ~= 0 then
3351 local rootNode = getChildAt(i3dNode, 0)
3352 link(referenceNode, rootNode)
3353 delete(i3dNode)
3354 setTranslation(rootNode, 0,0,0)
3355 bottomArm.toolbar = rootNode
3356 setVisibility(rootNode, false)
3357 end
3358end

onBrake

Description
Definition
onBrake()
Code
3987function AttacherJoints:onBrake(brakePedal)
3988 local spec = self.spec_attacherJoints
3989 for _, implement in pairs(spec.attachedImplements) do
3990 local vehicle = implement.object
3991 if vehicle ~= nil and vehicle.brake ~= nil then
3992 vehicle:brake(brakePedal)
3993 end
3994 end
3995end

onBrakeLightsVisibilityChanged

Description
Definition
onBrakeLightsVisibilityChanged()
Code
3951function AttacherJoints:onBrakeLightsVisibilityChanged(visibility)
3952 local spec = self.spec_attacherJoints
3953 for _, implement in pairs(spec.attachedImplements) do
3954 local vehicle = implement.object
3955 if vehicle ~= nil and vehicle.setBrakeLightsVisibility ~= nil then
3956 vehicle:setBrakeLightsVisibility(visibility)
3957 end
3958 end
3959end

onDeactivate

Description
Called on deactivate
Definition
onDeactivate()
Code
3886function AttacherJoints:onDeactivate()
3887 self:deactivateAttachments()
3888 if self.isClient then
3889 local spec = self.spec_attacherJoints
3890 g_soundManager:stopSample(spec.samples.hydraulic)
3891 spec.isHydraulicSamplePlaying = false
3892 end
3893end

onDelete

Description
Called on deleting
Definition
onDelete()
Code
617function AttacherJoints:onDelete()
618 local spec = self.spec_attacherJoints
619
620 if spec.attacherJoints ~= nil then
621 for _, jointDesc in pairs(spec.attacherJoints) do
622 g_soundManager:deleteSample(jointDesc.sampleAttach)
623 g_soundManager:deleteSample(jointDesc.sampleDetach)
624
625 if jointDesc.sharedLoadRequestIdTopArm ~= nil then
626 g_i3DManager:releaseSharedI3DFile(jointDesc.sharedLoadRequestIdTopArm)
627 jointDesc.sharedLoadRequestIdTopArm = nil
628 end
629
630 local bottomArm = jointDesc.bottomArm
631 if bottomArm ~= nil then
632 if bottomArm.sharedLoadRequestIdToolbar ~= nil then
633 g_i3DManager:releaseSharedI3DFile(bottomArm.sharedLoadRequestIdToolbar)
634 bottomArm.sharedLoadRequestIdToolbar = nil
635 end
636 end
637 end
638
639 g_soundManager:deleteSamples(spec.samples)
640 end
641end

onLeaveVehicle

Description
Definition
onLeaveVehicle()
Code
4033function AttacherJoints:onLeaveVehicle()
4034 local spec = self.spec_attacherJoints
4035 for _, implement in pairs(spec.attachedImplements) do
4036 local vehicle = implement.object
4037 if vehicle ~= nil then
4038 SpecializationUtil.raiseEvent(vehicle, "onLeaveRootVehicle")
4039 end
4040 end
4041end

onLightsTypesMaskChanged

Description
Definition
onLightsTypesMaskChanged()
Code
3927function AttacherJoints:onLightsTypesMaskChanged(lightsTypesMask)
3928 local spec = self.spec_attacherJoints
3929 for _, implement in pairs(spec.attachedImplements) do
3930 local vehicle = implement.object
3931 if vehicle ~= nil and vehicle.setLightsTypesMask ~= nil then
3932 vehicle:setLightsTypesMask(lightsTypesMask, true, true)
3933 end
3934 end
3935end

onLoad

Description
Called on loading
Definition
onLoad(table savegame)
Arguments
tablesavegamesavegame
Code
382function AttacherJoints:onLoad(savegame)
383 local spec = self.spec_attacherJoints
384
385 spec.attacherJointCombos = {}
386 spec.attacherJointCombos.duration = self.xmlFile:getValue("vehicle.attacherJoints#comboDuration", 2) * 1000
387 spec.attacherJointCombos.currentTime = 0
388 spec.attacherJointCombos.direction = -1
389 spec.attacherJointCombos.isRunning = false
390 spec.attacherJointCombos.joints = {}
391
392 spec.maxUpdateDistance = self.xmlFile:getValue("vehicle.attacherJoints#maxUpdateDistance", AttacherJoints.DEFAULT_MAX_UPDATE_DISTANCE)
393
394 spec.visualNodeToAttacherJoints = {}
395 spec.hideVisualNodeToAttacherJoints = {}
396
397 spec.attacherJoints = {}
398 local i = 0
399 while true do
400 local baseName = string.format("vehicle.attacherJoints.attacherJoint(%d)", i)
401 if not self.xmlFile:hasProperty(baseName) then
402 break
403 end
404 local attacherJoint = {}
405 if self:loadAttacherJointFromXML(attacherJoint, self.xmlFile, baseName, i) then
406 table.insert(spec.attacherJoints, attacherJoint)
407 attacherJoint.index = #spec.attacherJoints
408 end
409 i = i + 1
410 end
411
412 if self.configurations["attacherJoint"] ~= nil then
413 local attacherConfigs = string.format("vehicle.attacherJoints.attacherJointConfigurations.attacherJointConfiguration(%d)", self.configurations["attacherJoint"]-1)
414 i = 0
415 while true do
416 local baseName = string.format(attacherConfigs..".attacherJoint(%d)", i)
417 if not self.xmlFile:hasProperty(baseName) then
418 break
419 end
420 local attacherJoint = {}
421 if self:loadAttacherJointFromXML(attacherJoint, self.xmlFile, baseName, i) then
422 table.insert(spec.attacherJoints, attacherJoint)
423 end
424 i = i + 1
425 end
426 ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.attacherJoints.attacherJointConfigurations.attacherJointConfiguration", self.configurations["attacherJoint"], self.components, self)
427 end
428
429 -- data structure to store information about eventually attachable vehicles
430 spec.attachableInfo = {}
431 spec.attachableInfo.attacherVehicle = nil
432 spec.attachableInfo.attacherVehicleJointDescIndex = nil
433 spec.attachableInfo.attachable = nil
434 spec.attachableInfo.attachableJointDescIndex = nil
435
436 spec.pendingAttachableInfo = {}
437 spec.pendingAttachableInfo.minDistance = math.huge
438 spec.pendingAttachableInfo.minDistanceY = math.huge
439 spec.pendingAttachableInfo.attacherVehicle = nil
440 spec.pendingAttachableInfo.attacherVehicleJointDescIndex = nil
441 spec.pendingAttachableInfo.attachable = nil
442 spec.pendingAttachableInfo.attachableJointDescIndex = nil
443 spec.pendingAttachableInfo.warning = nil
444
445 if self.isClient then
446 spec.samples = {}
447 spec.isHydraulicSamplePlaying = false
448 spec.samples.hydraulic = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.attacherJoints.sounds", "hydraulic", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
449 spec.samples.attach = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.attacherJoints.sounds", "attach", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
450 spec.samples.detach = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.attacherJoints.sounds", "detach", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
451 end
452
453 if self.isClient and g_isDevelopmentVersion then
454 for k, attacherJoint in ipairs(spec.attacherJoints) do
455 if spec.samples.attach == nil and attacherJoint.sampleAttach == nil then
456 Logging.xmlDevWarning(self.xmlFile, "Missing attach sound for attacherjoint '%d'", k)
457 end
458 if attacherJoint.rotationNode ~= nil and spec.samples.hydraulic == nil then
459 Logging.xmlDevWarning(self.xmlFile, "Missing hydraulic sound for attacherjoint '%d'", k)
460 end
461 end
462 end
463
464 spec.showAttachNotAllowedText = 0
465 spec.wasInAttachRange = false
466
467 spec.texts = {}
468 spec.texts.warningToolNotCompatible = g_i18n:getText("warning_toolNotCompatible")
469 spec.texts.warningToolBrandNotCompatible = g_i18n:getText("warning_toolBrandNotCompatible")
470 spec.texts.infoAttachNotAllowed = g_i18n:getText("info_attach_not_allowed")
471 spec.texts.lowerImplementFirst = g_i18n:getText("warning_lowerImplementFirst")
472 spec.texts.detachNotAllowed = g_i18n:getText("warning_detachNotAllowed")
473 spec.texts.actionAttach = g_i18n:getText("action_attach")
474 spec.texts.actionDetach = g_i18n:getText("action_detach")
475 spec.texts.warningFoldingAttacherJoint = g_i18n:getText("warning_foldingNotWhileAttachedToAttacherJoint")
476
477 spec.groundHeightNodeCheckData = {
478 isDirty = false,
479 minDistance = math.huge,
480 hit = false,
481 raycastDistance = 1,
482 currentRaycastDistance = 1,
483 heightNodes = {},
484 jointDesc = {},
485 index = -1,
486 lowerDistanceToGround = 0,
487 upperDistanceToGround = 0,
488 currentRaycastWorldPos = {0, 0, 0},
489 currentRaycastWorldDir = {0, 0, 0},
490 currentJointTransformPos = {0, 0, 0},
491 raycastWorldPos = {0, 0, 0},
492 raycastWorldDir = {0, 0, 0},
493 jointTransformPos = {0, 0, 0},
494 upperAlpha = 0,
495 lowerAlpha = 0,
496 }
497
498 spec.dirtyFlag = self:getNextDirtyFlag()
499end

onPostLoad

Description
Called after loading
Definition
onPostLoad(table savegame)
Arguments
tablesavegamesavegame
Code
504function AttacherJoints:onPostLoad(savegame)
505 local spec = self.spec_attacherJoints
506
507 for attacherJointIndex, attacherJoint in pairs(spec.attacherJoints) do
508 attacherJoint.jointOrigRot = { getRotation(attacherJoint.jointTransform) }
509 attacherJoint.jointOrigTrans = { getTranslation(attacherJoint.jointTransform) }
510 if attacherJoint.transNode ~= nil then
511 local _
512 attacherJoint.transNodeMinY = Utils.getNoNil(attacherJoint.transNodeMinY, attacherJoint.jointOrigTrans[2])
513 attacherJoint.transNodeMaxY = Utils.getNoNil(attacherJoint.transNodeMaxY, attacherJoint.jointOrigTrans[2])
514 _, attacherJoint.transNodeOffsetY, _ = localToLocal(attacherJoint.jointTransform, attacherJoint.transNode, 0, 0, 0)
515 _, attacherJoint.transNodeMinY, _ = localToLocal(getParent(attacherJoint.transNode), self.rootNode, 0, attacherJoint.transNodeMinY, 0)
516 _, attacherJoint.transNodeMaxY, _ = localToLocal(getParent(attacherJoint.transNode), self.rootNode, 0, attacherJoint.transNodeMaxY, 0)
517 end
518
519 if attacherJoint.transNodeDependentBottomArm ~= nil then
520 for _, attacherJoint2 in pairs(spec.attacherJoints) do
521 if attacherJoint2.bottomArm ~= nil then
522 if attacherJoint2.bottomArm.rotationNode == attacherJoint.transNodeDependentBottomArm then
523 attacherJoint.transNodeDependentBottomArmAttacherJoint = attacherJoint2
524 end
525 end
526 end
527
528 if attacherJoint.transNodeDependentBottomArmAttacherJoint == nil then
529 Logging.xmlWarning(self.xmlFile, "Unable to find dependent bottom arm '%s' in any attacher joint.", getName(attacherJoint.transNodeDependentBottomArm))
530 attacherJoint.transNodeDependentBottomArm = nil
531 end
532 end
533
534 if attacherJoint.bottomArm ~= nil then
535 setRotation(attacherJoint.bottomArm.rotationNode, attacherJoint.bottomArm.rotX, attacherJoint.bottomArm.rotY, attacherJoint.bottomArm.rotZ)
536 if self.setMovingToolDirty ~= nil then
537 self:setMovingToolDirty(attacherJoint.bottomArm.rotationNode)
538 end
539 end
540 if attacherJoint.rotationNode ~= nil then
541 setRotation(attacherJoint.rotationNode, attacherJoint.rotX, attacherJoint.rotY, attacherJoint.rotZ)
542 end
543
544 if self.getInputAttacherJoints ~= nil then
545 attacherJoint.inputAttacherJointOffsets = {}
546 for _, inputAttacherJoint in ipairs(self:getInputAttacherJoints()) do
547 local xDir, yDir, zDir = localDirectionToLocal(attacherJoint.jointTransform, inputAttacherJoint.node, 0, 0, 1)
548 local xUp, yUp, zUp = localDirectionToLocal(attacherJoint.jointTransform, inputAttacherJoint.node, 0, 1, 0)
549 local xNorm, yNorm, zNorm = localDirectionToLocal(attacherJoint.jointTransform, inputAttacherJoint.node, 1, 0, 0)
550 local xOffset, yOffset, zOffset = localToLocal(attacherJoint.jointTransform, inputAttacherJoint.node, 0, 0, 0)
551 table.insert(attacherJoint.inputAttacherJointOffsets, {xOffset, yOffset, zOffset, xDir, yDir, zDir, xUp, yUp, zUp, xNorm, yNorm, zNorm})
552 end
553 end
554
555 if self.getAIRootNode ~= nil then
556 local aiRootNode = self:getAIRootNode()
557 local xDir, yDir, zDir = localDirectionToLocal(attacherJoint.jointTransform, aiRootNode, 0, 0, 1)
558 local xUp, yUp, zUp = localDirectionToLocal(attacherJoint.jointTransform, aiRootNode, 0, 1, 0)
559 local xNorm, yNorm, zNorm = localDirectionToLocal(attacherJoint.jointTransform, aiRootNode, 1, 0, 0)
560 local xOffset, yOffset, zOffset = localToLocal(attacherJoint.jointTransform, aiRootNode, 0, 0, 0)
561 attacherJoint.aiRootNodeOffset = {xOffset, yOffset, zOffset, xDir, yDir, zDir, xUp, yUp, zUp, xNorm, yNorm, zNorm}
562 end
563
564 if attacherJoint.comboTime ~= nil then
565 table.insert(spec.attacherJointCombos.joints, {jointIndex = attacherJointIndex, time = MathUtil.clamp(attacherJoint.comboTime, 0, 1) * spec.attacherJointCombos.duration})
566 end
567 end
568
569 if savegame ~= nil and not savegame.resetVehicles then
570 if spec.attacherJointCombos ~= nil then
571 local comboDirection = savegame.xmlFile:getValue(savegame.key..".attacherJoints#comboDirection")
572 if comboDirection ~= nil then
573 spec.attacherJointCombos.direction = comboDirection
574 if comboDirection == 1 then
575 spec.attacherJointCombos.currentTime = spec.attacherJointCombos.duration
576 end
577 end
578 end
579 end
580
581 if #spec.attacherJoints == 0 then
582 SpecializationUtil.removeEventListener(self, "onReadStream", AttacherJoints)
583 SpecializationUtil.removeEventListener(self, "onWriteStream", AttacherJoints)
584 SpecializationUtil.removeEventListener(self, "onUpdate", AttacherJoints)
585 SpecializationUtil.removeEventListener(self, "onUpdateEnd", AttacherJoints)
586 SpecializationUtil.removeEventListener(self, "onPostUpdate", AttacherJoints)
587 SpecializationUtil.removeEventListener(self, "onStateChange", AttacherJoints)
588 SpecializationUtil.removeEventListener(self, "onLightsTypesMaskChanged", AttacherJoints)
589 SpecializationUtil.removeEventListener(self, "onTurnLightStateChanged", AttacherJoints)
590 SpecializationUtil.removeEventListener(self, "onBrakeLightsVisibilityChanged", AttacherJoints)
591 SpecializationUtil.removeEventListener(self, "onReverseLightsVisibilityChanged", AttacherJoints)
592 SpecializationUtil.removeEventListener(self, "onBeaconLightsVisibilityChanged", AttacherJoints)
593 SpecializationUtil.removeEventListener(self, "onBrake", AttacherJoints)
594 SpecializationUtil.removeEventListener(self, "onTurnedOn", AttacherJoints)
595 SpecializationUtil.removeEventListener(self, "onTurnedOff", AttacherJoints)
596 SpecializationUtil.removeEventListener(self, "onLeaveVehicle", AttacherJoints)
597 SpecializationUtil.removeEventListener(self, "onActivate", AttacherJoints)
598 SpecializationUtil.removeEventListener(self, "onDeactivate", AttacherJoints)
599 SpecializationUtil.removeEventListener(self, "onReverseDirectionChanged", AttacherJoints)
600 end
601end

onPostUpdate

Description
Called on after update
Definition
onPostUpdate(float dt, boolean isActiveForInput, boolean isSelected)
Arguments
floatdttime since last call in ms
booleanisActiveForInputtrue if vehicle is active for input
booleanisSelectedtrue if vehicle is selected
Code
784function AttacherJoints:onPostUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
785 local spec = self.spec_attacherJoints
786
787 for _,implement in pairs(spec.attachedImplements) do
788 if not implement.attachingIsInProgress then
789 local attacherJoint = implement.object:getActiveInputAttacherJoint()
790 if attacherJoint ~= nil then
791 if spec.attacherJoints[implement.jointDescIndex].steeringBarLeftNode ~= nil and attacherJoint.steeringBarLeftMovingPart ~= nil then
792 Cylindered.updateMovingPart(self, attacherJoint.steeringBarLeftMovingPart, nil, true)
793 end
794 if spec.attacherJoints[implement.jointDescIndex].steeringBarRightNode ~= nil and attacherJoint.steeringBarRightMovingPart ~= nil then
795 Cylindered.updateMovingPart(self, attacherJoint.steeringBarRightMovingPart, nil, true)
796 end
797 end
798 end
799 end
800end

onPreDelete

Description
Called on before deleting
Definition
onPreDelete()
Code
605function AttacherJoints:onPreDelete()
606 local spec = self.spec_attacherJoints
607
608 if spec.attachedImplements ~= nil then
609 for i=#spec.attachedImplements, 1, -1 do
610 self:detachImplement(1, true)
611 end
612 end
613end

onPreLoad

Description
Called before loading
Definition
onPreLoad(table savegame)
Arguments
tablesavegamesavegame
Code
371function AttacherJoints:onPreLoad(savegame)
372 local spec = self.spec_attacherJoints
373 spec.attachedImplements = {}
374 spec.selectedImplement = nil
375
376 spec.lastInputAttacherCheckIndex = 0
377end

onReadStream

Description
Called on client side on join
Definition
onReadStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
689function AttacherJoints:onReadStream(streamId, connection)
690 local numImplements = streamReadInt8(streamId)
691 for i=1, numImplements do
692 local object = NetworkUtil.readNodeObject(streamId)
693 local inputJointDescIndex = streamReadInt8(streamId)
694 local jointDescIndex = streamReadInt8(streamId)
695 local moveDown = streamReadBool(streamId)
696 if object ~= nil and object:getIsSynchronized() then
697 self:attachImplement(object, inputJointDescIndex, jointDescIndex, true, i, moveDown, true, true)
698 self:setJointMoveDown(jointDescIndex, moveDown, true)
699 end
700 end
701end

onRegisterActionEvents

Description
Definition
onRegisterActionEvents()
Code
3829function AttacherJoints:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
3830 if self.isClient then
3831 local spec = self.spec_attacherJoints
3832 self:clearActionEventsTable(spec.actionEvents)
3833
3834 -- ignore vehicle selection on 'getIsActiveForInput', so we can select the target vehicle and attach or lower it
3835 if isActiveForInputIgnoreSelection then
3836 if #spec.attacherJoints > 0 then
3837 -- only display lower and attach action if selected implement is direct child of vehicle, not sub child
3838 local selectedImplement = self:getSelectedImplement()
3839 if selectedImplement ~= nil and selectedImplement.object ~= self then
3840 for _, attachedImplement in pairs(spec.attachedImplements) do
3841 if attachedImplement == selectedImplement then
3842 -- custom registration of the action event. This allows us to overwritte it in the implement (e.g in Foldable)
3843 selectedImplement.object:registerLoweringActionEvent(spec.actionEvents, InputAction.LOWER_IMPLEMENT, selectedImplement.object, AttacherJoints.actionEventLowerImplement, false, true, false, true, nil, nil, true)
3844 end
3845 end
3846 end
3847
3848 local _, actionEventId = self:addPoweredActionEvent(spec.actionEvents, InputAction.LOWER_ALL_IMPLEMENTS, self, AttacherJoints.actionEventLowerAllImplements, false, true, false, true, nil, nil, true)
3849 g_inputBinding:setActionEventTextVisibility(actionEventId, false)
3850 end
3851
3852 if self:getSelectedVehicle() == self then
3853 local state, _ = self:registerSelfLoweringActionEvent(spec.actionEvents, InputAction.LOWER_IMPLEMENT, self, AttacherJoints.actionEventLowerImplement, false, true, false, true, nil, nil, true)
3854
3855 -- if the selected attacher vehicle can not be lowered and we got only one implement that can be lowered
3856 -- we add the lowering action for the first implement
3857 if state == nil or not state then
3858 if #spec.attachedImplements == 1 then
3859 local firstImplement = spec.attachedImplements[1]
3860 if firstImplement ~= nil then
3861 firstImplement.object:registerLoweringActionEvent(spec.actionEvents, InputAction.LOWER_IMPLEMENT, firstImplement.object, AttacherJoints.actionEventLowerImplement, false, true, false, true, nil, nil, true)
3862 end
3863 end
3864 end
3865 end
3866
3867 local _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.ATTACH, self, AttacherJoints.actionEventAttach, false, true, false, true, nil, nil, true)
3868 g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_HIGH)
3869
3870 _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.DETACH, self, AttacherJoints.actionEventDetach, false, true, false, true, nil, nil, true)
3871 g_inputBinding:setActionEventTextVisibility(actionEventId, false)
3872
3873 AttacherJoints.updateActionEvents(self)
3874 end
3875 end
3876end

onReverseDirectionChanged

Description
Definition
onReverseDirectionChanged()
Code
3897function AttacherJoints:onReverseDirectionChanged(direction)
3898 local spec = self.spec_attacherJoints
3899
3900 if spec.attacherJointCombos ~= nil then
3901 for _, joint in pairs(spec.attacherJointCombos.joints) do
3902 joint.time = math.abs(joint.time - spec.attacherJointCombos.duration)
3903 end
3904 end
3905end

onReverseLightsVisibilityChanged

Description
Definition
onReverseLightsVisibilityChanged()
Code
3963function AttacherJoints:onReverseLightsVisibilityChanged(visibility)
3964 local spec = self.spec_attacherJoints
3965 for _, implement in pairs(spec.attachedImplements) do
3966 local vehicle = implement.object
3967 if vehicle ~= nil and vehicle.setReverseLightsVisibility ~= nil then
3968 vehicle:setReverseLightsVisibility(visibility)
3969 end
3970 end
3971end

onStateChange

Description
Definition
onStateChange()
Code
3909function AttacherJoints:onStateChange(state, data)
3910 local spec = self.spec_attacherJoints
3911
3912 for _, implement in pairs(spec.attachedImplements) do
3913 if implement.object ~= nil then
3914 implement.object:raiseStateChange(state, data)
3915 end
3916 end
3917
3918 if state == Vehicle.STATE_CHANGE_LOWER_ALL_IMPLEMENTS then
3919 if #spec.attacherJoints > 0 then
3920 self:startAttacherJointCombo()
3921 end
3922 end
3923end

onTopArmI3DLoaded

Description
Called when top arm was loaded
Definition
onTopArmI3DLoaded(integer i3dNode, table args)
Arguments
integeri3dNodetop arm i3d node
tableargsasync arguments
Code
3240function AttacherJoints:onTopArmI3DLoaded(i3dNode, failedReason, args)
3241 if i3dNode ~= 0 then
3242 local xmlFile = args.xmlFile
3243 local baseName = args.baseName
3244 local attacherJoint = args.attacherJoint
3245 local baseNode = args.baseNode
3246
3247 local rootNode = getChildAt(i3dNode, 0)
3248 link(baseNode, rootNode)
3249 delete(i3dNode)
3250 setTranslation(rootNode, 0,0,0)
3251 local translationNode = getChildAt(rootNode, 0)
3252 local referenceNode = getChildAt(translationNode, 0)
3253
3254
3255 local topArm = {}
3256 topArm.rotationNode = rootNode
3257 topArm.rotX, topArm.rotY, topArm.rotZ = 0,0,0
3258 topArm.translationNode = translationNode
3259
3260 local _,_,referenceDistance = getTranslation(referenceNode)
3261 topArm.referenceDistance = referenceDistance
3262
3263 topArm.zScale = 1
3264 local zScale = MathUtil.sign(xmlFile:getValue(baseName.. ".topArm#zScale", 1))
3265 if zScale < 0 then
3266 topArm.rotY = math.pi
3267 setRotation(rootNode, topArm.rotX, topArm.rotY, topArm.rotZ)
3268 end
3269
3270 if getNumOfChildren(rootNode) > 1 then
3271 topArm.scaleNode = getChildAt(rootNode, 1)
3272 local scaleReferenceNode = getChildAt(topArm.scaleNode, 0)
3273 local _,_,scaleReferenceDistance = getTranslation(scaleReferenceNode)
3274 topArm.scaleReferenceDistance = scaleReferenceDistance
3275 end
3276
3277 topArm.toggleVisibility = xmlFile:getValue(baseName.. ".topArm#toggleVisibility", false)
3278 if topArm.toggleVisibility then
3279 setVisibility(topArm.rotationNode, false)
3280 end
3281
3282 local colorValue = xmlFile:getValue(baseName..".topArm#color", nil, true)
3283 local colorValue2 = xmlFile:getValue(baseName..".topArm#color2", nil, true)
3284 local decalColor = xmlFile:getValue(baseName..".topArm#decalColor", nil, true)
3285 local useMainColor = xmlFile:getValue(baseName..".topArm#secondPartUseMainColor", true)
3286 local useBrandDecal = xmlFile:getValue(baseName..".topArm#useBrandDecal", true)
3287 if not useBrandDecal then
3288 local brandDecal = I3DUtil.getChildByName(rootNode, "brandDecal")
3289 if brandDecal ~= nil then
3290 delete(brandDecal)
3291 else
3292 Logging.xmlDevWarning(xmlFile, "Unable to find brand decal to remove for '%s'", baseName..".topArm")
3293 end
3294 end
3295
3296 -- on dark colors we use a white decal and on bright colors a black decal
3297 if decalColor == nil and colorValue ~= nil then
3298 local brightness = MathUtil.getBrightnessFromColor(colorValue[1], colorValue[2], colorValue[3])
3299 brightness = (brightness > 0.075 and 1) or 0
3300
3301 decalColor = {1-brightness, 1-brightness, 1-brightness}
3302 end
3303
3304 local _, _, _, mat0 = I3DUtil.getShaderParameterRec(rootNode, "colorMat0")
3305 local _, _, _, mat1 = I3DUtil.getShaderParameterRec(rootNode, "colorMat1")
3306
3307 if colorValue ~= nil then
3308 local material = xmlFile:getValue(baseName..".topArm#material")
3309 I3DUtil.setShaderParameterRec(rootNode, "colorMat0", colorValue[1], colorValue[2], colorValue[3], material or mat0)
3310 end
3311 if colorValue2 ~= nil then
3312 local material2 = xmlFile:getValue(baseName..".topArm#material2")
3313 I3DUtil.setShaderParameterRec(rootNode, "colorMat1", colorValue2[1], colorValue2[2], colorValue2[3], material2 or mat1)
3314 end
3315
3316 if useMainColor then
3317 if colorValue ~= nil then
3318 local material = xmlFile:getValue(baseName..".topArm#material")
3319 I3DUtil.setShaderParameterRec(rootNode, "colorMat3", colorValue[1], colorValue[2], colorValue[3], material or mat0)
3320 else
3321 local x, y, z, w = I3DUtil.getShaderParameterRec(rootNode, "colorMat0")
3322 I3DUtil.setShaderParameterRec(rootNode, "colorMat3", x, y, z, w)
3323 end
3324 else
3325 if colorValue2 ~= nil then
3326 local material2 = xmlFile:getValue(baseName..".topArm#material2")
3327 I3DUtil.setShaderParameterRec(rootNode, "colorMat3", colorValue2[1], colorValue2[2], colorValue2[3], material2 or mat1)
3328 else
3329 local x, y, z, w = I3DUtil.getShaderParameterRec(rootNode, "colorMat1")
3330 I3DUtil.setShaderParameterRec(rootNode, "colorMat3", x, y, z, w)
3331 end
3332 end
3333
3334 if decalColor ~= nil then
3335 I3DUtil.setShaderParameterRec(rootNode, "colorMat2", decalColor[1], decalColor[2], decalColor[3], 1)
3336 end
3337
3338 attacherJoint.topArm = topArm
3339 end
3340end

onTurnedOff

Description
Definition
onTurnedOff()
Code
4016function AttacherJoints:onTurnedOff()
4017 local spec = self.spec_attacherJoints
4018 for _, implement in pairs(spec.attachedImplements) do
4019 local vehicle = implement.object
4020 if vehicle ~= nil then
4021 local turnedOnVehicleSpec = vehicle.spec_turnOnVehicle
4022 if turnedOnVehicleSpec then
4023 if turnedOnVehicleSpec.turnedOnByAttacherVehicle then
4024 vehicle:setIsTurnedOn(false, true)
4025 end
4026 end
4027 end
4028 end
4029end

onTurnedOn

Description
Definition
onTurnedOn()
Code
3999function AttacherJoints:onTurnedOn()
4000 local spec = self.spec_attacherJoints
4001 for _, implement in pairs(spec.attachedImplements) do
4002 local vehicle = implement.object
4003 if vehicle ~= nil then
4004 local turnedOnVehicleSpec = vehicle.spec_turnOnVehicle
4005 if turnedOnVehicleSpec then
4006 if turnedOnVehicleSpec.turnedOnByAttacherVehicle then
4007 vehicle:setIsTurnedOn(true, true)
4008 end
4009 end
4010 end
4011 end
4012end

onTurnLightStateChanged

Description
Definition
onTurnLightStateChanged()
Code
3939function AttacherJoints:onTurnLightStateChanged(state)
3940 local spec = self.spec_attacherJoints
3941 for _, implement in pairs(spec.attachedImplements) do
3942 local vehicle = implement.object
3943 if vehicle ~= nil and vehicle.setTurnLightState ~= nil then
3944 vehicle:setTurnLightState(state, true, true)
3945 end
3946 end
3947end

onUpdate

Description
Called on update
Definition
onUpdate(float dt, boolean isActiveForInput, boolean isSelected)
Arguments
floatdttime since last call in ms
booleanisActiveForInputtrue if vehicle is active for input
booleanisSelectedtrue if vehicle is selected
Code
731function AttacherJoints:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
732 local spec = self.spec_attacherJoints
733
734 if self.currentUpdateDistance < spec.maxUpdateDistance then
735 for _, implement in pairs(spec.attachedImplements) do
736 if implement.object ~= nil then
737 if self.updateLoopIndex == implement.object.updateLoopIndex then
738 self:updateAttacherJointGraphics(implement, dt)
739 end
740 end
741 end
742 end
743
744 if self.isClient then
745 spec.showAttachNotAllowedText = math.max(spec.showAttachNotAllowedText - dt, 0)
746 if spec.showAttachNotAllowedText > 0 then
747 g_currentMission:addExtraPrintText(spec.texts.infoAttachNotAllowed)
748 end
749 end
750
751 -- update attachables in range
752 local info = spec.attachableInfo
753
754 if (Platform.gameplay.automaticAttach and self.isServer)
755 or (self.isClient and spec.actionEvents ~= nil and spec.actionEvents[InputAction.ATTACH] ~= nil) then
756 if self:getCanToggleAttach() then
757 AttacherJoints.updateVehiclesInAttachRange(self, AttacherJoints.MAX_ATTACH_DISTANCE_SQ, AttacherJoints.MAX_ATTACH_ANGLE, true)
758 else
759 info.attacherVehicle, info.attacherVehicleJointDescIndex, info.attachable, info.attachableJointDescIndex = nil, nil, nil, nil
760 end
761 end
762end

onUpdateEnd

Description
Definition
onUpdateEnd()
Code
766function AttacherJoints:onUpdateEnd(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
767 -- force update of all attacher joint graphics independent of camera distance right before vehicles starts to sleep
768 -- so if we get into the update distance agan we are already in the right state without waking up the vehicle
769 local spec = self.spec_attacherJoints
770 for _, implement in pairs(spec.attachedImplements) do
771 if implement.object ~= nil then
772 if self.updateLoopIndex == implement.object.updateLoopIndex then
773 self:updateAttacherJointGraphics(implement, dt)
774 end
775 end
776 end
777end

onUpdateTick

Description
Called on update tick
Definition
onUpdateTick(float dt, boolean isActiveForInput, boolean isSelected)
Arguments
floatdttime since last call in ms
booleanisActiveForInputtrue if vehicle is active for input
booleanisSelectedtrue if vehicle is selected
Code
807function AttacherJoints:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
808 local spec = self.spec_attacherJoints
809
810 local playHydraulicSound = false
811
812 for _, implement in pairs(spec.attachedImplements) do
813 if implement.object ~= nil then
814 local jointDesc = spec.attacherJoints[implement.jointDescIndex]
815
816 if not implement.object.spec_attachable.isHardAttached then
817 if self.isServer then
818 if implement.attachingIsInProgress then
819 local done = true
820 for i=1,3 do
821 local lastRotLimit = implement.attachingRotLimit[i]
822 local lastTransLimit = implement.attachingTransLimit[i]
823 implement.attachingRotLimit[i] = math.max(0, implement.attachingRotLimit[i] - implement.attachingRotLimitSpeed[i] * dt)
824 implement.attachingTransLimit[i] = math.max(0, implement.attachingTransLimit[i] - implement.attachingTransLimitSpeed[i] * dt)
825 if (implement.attachingRotLimit[i] > 0 or implement.attachingTransLimit[i] > 0) or
826 (lastRotLimit > 0 or lastTransLimit > 0)
827 then
828 done = false
829 end
830 end
831 implement.attachingIsInProgress = not done
832
833 if done then
834 if implement.object.spec_attachable.attacherJoint.hardAttach and self:getIsHardAttachAllowed(implement.jointDescIndex) then
835 self:hardAttachImplement(implement)
836 end
837 self:postAttachImplement(implement)
838 end
839 end
840 end
841 if not implement.attachingIsInProgress then
842 local jointFrameInvalid = false
843 if jointDesc.allowsLowering then
844 if self:getIsActive() then
845 local upperAlpha, lowerAlpha = jointDesc.upperAlpha, jointDesc.lowerAlpha
846 if jointDesc.moveDown then
847 upperAlpha, lowerAlpha = self:calculateAttacherJointMoveUpperLowerAlpha(jointDesc, implement.object)
848 jointDesc.moveTime = jointDesc.moveDefaultTime * math.abs(upperAlpha - lowerAlpha)
849 end
850
851 local moveAlpha = Utils.getMovedLimitedValue(jointDesc.moveAlpha, lowerAlpha, upperAlpha, jointDesc.moveTime, dt, not jointDesc.moveDown)
852 if moveAlpha ~= jointDesc.moveAlpha or upperAlpha ~= jointDesc.upperAlpha or lowerAlpha ~= jointDesc.lowerAlpha then
853 jointDesc.upperAlpha = upperAlpha
854 jointDesc.lowerAlpha = lowerAlpha
855
856 if jointDesc.moveDown then
857 if math.abs(jointDesc.moveAlpha - jointDesc.lowerAlpha) < 0.05 then
858 jointDesc.isMoving = false
859 end
860 else
861 if math.abs(jointDesc.moveAlpha - jointDesc.upperAlpha) < 0.05 then
862 jointDesc.isMoving = false
863 end
864 end
865
866 playHydraulicSound = jointDesc.isMoving
867
868 jointDesc.moveAlpha = moveAlpha
869 jointDesc.moveLimitAlpha = 1- (moveAlpha-jointDesc.lowerAlpha) / (jointDesc.upperAlpha-jointDesc.lowerAlpha)
870 jointFrameInvalid = true
871 self:updateAttacherJointRotationNodes(jointDesc, jointDesc.moveAlpha)
872 self:updateAttacherJointRotation(jointDesc, implement.object)
873 end
874 end
875 end
876
877 jointFrameInvalid = jointFrameInvalid or jointDesc.jointFrameInvalid
878 if jointFrameInvalid then
879 jointDesc.jointFrameInvalid = false
880 if self.isServer then
881 setJointFrame(jointDesc.jointIndex, 0, jointDesc.jointTransform)
882 end
883 end
884 end
885 if self.isServer then
886 local force = implement.attachingIsInProgress
887 if force or (jointDesc.allowsLowering and jointDesc.allowsJointLimitMovement) then
888 if jointDesc.jointIndex ~= nil and jointDesc.jointIndex ~= 0 then
889 if force or implement.object.spec_attachable.attacherJoint.allowsJointRotLimitMovement then
890 local alpha = math.max(jointDesc.moveLimitAlpha - implement.rotLimitThreshold, 0) / (1-implement.rotLimitThreshold)
891
892 for i=1, 3 do
893 AttacherJoints.updateAttacherJointRotationLimit(implement, jointDesc, i, force, alpha)
894 end
895 end
896
897 if force or implement.object.spec_attachable.attacherJoint.allowsJointTransLimitMovement then
898 local alpha = math.max(jointDesc.moveLimitAlpha - implement.transLimitThreshold, 0) / (1-implement.transLimitThreshold)
899
900 for i=1, 3 do
901 AttacherJoints.updateAttacherJointTranslationLimit(implement, jointDesc, i, force, alpha)
902 end
903 end
904 end
905 end
906 end
907 end
908 end
909 end
910
911 if self.isClient and spec.samples.hydraulic ~= nil then
912 for i=1, #spec.attacherJoints do
913 local jointDesc = spec.attacherJoints[i]
914 if jointDesc.bottomArm ~= nil and jointDesc.bottomArm.bottomArmInterpolating then
915 playHydraulicSound = true
916 end
917 end
918
919 if playHydraulicSound then
920 if not spec.isHydraulicSamplePlaying then
921 g_soundManager:playSample(spec.samples.hydraulic)
922 spec.isHydraulicSamplePlaying = true
923 end
924 else
925 if spec.isHydraulicSamplePlaying then
926 g_soundManager:stopSample(spec.samples.hydraulic)
927 spec.isHydraulicSamplePlaying = false
928 end
929 end
930 end
931
932 local combos = spec.attacherJointCombos
933 if combos ~= nil and combos.isRunning then
934 for _, joint in pairs(combos.joints) do
935 local doLowering
936 if combos.direction == 1 and combos.currentTime >= joint.time then
937 doLowering = true
938 elseif combos.direction == -1 and combos.currentTime <= combos.duration-joint.time then
939 doLowering = false
940 end
941
942 if doLowering ~= nil then
943 local implement = self:getImplementFromAttacherJointIndex(joint.jointIndex)
944 if implement ~= nil then
945 if implement.object.setLoweredAll ~= nil then
946 implement.object:setLoweredAll(doLowering, joint.jointIndex)
947 log("set lowered", doLowering, joint.jointIndex)
948 end
949 end
950 end
951 end
952
953 if (combos.direction == -1 and combos.currentTime == 0) or
954 (combos.direction == 1 and combos.currentTime == combos.duration) then
955 combos.isRunning = false
956 end
957
958 combos.currentTime = MathUtil.clamp(combos.currentTime + dt*combos.direction, 0, combos.duration)
959 end
960
961 AttacherJoints.updateActionEvents(self)
962
963 -- auto attach for mobile version
964 if Platform.gameplay.automaticAttach then
965 if self.isServer then
966 if self:getCanToggleAttach() then
967 local info = spec.attachableInfo
968
969 if info.attachable ~= nil and not spec.wasInAttachRange and info.attacherVehicle == self then
970 local attachAllowed, warning = info.attachable:isAttachAllowed(self:getActiveFarm(), info.attacherVehicle)
971 if attachAllowed then
972 -- wasInAttachRange is nil after detach until the detached implement is in range
973 if spec.wasInAttachRange == nil then
974 spec.wasInAttachRange = true
975 else
976 self:attachImplementFromInfo(info)
977 end
978 elseif warning ~= nil then
979 g_currentMission:showBlinkingWarning(warning, 2000)
980 end
981 elseif info.attachable == nil and spec.wasInAttachRange then
982 spec.wasInAttachRange = false
983 end
984 end
985 end
986 end
987end

onWriteStream

Description
Called on server side on join
Definition
onWriteStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
707function AttacherJoints:onWriteStream(streamId, connection)
708 local spec = self.spec_attacherJoints
709
710 -- write attached implements
711 streamWriteInt8(streamId, #spec.attachedImplements)
712 for i=1, #spec.attachedImplements do
713 local implement = spec.attachedImplements[i]
714 local inputJointDescIndex = implement.object.spec_attachable.inputAttacherJointDescIndex
715 local jointDescIndex = implement.jointDescIndex
716 local jointDesc = spec.attacherJoints[jointDescIndex]
717 local moveDown = jointDesc.moveDown
718 NetworkUtil.writeNodeObject(streamId, implement.object)
719 streamWriteInt8(streamId, inputJointDescIndex)
720 streamWriteInt8(streamId, jointDescIndex)
721 streamWriteBool(streamId, moveDown)
722 end
723end

playAttachSound

Description
Play attach sound
Definition
playAttachSound(table jointDesc)
Arguments
tablejointDescjoint desc
Return Values
booleansuccesssuccess
Code
2537function AttacherJoints:playAttachSound(jointDesc)
2538 local spec = self.spec_attacherJoints
2539
2540 if self.isClient then
2541 if jointDesc ~= nil and jointDesc.sampleAttach ~= nil then
2542 g_soundManager:playSample(jointDesc.sampleAttach)
2543 else
2544 g_soundManager:playSample(spec.samples.attach)
2545 end
2546 end
2547
2548 return true
2549end

playDetachSound

Description
Play detach sound
Definition
playDetachSound(table jointDesc)
Arguments
tablejointDescjoint desc
Return Values
booleansuccesssuccess
Code
2555function AttacherJoints:playDetachSound(jointDesc)
2556 local spec = self.spec_attacherJoints
2557
2558 if self.isClient then
2559 if jointDesc ~= nil and jointDesc.sampleDetach ~= nil then
2560 g_soundManager:playSample(jointDesc.sampleDetach)
2561 elseif spec.samples.detach ~= nil then
2562 g_soundManager:playSample(spec.samples.detach)
2563 elseif jointDesc ~= nil and jointDesc.sampleAttach ~= nil then
2564 g_soundManager:playSample(jointDesc.sampleAttach)
2565 else
2566 g_soundManager:playSample(spec.samples.attach)
2567 end
2568 end
2569
2570 return true
2571end

postAttachImplement

Description
Definition
postAttachImplement()
Code
1883function AttacherJoints:postAttachImplement(implement)
1884 local spec = self.spec_attacherJoints
1885
1886 local object = implement.object
1887 local inputJointDescIndex = implement.inputJointDescIndex
1888 local jointDescIndex = implement.jointDescIndex
1889 local objectAttacherJoint = object.spec_attachable.inputAttacherJoints[inputJointDescIndex]
1890 local jointDesc = spec.attacherJoints[jointDescIndex]
1891
1892 if objectAttacherJoint.topReferenceNode ~= nil then
1893 if jointDesc.topArm ~= nil and jointDesc.topArm.toggleVisibility then
1894 setVisibility(jointDesc.topArm.rotationNode, true)
1895 end
1896 end
1897
1898 if jointDesc.bottomArm ~= nil then
1899 if jointDesc.bottomArm.toggleVisibility then
1900 setVisibility(jointDesc.bottomArm.rotationNode, true)
1901 end
1902
1903 if objectAttacherJoint.needsToolbar and jointDesc.bottomArm.toolbar ~= nil then
1904 setVisibility(jointDesc.bottomArm.toolbar, true)
1905 end
1906 end
1907
1908 if jointDesc.delayedObjectChangesOnAttach then
1909 ObjectChangeUtil.setObjectChanges(jointDesc.changeObjects, true, self, self.setMovingToolDirty)
1910 end
1911
1912 self:updateAttacherJointGraphics(implement, 0)
1913
1914 SpecializationUtil.raiseEvent(self, "onPostAttachImplement", object, inputJointDescIndex, jointDescIndex)
1915 object:postAttach(self, inputJointDescIndex, jointDescIndex, implement.loadFromSavegame)
1916
1917 local data = {attacherVehicle=self, attachedVehicle=implement.object}
1918 local rootVehicle = self.rootVehicle
1919 rootVehicle:raiseStateChange(Vehicle.STATE_CHANGE_ATTACH, data)
1920end

prerequisitesPresent

Description
Definition
prerequisitesPresent()
Code
229function AttacherJoints.prerequisitesPresent(specializations)
230 return true
231end

raiseActive

Description
Definition
raiseActive()
Code
3362function AttacherJoints:raiseActive(superFunc)
3363 local spec = self.spec_attacherJoints
3364
3365 superFunc(self)
3366 for _,implement in pairs(spec.attachedImplements) do
3367 if implement.object ~= nil then
3368 implement.object:raiseActive()
3369 end
3370 end
3371end

registerActionEvents

Description
Definition
registerActionEvents()
Code
3375function AttacherJoints:registerActionEvents(superFunc, excludedVehicle)
3376 local spec = self.spec_attacherJoints
3377
3378 superFunc(self, excludedVehicle)
3379 if self ~= excludedVehicle then
3380 -- at first we register the inputs of the selected vehicle
3381 -- so they got the higest prio and cannot be overwritten by another vehicle
3382 local selectedObject = self:getSelectedObject()
3383 if selectedObject ~= nil and self ~= selectedObject.vehicle and excludedVehicle ~= selectedObject.vehicle then
3384 selectedObject.vehicle:registerActionEvents()
3385 end
3386
3387 for _,implement in pairs(spec.attachedImplements) do
3388 if implement.object ~= nil then
3389 if selectedObject == nil then printCallstack() end
3390 implement.object:registerActionEvents(selectedObject.vehicle)
3391 end
3392 end
3393 end
3394end

registerAttacherJointXMLPaths

Description
Definition
registerAttacherJointXMLPaths()
Code
69function AttacherJoints.registerAttacherJointXMLPaths(schema, baseName)
70 schema:register(XMLValueType.NODE_INDEX, baseName .. "#node", "Node")
71 schema:register(XMLValueType.NODE_INDEX, baseName .. "#nodeVisual", "Visual node")
72
73 schema:register(XMLValueType.BOOL, baseName .. "#supportsHardAttach", "Supports hard attach")
74 schema:register(XMLValueType.STRING, baseName .. "#jointType", "Joint type", "implement")
75
76 schema:register(XMLValueType.STRING, baseName .. ".subType#name", "If defined this type needs to match with the sub type in the tool")
77 schema:register(XMLValueType.STRING, baseName .. ".subType#brandRestriction", "If defined it's only possible to attach tools from these brands (can be multiple separated by ' ')")
78 schema:register(XMLValueType.STRING, baseName .. ".subType#vehicleRestriction", "If defined it's only possible to attach tools containing these strings in there xml path (can be multiple separated by ' ')")
79 schema:register(XMLValueType.BOOL, baseName .. ".subType#subTypeShowWarning", "Show warning if sub type does not match", true)
80
81 schema:register(XMLValueType.BOOL, baseName .. "#allowsJointLimitMovement", "Allows joint limit movement", true)
82 schema:register(XMLValueType.BOOL, baseName .. "#allowsLowering", "Allows lowering", true)
83 schema:register(XMLValueType.BOOL, baseName .. "#isDefaultLowered", "Default lowered state", false)
84 schema:register(XMLValueType.BOOL, baseName .. "#allowDetachingWhileLifted", "Allow detach while lifted", true)
85 schema:register(XMLValueType.BOOL, baseName .. "#allowFoldingWhileAttached", "Allow folding while attached", true)
86
87 schema:register(XMLValueType.BOOL, baseName .. "#canTurnOnImplement", "Can turn on implement", true)
88
89 schema:register(XMLValueType.NODE_INDEX, baseName .. ".rotationNode#node", "Rotation node")
90 schema:register(XMLValueType.VECTOR_ROT, baseName .. ".rotationNode#lowerRotation", "Lower rotation", "0 0 0")
91 schema:register(XMLValueType.VECTOR_ROT, baseName .. ".rotationNode#upperRotation", "Upper rotation", "rotation in i3d")
92 schema:register(XMLValueType.VECTOR_ROT, baseName .. ".rotationNode#startRotation", "Start rotation", "rotation in i3d")
93
94 schema:register(XMLValueType.NODE_INDEX, baseName .. ".rotationNode2#node", "Rotation node")
95 schema:register(XMLValueType.VECTOR_ROT, baseName .. ".rotationNode2#lowerRotation", "Lower rotation", "0 0 0")
96 schema:register(XMLValueType.VECTOR_ROT, baseName .. ".rotationNode2#upperRotation", "Upper rotation", "rotation in i3d")
97
98 schema:register(XMLValueType.NODE_INDEX, baseName .. ".transNode#node", "Translation node")
99 schema:register(XMLValueType.FLOAT, baseName .. ".transNode#height", "Height of visual translation node", 0.12)
100 schema:register(XMLValueType.FLOAT, baseName .. ".transNode#minY", "Min Y translation")
101 schema:register(XMLValueType.FLOAT, baseName .. ".transNode#maxY", "Max Y translation")
102
103 schema:register(XMLValueType.NODE_INDEX, baseName .. ".transNode.dependentBottomArm#node", "Dependent bottom arm node")
104 schema:register(XMLValueType.FLOAT, baseName .. ".transNode.dependentBottomArm#threshold", "If the trans node Y translation is below this threshold the rotation will be set", "unlimited, so rotation is always set")
105 schema:register(XMLValueType.VECTOR_ROT, baseName .. ".transNode.dependentBottomArm#rotation", "Rotation to be set when the translation node is below the threshold", "0 0 0")
106
107 schema:register(XMLValueType.FLOAT, baseName .. ".distanceToGround#lower", "Lower distance to ground", 0.7)
108 schema:register(XMLValueType.FLOAT, baseName .. ".distanceToGround#upper", "Upper distance to ground", 1.0)
109
110
111 schema:register(XMLValueType.ANGLE, baseName .. "#lowerRotationOffset", "Upper rotation offset", 0)
112 schema:register(XMLValueType.ANGLE, baseName .. "#upperRotationOffset", "Lower rotation offset", 0)
113
114 schema:register(XMLValueType.BOOL, baseName .. "#lockDownRotLimit", "Lock down rotation limit", false)
115 schema:register(XMLValueType.BOOL, baseName .. "#lockUpRotLimit", "Lock up rotation limit", false)
116
117 schema:register(XMLValueType.BOOL, baseName .. "#lockDownTransLimit", "Lock down translation limit", true)
118 schema:register(XMLValueType.BOOL, baseName .. "#lockUpTransLimit", "Lock up translation limit", false)
119
120
121 schema:register(XMLValueType.VECTOR_ROT, baseName .. "#lowerRotLimit", "Lower rotation limit", "(20 20 20) for implement type, otherwise (0 0 0)")
122 schema:register(XMLValueType.VECTOR_ROT, baseName .. "#upperRotLimit", "Upper rotation limit", "Lower rot limit")
123
124 schema:register(XMLValueType.VECTOR_3, baseName .. "#lowerTransLimit", "Lower translation limit", "(0.5 0.5 0.5) for implement type, otherwise (0 0 0)")
125 schema:register(XMLValueType.VECTOR_3, baseName .. "#upperTransLimit", "Upper translation limit", "Lower trans limit")
126
127 schema:register(XMLValueType.VECTOR_3, baseName .. "#jointPositionOffset", "Joint position offset", "0 0 0")
128
129 schema:register(XMLValueType.VECTOR_3, baseName .. "#rotLimitSpring", "Rotation limit spring", "0 0 0")
130 schema:register(XMLValueType.VECTOR_3, baseName .. "#rotLimitDamping", "Rotation limit damping", "1 1 1")
131 schema:register(XMLValueType.VECTOR_3, baseName .. "#rotLimitForceLimit", "Rotation limit force limit", "-1 -1 -1")
132
133 schema:register(XMLValueType.VECTOR_3, baseName .. "#transLimitSpring", "Translation limit spring", "0 0 0")
134 schema:register(XMLValueType.VECTOR_3, baseName .. "#transLimitDamping", "Translation limit damping", "1 1 1")
135 schema:register(XMLValueType.VECTOR_3, baseName .. "#transLimitForceLimit", "Translation limit force limit", "-1 -1 -1")
136
137 schema:register(XMLValueType.FLOAT, baseName .. "#moveTime", "Move time", 0.5)
138 schema:register(XMLValueType.VECTOR_N, baseName .. "#disabledByAttacherJoints", "This attacher becomes unavailable after attaching something to these attacher joint indices")
139 schema:register(XMLValueType.BOOL, baseName .. "#enableCollision", "Collision between vehicle is enabled", false)
140
141 schema:register(XMLValueType.STRING, baseName .. ".topArm#filename", "Path to top arm i3d file")
142 schema:register(XMLValueType.NODE_INDEX, baseName .. ".topArm#baseNode", "Link node for upper link")
143 schema:register(XMLValueType.INT, baseName .. ".topArm#zScale", "Inverts top arm direction", 1)
144 schema:register(XMLValueType.BOOL, baseName .. ".topArm#toggleVisibility", "Top arm will be hidden on detach", false)
145 schema:register(XMLValueType.COLOR, baseName .. ".topArm#color", "Top arm color")
146 schema:register(XMLValueType.COLOR, baseName .. ".topArm#color2", "Top arm color 2")
147 schema:register(XMLValueType.COLOR, baseName .. ".topArm#decalColor", "Top arm decal color")
148 schema:register(XMLValueType.BOOL, baseName .. ".topArm#secondPartUseMainColor", "Second part of upper link (parts on colorMat3) will use main color or color2", true)
149 schema:register(XMLValueType.BOOL, baseName .. ".topArm#useBrandDecal", "Defines if the brand decal on the top arm is allowed or not", true)
150 schema:register(XMLValueType.INT, baseName .. ".topArm#material", "Top arm material index")
151 schema:register(XMLValueType.INT, baseName .. ".topArm#material2", "Top arm material index 2")
152
153 schema:register(XMLValueType.NODE_INDEX, baseName .. ".topArm#rotationNode", "Rotation node if top arm not loaded from i3d")
154 schema:register(XMLValueType.NODE_INDEX, baseName .. ".topArm#translationNode", "Translation node if top arm not loaded from i3d")
155 schema:register(XMLValueType.NODE_INDEX, baseName .. ".topArm#referenceNode", "Reference node if top arm not loaded from i3d")
156
157 schema:register(XMLValueType.NODE_INDEX, baseName .. ".bottomArm#rotationNode", "Rotation node of bottom arm")
158 schema:register(XMLValueType.NODE_INDEX, baseName .. ".bottomArm#translationNode", "Translation node of bottom arm")
159 schema:register(XMLValueType.NODE_INDEX, baseName .. ".bottomArm#referenceNode", "Reference node of bottom arm")
160 schema:register(XMLValueType.VECTOR_ROT, baseName .. ".bottomArm#startRotation", "Start rotation", "values set in i3d")
161 schema:register(XMLValueType.INT, baseName .. ".bottomArm#zScale", "Inverts bottom arm direction", 1)
162 schema:register(XMLValueType.BOOL, baseName .. ".bottomArm#lockDirection", "Lock direction", true)
163 schema:register(XMLValueType.ANGLE, baseName .. ".bottomArm#resetSpeed", "Speed of bottom arm to return to idle position (deg/sec)", 45)
164 schema:register(XMLValueType.BOOL, baseName .. ".bottomArm#toggleVisibility", "Bottom arm will be hidden on detach", false)
165
166 schema:register(XMLValueType.STRING, baseName .. ".toolbar#filename", "Filename to toolbar i3d", "$data/shared/assets/toolbar.i3d")
167
168 SoundManager.registerSampleXMLPaths(schema, baseName, "attachSound")
169 SoundManager.registerSampleXMLPaths(schema, baseName, "detachSound")
170
171 schema:register(XMLValueType.NODE_INDEX, baseName .. ".steeringBars#leftNode", "Steering bar left node")
172 schema:register(XMLValueType.NODE_INDEX, baseName .. ".steeringBars#rightNode", "Steering bar right node")
173 schema:register(XMLValueType.BOOL, baseName .. ".steeringBars#forceUsage", "Forces usage of tools steering axle even if no steering bars are defined", true)
174
175 schema:register(XMLValueType.NODE_INDICES, baseName .. ".visuals#nodes", "Visual nodes of attacher joint that will be visible when the joint is active")
176 schema:register(XMLValueType.NODE_INDICES, baseName .. ".visuals#hide", "Visual nodes that will be hidden while attacher joint is active if there attacher is inactive")
177
178 ObjectChangeUtil.registerObjectChangeXMLPaths(schema, baseName)
179 schema:register(XMLValueType.BOOL, baseName .. "#delayedObjectChanges", "Defines if object change is deactivated after the bottomArm has moved (if available)", true)
180 schema:register(XMLValueType.BOOL, baseName .. "#delayedObjectChangesOnAttach", "Defines if object change is activated on attach or post attach", false)
181
182 schema:register(XMLValueType.INT, baseName .. ".additionalAttachment#attacherJointDirection", "Attacher joint direction")
183 schema:register(XMLValueType.NODE_INDEX, baseName .. "#rootNode", "Root node", "first component")
184
185 schema:register(XMLValueType.FLOAT, baseName .. "#comboTime", "Combo time")
186
187 schema:register(XMLValueType.VECTOR_2, baseName .. ".schema#position", "Schema position")
188 schema:register(XMLValueType.VECTOR_2, baseName .. ".schema#liftedOffset", "Offset if lifted", "0 5")
189 schema:register(XMLValueType.ANGLE, baseName .. ".schema#rotation", "Schema rotation", 0)
190 schema:register(XMLValueType.BOOL, baseName .. ".schema#invertX", "Invert X", false)
191end

registerEventListeners

Description
Definition
registerEventListeners()
Code
340function AttacherJoints.registerEventListeners(vehicleType)
341 SpecializationUtil.registerEventListener(vehicleType, "onPreLoad", AttacherJoints)
342 SpecializationUtil.registerEventListener(vehicleType, "onLoad", AttacherJoints)
343 SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", AttacherJoints)
344 SpecializationUtil.registerEventListener(vehicleType, "onPreDelete", AttacherJoints)
345 SpecializationUtil.registerEventListener(vehicleType, "onDelete", AttacherJoints)
346 SpecializationUtil.registerEventListener(vehicleType, "onReadStream", AttacherJoints)
347 SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", AttacherJoints)
348 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", AttacherJoints)
349 SpecializationUtil.registerEventListener(vehicleType, "onUpdateEnd", AttacherJoints)
350 SpecializationUtil.registerEventListener(vehicleType, "onPostUpdate", AttacherJoints)
351 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", AttacherJoints)
352 SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", AttacherJoints)
353 SpecializationUtil.registerEventListener(vehicleType, "onStateChange", AttacherJoints)
354 SpecializationUtil.registerEventListener(vehicleType, "onLightsTypesMaskChanged", AttacherJoints)
355 SpecializationUtil.registerEventListener(vehicleType, "onTurnLightStateChanged", AttacherJoints)
356 SpecializationUtil.registerEventListener(vehicleType, "onBrakeLightsVisibilityChanged", AttacherJoints)
357 SpecializationUtil.registerEventListener(vehicleType, "onReverseLightsVisibilityChanged", AttacherJoints)
358 SpecializationUtil.registerEventListener(vehicleType, "onBeaconLightsVisibilityChanged", AttacherJoints)
359 SpecializationUtil.registerEventListener(vehicleType, "onBrake", AttacherJoints)
360 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOn", AttacherJoints)
361 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOff", AttacherJoints)
362 SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", AttacherJoints)
363 SpecializationUtil.registerEventListener(vehicleType, "onActivate", AttacherJoints)
364 SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", AttacherJoints)
365 SpecializationUtil.registerEventListener(vehicleType, "onReverseDirectionChanged", AttacherJoints)
366end

registerEvents

Description
Definition
registerEvents()
Code
235function AttacherJoints.registerEvents(vehicleType)
236 SpecializationUtil.registerEvent(vehicleType, "onPreAttachImplement")
237 SpecializationUtil.registerEvent(vehicleType, "onPostAttachImplement")
238 SpecializationUtil.registerEvent(vehicleType, "onPreDetachImplement")
239 SpecializationUtil.registerEvent(vehicleType, "onPostDetachImplement")
240end

registerFunctions

Description
Definition
registerFunctions()
Code
244function AttacherJoints.registerFunctions(vehicleType)
245 SpecializationUtil.registerFunction(vehicleType, "saveAttachmentsToXMLFile", AttacherJoints.saveAttachmentsToXMLFile)
246 SpecializationUtil.registerFunction(vehicleType, "loadAttachmentsFromXMLFile", AttacherJoints.loadAttachmentsFromXMLFile)
247 SpecializationUtil.registerFunction(vehicleType, "loadAttachmentsFinished", AttacherJoints.loadAttachmentsFinished)
248 SpecializationUtil.registerFunction(vehicleType, "handleLowerImplementEvent", AttacherJoints.handleLowerImplementEvent)
249 SpecializationUtil.registerFunction(vehicleType, "handleLowerImplementByAttacherJointIndex", AttacherJoints.handleLowerImplementByAttacherJointIndex)
250 SpecializationUtil.registerFunction(vehicleType, "getAttachedImplements", AttacherJoints.getAttachedImplements)
251 SpecializationUtil.registerFunction(vehicleType, "getAttacherJoints", AttacherJoints.getAttacherJoints)
252 SpecializationUtil.registerFunction(vehicleType, "getAttacherJointByJointDescIndex", AttacherJoints.getAttacherJointByJointDescIndex)
253 SpecializationUtil.registerFunction(vehicleType, "getAttacherJointIndexByNode", AttacherJoints.getAttacherJointIndexByNode)
254 SpecializationUtil.registerFunction(vehicleType, "getImplementFromAttacherJointIndex", AttacherJoints.getImplementFromAttacherJointIndex)
255 SpecializationUtil.registerFunction(vehicleType, "getAttacherJointIndexFromObject", AttacherJoints.getAttacherJointIndexFromObject)
256 SpecializationUtil.registerFunction(vehicleType, "getAttacherJointDescFromObject", AttacherJoints.getAttacherJointDescFromObject)
257 SpecializationUtil.registerFunction(vehicleType, "getAttacherJointIndexFromImplementIndex", AttacherJoints.getAttacherJointIndexFromImplementIndex)
258 SpecializationUtil.registerFunction(vehicleType, "getObjectFromImplementIndex", AttacherJoints.getObjectFromImplementIndex)
259 SpecializationUtil.registerFunction(vehicleType, "updateAttacherJointGraphics", AttacherJoints.updateAttacherJointGraphics)
260 SpecializationUtil.registerFunction(vehicleType, "calculateAttacherJointMoveUpperLowerAlpha", AttacherJoints.calculateAttacherJointMoveUpperLowerAlpha)
261 SpecializationUtil.registerFunction(vehicleType, "doGroundHeightNodeCheck", AttacherJoints.doGroundHeightNodeCheck)
262 SpecializationUtil.registerFunction(vehicleType, "finishGroundHeightNodeCheck", AttacherJoints.finishGroundHeightNodeCheck)
263 SpecializationUtil.registerFunction(vehicleType, "groundHeightNodeCheckCallback", AttacherJoints.groundHeightNodeCheckCallback)
264 SpecializationUtil.registerFunction(vehicleType, "updateAttacherJointRotation", AttacherJoints.updateAttacherJointRotation)
265 SpecializationUtil.registerFunction(vehicleType, "updateAttacherJointRotationNodes", AttacherJoints.updateAttacherJointRotationNodes)
266 SpecializationUtil.registerFunction(vehicleType, "updateAttacherJointSettingsByObject", AttacherJoints.updateAttacherJointSettingsByObject)
267 SpecializationUtil.registerFunction(vehicleType, "attachImplementFromInfo", AttacherJoints.attachImplementFromInfo)
268 SpecializationUtil.registerFunction(vehicleType, "attachImplement", AttacherJoints.attachImplement)
269 SpecializationUtil.registerFunction(vehicleType, "postAttachImplement", AttacherJoints.postAttachImplement)
270 SpecializationUtil.registerFunction(vehicleType, "createAttachmentJoint", AttacherJoints.createAttachmentJoint)
271 SpecializationUtil.registerFunction(vehicleType, "hardAttachImplement", AttacherJoints.hardAttachImplement)
272 SpecializationUtil.registerFunction(vehicleType, "hardDetachImplement", AttacherJoints.hardDetachImplement)
273 SpecializationUtil.registerFunction(vehicleType, "detachImplement", AttacherJoints.detachImplement)
274 SpecializationUtil.registerFunction(vehicleType, "detachImplementByObject", AttacherJoints.detachImplementByObject)
275 SpecializationUtil.registerFunction(vehicleType, "playAttachSound", AttacherJoints.playAttachSound)
276 SpecializationUtil.registerFunction(vehicleType, "playDetachSound", AttacherJoints.playDetachSound)
277 SpecializationUtil.registerFunction(vehicleType, "detachingIsPossible", AttacherJoints.detachingIsPossible)
278 SpecializationUtil.registerFunction(vehicleType, "attachAdditionalAttachment", AttacherJoints.attachAdditionalAttachment)
279 SpecializationUtil.registerFunction(vehicleType, "detachAdditionalAttachment", AttacherJoints.detachAdditionalAttachment)
280 SpecializationUtil.registerFunction(vehicleType, "getImplementIndexByJointDescIndex", AttacherJoints.getImplementIndexByJointDescIndex)
281 SpecializationUtil.registerFunction(vehicleType, "getImplementByJointDescIndex", AttacherJoints.getImplementByJointDescIndex)
282 SpecializationUtil.registerFunction(vehicleType, "getImplementIndexByObject", AttacherJoints.getImplementIndexByObject)
283 SpecializationUtil.registerFunction(vehicleType, "getImplementByObject", AttacherJoints.getImplementByObject)
284 SpecializationUtil.registerFunction(vehicleType, "callFunctionOnAllImplements", AttacherJoints.callFunctionOnAllImplements)
285 SpecializationUtil.registerFunction(vehicleType, "activateAttachments", AttacherJoints.activateAttachments)
286 SpecializationUtil.registerFunction(vehicleType, "deactivateAttachments", AttacherJoints.deactivateAttachments)
287 SpecializationUtil.registerFunction(vehicleType, "deactivateAttachmentsLights", AttacherJoints.deactivateAttachmentsLights)
288 SpecializationUtil.registerFunction(vehicleType, "setJointMoveDown", AttacherJoints.setJointMoveDown)
289 SpecializationUtil.registerFunction(vehicleType, "getJointMoveDown", AttacherJoints.getJointMoveDown)
290 SpecializationUtil.registerFunction(vehicleType, "getIsHardAttachAllowed", AttacherJoints.getIsHardAttachAllowed)
291 SpecializationUtil.registerFunction(vehicleType, "loadAttacherJointFromXML", AttacherJoints.loadAttacherJointFromXML)
292 SpecializationUtil.registerFunction(vehicleType, "onTopArmI3DLoaded", AttacherJoints.onTopArmI3DLoaded)
293 SpecializationUtil.registerFunction(vehicleType, "onBottomArmToolbarI3DLoaded", AttacherJoints.onBottomArmToolbarI3DLoaded)
294 SpecializationUtil.registerFunction(vehicleType, "setSelectedImplementByObject", AttacherJoints.setSelectedImplementByObject)
295 SpecializationUtil.registerFunction(vehicleType, "getSelectedImplement", AttacherJoints.getSelectedImplement)
296 SpecializationUtil.registerFunction(vehicleType, "getCanToggleAttach", AttacherJoints.getCanToggleAttach)
297 SpecializationUtil.registerFunction(vehicleType, "getShowDetachAttachedImplement", AttacherJoints.getShowDetachAttachedImplement)
298 SpecializationUtil.registerFunction(vehicleType, "detachAttachedImplement", AttacherJoints.detachAttachedImplement)
299 SpecializationUtil.registerFunction(vehicleType, "startAttacherJointCombo", AttacherJoints.startAttacherJointCombo)
300 SpecializationUtil.registerFunction(vehicleType, "registerSelfLoweringActionEvent", AttacherJoints.registerSelfLoweringActionEvent)
301 SpecializationUtil.registerFunction(vehicleType, "getIsAttachingAllowed", AttacherJoints.getIsAttachingAllowed)
302 SpecializationUtil.registerFunction(vehicleType, "getCanSteerAttachable", AttacherJoints.getCanSteerAttachable)
303end

registerJointType

Description
Registration of attacher joint type
Definition
registerJointType(string name)
Arguments
stringnamename if attacher type
Code
196function AttacherJoints.registerJointType(name)
197 local key = "JOINTTYPE_"..string.upper(name)
198 if AttacherJoints[key] == nil then
199 AttacherJoints.NUM_JOINTTYPES = AttacherJoints.NUM_JOINTTYPES+1
200 AttacherJoints[key] = AttacherJoints.NUM_JOINTTYPES
201 AttacherJoints.jointTypeNameToInt[name] = AttacherJoints.NUM_JOINTTYPES
202 end
203end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
307function AttacherJoints.registerOverwrittenFunctions(vehicleType)
308 SpecializationUtil.registerOverwrittenFunction(vehicleType, "raiseActive", AttacherJoints.raiseActive)
309 SpecializationUtil.registerOverwrittenFunction(vehicleType, "registerActionEvents", AttacherJoints.registerActionEvents)
310 SpecializationUtil.registerOverwrittenFunction(vehicleType, "removeActionEvents", AttacherJoints.removeActionEvents)
311 SpecializationUtil.registerOverwrittenFunction(vehicleType, "addToPhysics", AttacherJoints.addToPhysics)
312 SpecializationUtil.registerOverwrittenFunction(vehicleType, "removeFromPhysics", AttacherJoints.removeFromPhysics)
313 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getTotalMass", AttacherJoints.getTotalMass)
314 SpecializationUtil.registerOverwrittenFunction(vehicleType, "addChildVehicles", AttacherJoints.addChildVehicles)
315 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAirConsumerUsage", AttacherJoints.getAirConsumerUsage)
316 SpecializationUtil.registerOverwrittenFunction(vehicleType, "addVehicleToAIImplementList", AttacherJoints.addVehicleToAIImplementList)
317 SpecializationUtil.registerOverwrittenFunction(vehicleType, "collectAIAgentAttachments", AttacherJoints.collectAIAgentAttachments)
318 SpecializationUtil.registerOverwrittenFunction(vehicleType, "setAIVehicleObstacleStateDirty", AttacherJoints.setAIVehicleObstacleStateDirty)
319 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDirectionSnapAngle", AttacherJoints.getDirectionSnapAngle)
320 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getFillLevelInformation", AttacherJoints.getFillLevelInformation)
321 SpecializationUtil.registerOverwrittenFunction(vehicleType, "attachableAddToolCameras", AttacherJoints.attachableAddToolCameras)
322 SpecializationUtil.registerOverwrittenFunction(vehicleType, "attachableRemoveToolCameras", AttacherJoints.attachableRemoveToolCameras)
323 SpecializationUtil.registerOverwrittenFunction(vehicleType, "registerSelectableObjects", AttacherJoints.registerSelectableObjects)
324 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsReadyForAutomatedTrainTravel", AttacherJoints.getIsReadyForAutomatedTrainTravel)
325 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsAutomaticShiftingAllowed", AttacherJoints.getIsAutomaticShiftingAllowed)
326 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadDashboardGroupFromXML", AttacherJoints.loadDashboardGroupFromXML)
327 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsDashboardGroupActive", AttacherJoints.getIsDashboardGroupActive)
328 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadAttacherJointHeightNode", AttacherJoints.loadAttacherJointHeightNode)
329 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsAttacherJointHeightNodeActive", AttacherJoints.getIsAttacherJointHeightNodeActive)
330 SpecializationUtil.registerOverwrittenFunction(vehicleType, "isDetachAllowed", AttacherJoints.isDetachAllowed)
331 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsFoldAllowed", AttacherJoints.getIsFoldAllowed)
332 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsWheelFoliageDestructionAllowed", AttacherJoints.getIsWheelFoliageDestructionAllowed)
333 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAreControlledActionsAllowed", AttacherJoints.getAreControlledActionsAllowed)
334 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getConnectionHoseConfigIndex", AttacherJoints.getConnectionHoseConfigIndex)
335 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getPowerTakeOffConfigIndex", AttacherJoints.getPowerTakeOffConfigIndex)
336end

registerSelectableObjects

Description
Definition
registerSelectableObjects()
Code
3606function AttacherJoints:registerSelectableObjects(superFunc, selectableObjects)
3607 superFunc(self, selectableObjects)
3608
3609 local spec = self.spec_attacherJoints
3610 for _,implement in pairs(spec.attachedImplements) do
3611 local object = implement.object
3612 if object ~= nil and object.registerSelectableObjects ~= nil then
3613 object:registerSelectableObjects(selectableObjects)
3614 end
3615 end
3616end

registerSelfLoweringActionEvent

Description
Definition
registerSelfLoweringActionEvent()
Code
2530function AttacherJoints:registerSelfLoweringActionEvent(actionEventsTable, inputAction, target, callback, triggerUp, triggerDown, triggerAlways, startActive, callbackState, customIconName, ignoreCollisions)
2531end

removeActionEvents

Description
Definition
removeActionEvents()
Code
3398function AttacherJoints:removeActionEvents(superFunc)
3399 local spec = self.spec_attacherJoints
3400
3401 superFunc(self)
3402
3403 for _,implement in pairs(spec.attachedImplements) do
3404 if implement.object ~= nil then
3405 implement.object:removeActionEvents()
3406 end
3407 end
3408end

removeFromPhysics

Description
Add to physics
Definition
removeFromPhysics()
Return Values
booleansuccesssuccess
Code
3432function AttacherJoints:removeFromPhysics(superFunc)
3433 local spec = self.spec_attacherJoints
3434
3435 for _, implement in pairs(spec.attachedImplements) do
3436 if not implement.object.spec_attachable.isHardAttached then
3437 local jointDesc = spec.attacherJoints[implement.jointDescIndex]
3438 if jointDesc.jointIndex ~= 0 then
3439 jointDesc.jointIndex = 0
3440 --removeJoint(jointDesc.jointIndex)
3441 end
3442 end
3443 end
3444
3445 if not superFunc(self) then
3446 return false
3447 end
3448
3449 return true
3450end

saveAttachmentsToXMLFile

Description
Definition
saveAttachmentsToXMLFile()
Code
654function AttacherJoints:saveAttachmentsToXMLFile(xmlFile, key, vehiclesToId)
655 local spec = self.spec_attacherJoints
656 local added = false
657
658 local id = vehiclesToId[self]
659 if id ~= nil then
660 local i = 0
661 for _, implement in ipairs(spec.attachedImplements) do
662 local object = implement.object
663 if object ~= nil and vehiclesToId[object] ~= nil then
664 local attachmentKey = string.format("%s.attachment(%d)", key, i)
665 local jointDescIndex = implement.jointDescIndex
666 local jointDesc = spec.attacherJoints[jointDescIndex]
667 local inputJointDescIndex = object:getActiveInputAttacherJointDescIndex()
668 xmlFile:setValue(attachmentKey.."#attachmentId", vehiclesToId[object])
669 xmlFile:setValue(attachmentKey.."#inputJointDescIndex", inputJointDescIndex)
670 xmlFile:setValue(attachmentKey.."#jointIndex", jointDescIndex)
671 xmlFile:setValue(attachmentKey.."#moveDown", jointDesc.moveDown)
672 added = true
673 i = i + 1
674 end
675 end
676
677 if added then
678 xmlFile:setValue(key.."#rootVehicleId", id)
679 end
680 end
681
682 return added
683end

saveToXMLFile

Description
Definition
saveToXMLFile()
Code
645function AttacherJoints:saveToXMLFile(xmlFile, key, usedModNames)
646 local spec = self.spec_attacherJoints
647 if spec.attacherJointCombos ~= nil then
648 xmlFile:setValue(key.."#comboDirection", spec.attacherJointCombos.direction)
649 end
650end

setAIVehicleObstacleStateDirty

Description
Definition
setAIVehicleObstacleStateDirty()
Code
3534function AttacherJoints:setAIVehicleObstacleStateDirty(superFunc)
3535 superFunc(self)
3536
3537 for _, implement in pairs(self:getAttachedImplements()) do
3538 local object = implement.object
3539 if object ~= nil and object.setAIVehicleObstacleStateDirty ~= nil then
3540 object:setAIVehicleObstacleStateDirty()
3541 end
3542 end
3543end

setJointMoveDown

Description
Set joint move down
Definition
setJointMoveDown(integer jointDescIndex, boolean moveDown, boolean noEventSend)
Arguments
integerjointDescIndexindex of joint desc
booleanmoveDownmove down
booleannoEventSendno event send
Return Values
booleansuccesssuccess
Code
2798function AttacherJoints:setJointMoveDown(jointDescIndex, moveDown, noEventSend)
2799 local spec = self.spec_attacherJoints
2800
2801 local jointDesc = spec.attacherJoints[jointDescIndex]
2802 if moveDown ~= jointDesc.moveDown then
2803 if jointDesc.allowsLowering then
2804 jointDesc.moveDown = moveDown
2805 jointDesc.isMoving = true
2806
2807 local implementIndex = self:getImplementIndexByJointDescIndex(jointDescIndex)
2808 if implementIndex ~= nil then
2809 local implement = spec.attachedImplements[implementIndex]
2810 if implement.object ~= nil then
2811 implement.object:setLowered(moveDown)
2812 end
2813 end
2814 end
2815
2816 VehicleLowerImplementEvent.sendEvent(self, jointDescIndex, moveDown, noEventSend)
2817 end
2818
2819 return true
2820end

setSelectedImplementByObject

Description
Definition
setSelectedImplementByObject()
Code
2428function AttacherJoints:setSelectedImplementByObject(object)
2429 self.spec_attacherJoints.selectedImplement = self:getImplementByObject(object)
2430end

startAttacherJointCombo

Description
Definition
startAttacherJointCombo()
Code
2485function AttacherJoints:startAttacherJointCombo(force)
2486 local spec = self.spec_attacherJoints
2487
2488 if not spec.attacherJointCombos.isRunning or force then
2489 spec.attacherJointCombos.direction = -spec.attacherJointCombos.direction
2490 spec.attacherJointCombos.isRunning = true
2491 end
2492end

updateActionEvents

Description
Definition
updateActionEvents()
Code
4312function AttacherJoints.updateActionEvents(self)
4313 local spec = self.spec_attacherJoints
4314 local info = spec.attachableInfo
4315
4316 if self.isClient then
4317 if spec.actionEvents ~= nil then
4318 local attachActionEvent = spec.actionEvents[InputAction.ATTACH]
4319 if attachActionEvent ~= nil then
4320 local visible = false
4321
4322 if self:getCanToggleAttach() then
4323 if info.warning ~= nil then
4324 g_currentMission:showBlinkingWarning(info.warning, 500)
4325 end
4326
4327 local text = ""
4328 local prio = GS_PRIO_VERY_LOW
4329
4330 local selectedVehicle = self:getSelectedVehicle()
4331 if selectedVehicle ~= nil and not selectedVehicle.isDeleted and selectedVehicle.isDetachAllowed ~= nil and selectedVehicle:isDetachAllowed() then
4332 if selectedVehicle:getAttacherVehicle() ~= nil then
4333 visible = true
4334 text = spec.texts.actionDetach
4335 end
4336 end
4337
4338 if info.attacherVehicle ~= nil then
4339 if g_currentMission.accessHandler:canFarmAccess(self:getActiveFarm(), info.attachable) then
4340 visible = true
4341 text = spec.texts.actionAttach
4342 g_currentMission:showAttachContext(info.attachable)
4343 prio = GS_PRIO_VERY_HIGH
4344 else
4345 spec.showAttachNotAllowedText = 100
4346 end
4347 end
4348
4349 g_inputBinding:setActionEventText(attachActionEvent.actionEventId, text)
4350 g_inputBinding:setActionEventTextPriority(attachActionEvent.actionEventId, prio)
4351 end
4352
4353 g_inputBinding:setActionEventTextVisibility(attachActionEvent.actionEventId, visible)
4354 end
4355
4356 local lowerActionEvent = spec.actionEvents[InputAction.LOWER_IMPLEMENT]
4357 if lowerActionEvent ~= nil then
4358 local showLower = false
4359 local text = ""
4360 local selectedImplement = self:getSelectedImplement()
4361 for _,attachedImplement in pairs(spec.attachedImplements) do
4362 if attachedImplement == selectedImplement then
4363 showLower, text = attachedImplement.object:getLoweringActionEventState()
4364 break
4365 end
4366 end
4367
4368 g_inputBinding:setActionEventActive(lowerActionEvent.actionEventId, showLower)
4369 g_inputBinding:setActionEventText(lowerActionEvent.actionEventId, text)
4370 g_inputBinding:setActionEventTextPriority(lowerActionEvent.actionEventId, GS_PRIO_NORMAL)
4371 end
4372 end
4373 end
4374end

updateAttacherJointGraphics

Description
Update attacher joint graphics
Definition
updateAttacherJointGraphics(table implement, float dt)
Arguments
tableimplementimplement
floatdttime since last call in ms
Code
1183function AttacherJoints:updateAttacherJointGraphics(implement, dt, forceUpdate)
1184 local spec = self.spec_attacherJoints
1185
1186 if implement.object ~= nil then
1187 local jointDesc = spec.attacherJoints[implement.jointDescIndex]
1188
1189 local attacherJoint = implement.object:getActiveInputAttacherJoint()
1190
1191 if jointDesc.topArm ~= nil and attacherJoint.topReferenceNode ~= nil then
1192 local ax, ay, az = getWorldTranslation(jointDesc.topArm.rotationNode)
1193 local bx, by, bz = getWorldTranslation(attacherJoint.topReferenceNode)
1194
1195 local x, y, z = worldDirectionToLocal(getParent(jointDesc.topArm.rotationNode), bx-ax, by-ay, bz-az)
1196 local distance = MathUtil.vector3Length(x,y,z)
1197
1198 -- different approach I) rotate actual direction of topArm by 90degree around x-axis
1199 local alpha = math.rad(-90)
1200 -- check if rotationNode is at back of tractor => inverted rotation direction, could be dismissed by rotating TG in i3d
1201 local px,py,pz = getWorldTranslation(jointDesc.topArm.rotationNode)
1202 local _,_,lz = worldToLocal(self.components[1].node, px,py,pz)
1203 if lz < 0 then
1204 alpha = math.rad(90)
1205 end
1206
1207 local dx, dy, dz = localDirectionToWorld(jointDesc.topArm.rotationNode, 0,0,1)
1208 dx, dy, dz = worldDirectionToLocal(getParent(jointDesc.topArm.rotationNode), dx, dy, dz)
1209 local upX = dx
1210 local upY = math.cos(alpha)*dy - math.sin(alpha)*dz
1211 local upZ = math.sin(alpha)*dy + math.cos(alpha)*dz
1212
1213 if not implement.attachingIsInProgress then
1214 setDirection(jointDesc.topArm.rotationNode, x*jointDesc.topArm.zScale, y*jointDesc.topArm.zScale, z*jointDesc.topArm.zScale, upX, upY, upZ)
1215
1216 if jointDesc.topArm.translationNode ~= nil then
1217 local translation = (distance-jointDesc.topArm.referenceDistance)
1218 setTranslation(jointDesc.topArm.translationNode, 0, 0, translation*jointDesc.topArm.zScale)
1219 if jointDesc.topArm.scaleNode ~= nil then
1220 setScale(jointDesc.topArm.scaleNode, 1, 1, math.max((translation+jointDesc.topArm.scaleReferenceDistance)/jointDesc.topArm.scaleReferenceDistance, 0))
1221 end
1222 end
1223 end
1224 end
1225 if jointDesc.bottomArm ~= nil then
1226 local ax, ay, az = getWorldTranslation(jointDesc.bottomArm.rotationNode)
1227 local bx, by, bz = getWorldTranslation(attacherJoint.node)
1228
1229 local x, y, z = worldDirectionToLocal(getParent(jointDesc.bottomArm.rotationNode), bx-ax, by-ay, bz-az)
1230 local distance = MathUtil.vector3Length(x,y,z)
1231 local upX, upY, upZ = 0,1,0
1232 if math.abs(y) > 0.99*distance then
1233 -- direction and up is parallel
1234 upY = 0
1235 if y > 0 then
1236 upZ = 1
1237 else
1238 upZ = -1
1239 end
1240 end
1241 local dirX, dirY, dirZ = 0, y*jointDesc.bottomArm.zScale, z*jointDesc.bottomArm.zScale
1242 if not jointDesc.bottomArm.lockDirection then
1243 dirX = x*jointDesc.bottomArm.zScale
1244 end
1245
1246 local changed = false
1247 if math.abs(jointDesc.bottomArm.lastDirection[1] - dirX) > 0.001 or
1248 math.abs(jointDesc.bottomArm.lastDirection[2] - dirY) > 0.001 or
1249 math.abs(jointDesc.bottomArm.lastDirection[3] - dirZ) > 0.001 then
1250
1251 if implement.attachingIsInProgress then
1252 -- set only direction node so we can use this rotation as reference for the interpolator
1253 setDirection(jointDesc.bottomArm.rotationNodeDir, dirX, dirY, dirZ, upX, upY, upZ)
1254 else
1255 setDirection(jointDesc.bottomArm.rotationNode, dirX, dirY, dirZ, upX, upY, upZ)
1256 end
1257
1258 jointDesc.bottomArm.lastDirection[1] = dirX
1259 jointDesc.bottomArm.lastDirection[2] = dirY
1260 jointDesc.bottomArm.lastDirection[3] = dirZ
1261
1262 changed = true
1263 end
1264
1265 if implement.attachingIsInProgress then
1266 if changed then
1267 if not implement.bottomArmInterpolating then
1268 local interpolator = ValueInterpolator.new(jointDesc.bottomArm.interpolatorKey, jointDesc.bottomArm.interpolatorGet, jointDesc.bottomArm.interpolatorSet, {getRotation(jointDesc.bottomArm.rotationNodeDir)}, AttacherJoints.SMOOTH_ATTACH_TIME)
1269 if interpolator ~= nil then
1270 interpolator:setDeleteListenerObject(self)
1271 interpolator:setFinishedFunc(jointDesc.bottomArm.interpolatorFinished, jointDesc.bottomArm)
1272
1273 jointDesc.bottomArm.bottomArmInterpolating = true
1274 implement.bottomArmInterpolating = true
1275 implement.bottomArmInterpolator = interpolator
1276 end
1277 else
1278 local rx, ry, rz = getRotation(jointDesc.bottomArm.rotationNodeDir)
1279 local target = implement.bottomArmInterpolator:getTarget()
1280 target[1], target[2], target[3] = rx, ry, rz
1281 implement.bottomArmInterpolator:updateSpeed()
1282 end
1283 end
1284 else
1285 ValueInterpolator.removeInterpolator(jointDesc.bottomArm.interpolatorKey)
1286 jointDesc.bottomArm.bottomArmInterpolating = false
1287 implement.bottomArmInterpolating = false
1288 implement.bottomArmInterpolator = nil
1289 end
1290
1291 if jointDesc.bottomArm.translationNode ~= nil and not implement.attachingIsInProgress then
1292 setTranslation(jointDesc.bottomArm.translationNode, 0, 0, (distance-jointDesc.bottomArm.referenceDistance)*jointDesc.bottomArm.zScale)
1293 end
1294 if self.setMovingToolDirty ~= nil then
1295 self:setMovingToolDirty(jointDesc.bottomArm.rotationNode, forceUpdate, dt)
1296 end
1297
1298 if attacherJoint.needsToolbar and jointDesc.bottomArm.toolbar ~= nil then
1299 local parent = getParent(jointDesc.bottomArm.toolbar)
1300
1301 local _, yDir, zDir = localDirectionToLocal(attacherJoint.node, jointDesc.rootNode, 1, 0, 0)
1302 local xDir, yDir, zDir = localDirectionToLocal(jointDesc.rootNode, parent, 0, yDir, zDir)
1303
1304 local _, yUp, zUp = localDirectionToLocal(attacherJoint.node, jointDesc.rootNode, 0, 1, 0)
1305 local xUp, yUp, zUp = localDirectionToLocal(jointDesc.rootNode, parent, 0, yUp, zUp)
1306
1307 setDirection(jointDesc.bottomArm.toolbar, xDir, yDir, zDir, xUp, yUp, zUp)
1308 end
1309 end
1310 end
1311end

updateAttacherJointLimits

Description
Definition
updateAttacherJointLimits()
Code
4378function AttacherJoints.updateAttacherJointLimits(implement, attacherJointDesc, inputAttacherJointDesc, axis)
4379 local lowerRotLimit = attacherJointDesc.lowerRotLimit[axis]*inputAttacherJointDesc.lowerRotLimitScale[axis]
4380 local upperRotLimit = attacherJointDesc.upperRotLimit[axis]*inputAttacherJointDesc.upperRotLimitScale[axis]
4381 if inputAttacherJointDesc.fixedRotation then
4382 lowerRotLimit = 0
4383 upperRotLimit = 0
4384 end
4385
4386 local upperTransLimit = attacherJointDesc.lowerTransLimit[axis]*inputAttacherJointDesc.lowerTransLimitScale[axis]
4387 local lowerTransLimit = attacherJointDesc.upperTransLimit[axis]*inputAttacherJointDesc.upperTransLimitScale[axis]
4388 implement.lowerRotLimit[axis] = lowerRotLimit
4389 implement.upperRotLimit[axis] = upperRotLimit
4390
4391 implement.lowerTransLimit[axis] = upperTransLimit
4392 implement.upperTransLimit[axis] = lowerTransLimit
4393
4394 if not attacherJointDesc.allowsLowering then
4395 implement.upperRotLimit[axis] = lowerRotLimit
4396 implement.upperTransLimit[axis] = upperTransLimit
4397 end
4398
4399 local rotLimit = lowerRotLimit
4400 local transLimit = upperTransLimit
4401 if attacherJointDesc.allowsLowering and attacherJointDesc.allowsJointLimitMovement then
4402 if inputAttacherJointDesc.allowsJointRotLimitMovement then
4403 rotLimit = MathUtil.lerp(upperRotLimit, lowerRotLimit, attacherJointDesc.moveAlpha)
4404 end
4405 if inputAttacherJointDesc.allowsJointTransLimitMovement then
4406 transLimit = MathUtil.lerp(lowerTransLimit, upperTransLimit, attacherJointDesc.moveAlpha)
4407 end
4408 end
4409
4410 return rotLimit, transLimit
4411end

updateAttacherJointRotation

Description
Update attacher joint rotations depending on move alpha
Definition
updateAttacherJointRotation(table jointDesc, table object)
Arguments
tablejointDescjoint desc of used attacher
tableobjectobject of attached vehicle
Code
1581function AttacherJoints:updateAttacherJointRotation(jointDesc, object)
1582 local objectAttacherJoint = object.spec_attachable.attacherJoint
1583
1584 -- rotate attacher such that
1585 local targetRot = MathUtil.lerp(objectAttacherJoint.upperRotationOffset, objectAttacherJoint.lowerRotationOffset, jointDesc.moveAlpha)
1586 local curRot = MathUtil.lerp(jointDesc.upperRotationOffset, jointDesc.lowerRotationOffset, jointDesc.moveAlpha)
1587 local rotDiff = targetRot - curRot
1588
1589 setRotation(jointDesc.jointTransform, unpack(jointDesc.jointOrigRot))
1590 rotateAboutLocalAxis(jointDesc.jointTransform, rotDiff, 0, 0, 1)
1591end

updateAttacherJointRotationLimit

Description
Definition
updateAttacherJointRotationLimit()
Code
4416function AttacherJoints.updateAttacherJointRotationLimit(implement, attacherJointDesc, axis, force, alpha)
4417 local newRotLimit = MathUtil.lerp( math.max(implement.attachingRotLimit[axis], implement.upperRotLimit[axis]),
4418 math.max(implement.attachingRotLimit[axis], implement.lowerRotLimit[axis]), alpha)
4419 if force or math.abs(newRotLimit - implement.jointRotLimit[axis]) > 0.0005 then
4420 local rotLimitDown = -newRotLimit
4421 local rotLimitUp = newRotLimit
4422 if axis == 3 then
4423 if attacherJointDesc.lockDownRotLimit then
4424 rotLimitDown = math.min(-implement.attachingRotLimit[axis], 0)
4425 end
4426 if attacherJointDesc.lockUpRotLimit then
4427 rotLimitUp = math.max(implement.attachingRotLimit[axis], 0)
4428 end
4429 end
4430 setJointRotationLimit(attacherJointDesc.jointIndex, axis-1, true, rotLimitDown, rotLimitUp)
4431 implement.jointRotLimit[axis] = newRotLimit
4432 end
4433end

updateAttacherJointRotationNodes

Description
Definition
updateAttacherJointRotationNodes()
Code
1595function AttacherJoints:updateAttacherJointRotationNodes(jointDesc, alpha)
1596 if jointDesc.rotationNode ~= nil then
1597 setRotation(jointDesc.rotationNode, MathUtil.vector3ArrayLerp(jointDesc.upperRotation, jointDesc.lowerRotation, alpha))
1598 end
1599 if jointDesc.rotationNode2 ~= nil then
1600 setRotation(jointDesc.rotationNode2, MathUtil.vector3ArrayLerp(jointDesc.upperRotation2, jointDesc.lowerRotation2, alpha))
1601 end
1602end

updateAttacherJointSettingsByObject

Description
Definition
updateAttacherJointSettingsByObject()
Code
1606function AttacherJoints:updateAttacherJointSettingsByObject(vehicle, updateLimit, updateRotationOffset, updateDistanceToGround)
1607 local jointDesc = self:getAttacherJointDescFromObject(vehicle)
1608 local implement = self:getImplementByObject(vehicle)
1609 local objectAttacherJoint = vehicle:getActiveInputAttacherJoint()
1610 if jointDesc ~= nil and implement ~= nil then
1611 if updateLimit then
1612 for i=1, 3 do
1613 AttacherJoints.updateAttacherJointLimits(implement, jointDesc, objectAttacherJoint, i)
1614 AttacherJoints.updateAttacherJointRotationLimit(implement, jointDesc, i, false, jointDesc.moveLimitAlpha)
1615 AttacherJoints.updateAttacherJointTranslationLimit(implement, jointDesc, i, false, jointDesc.moveLimitAlpha)
1616 end
1617 end
1618
1619 if updateRotationOffset then
1620 self:updateAttacherJointRotation(jointDesc, vehicle)
1621 if self.isServer then
1622 setJointFrame(jointDesc.jointIndex, 0, jointDesc.jointTransform)
1623 end
1624 end
1625
1626 if updateDistanceToGround then
1627 local upperAlpha, lowerAlpha = self:calculateAttacherJointMoveUpperLowerAlpha(jointDesc, vehicle)
1628 jointDesc.moveTime = jointDesc.moveDefaultTime * math.abs(upperAlpha - lowerAlpha)
1629 jointDesc.upperAlpha = upperAlpha
1630 jointDesc.lowerAlpha = lowerAlpha
1631 end
1632 end
1633end

updateAttacherJointTranslationLimit

Description
Definition
updateAttacherJointTranslationLimit()
Code
4437function AttacherJoints.updateAttacherJointTranslationLimit(implement, attacherJointDesc, axis, force, alpha)
4438 local newTransLimit = MathUtil.lerp( math.max(implement.attachingTransLimit[axis], implement.upperTransLimit[axis]),
4439 math.max(implement.attachingTransLimit[axis], implement.lowerTransLimit[axis]), alpha)
4440
4441 if force or math.abs(newTransLimit - implement.jointTransLimit[axis]) > 0.0005 then
4442 local transLimitDown = -newTransLimit
4443 local transLimitUp = newTransLimit
4444 if axis == 2 then
4445 if attacherJointDesc.lockDownTransLimit then
4446 transLimitDown = math.min(-implement.attachingTransLimit[axis], 0)
4447 end
4448 if attacherJointDesc.lockUpTransLimit then
4449 transLimitUp = math.max(implement.attachingTransLimit[axis], 0)
4450 end
4451 end
4452
4453 setJointTranslationLimit(attacherJointDesc.jointIndex, axis-1, true, transLimitDown, transLimitUp)
4454 implement.jointTransLimit[axis] = newTransLimit
4455 end
4456end

updateVehiclesInAttachRange

Description
Definition
updateVehiclesInAttachRange()
Code
4135function AttacherJoints.updateVehiclesInAttachRange(vehicle, maxDistanceSq, maxAngle, fullUpdate)
4136 local spec = vehicle.spec_attacherJoints
4137
4138 if spec ~= nil then
4139 local attachableInfo = spec.attachableInfo
4140 local pendingInfo = spec.pendingAttachableInfo
4141
4142 -- first, check if attached implements can attach something
4143 if vehicle.getAttachedImplements ~= nil then
4144 local implements = vehicle:getAttachedImplements()
4145 for _,implement in pairs(implements) do
4146 if implement.object ~= nil then
4147 local attacherVehicle, attacherVehicleJointDescIndex, attachable, attachableJointDescIndex, warning = AttacherJoints.updateVehiclesInAttachRange(implement.object, maxDistanceSq, maxAngle, fullUpdate)
4148 if attacherVehicle ~= nil then
4149 attachableInfo.attacherVehicle, attachableInfo.attacherVehicleJointDescIndex, attachableInfo.attachable, attachableInfo.attachableJointDescIndex, attachableInfo.warning = attacherVehicle, attacherVehicleJointDescIndex, attachable, attachableJointDescIndex, warning
4150 return attacherVehicle, attacherVehicleJointDescIndex, attachable, attachableJointDescIndex, warning
4151 end
4152 end
4153 end
4154 end
4155
4156 local numJoints = #g_currentMission.inputAttacherJoints
4157 local minUpdateJoints = math.max(math.floor(numJoints / 5), 1) -- update each joint at least every 5 frames
4158 local firstJoint = spec.lastInputAttacherCheckIndex % numJoints + 1
4159 local lastJoint = math.min(firstJoint + minUpdateJoints, numJoints)
4160
4161 if fullUpdate then
4162 firstJoint = 1
4163 lastJoint = numJoints
4164 end
4165
4166 spec.lastInputAttacherCheckIndex = lastJoint % numJoints
4167
4168 for attacherJointIndex=1, #spec.attacherJoints do
4169 local attacherJoint = spec.attacherJoints[attacherJointIndex]
4170
4171 if attacherJoint.jointIndex == 0 then
4172 if vehicle:getIsAttachingAllowed(attacherJoint) then
4173 local x, y, z = getWorldTranslation(attacherJoint.jointTransform)
4174
4175 for i=firstJoint, lastJoint do
4176 local jointInfo = g_currentMission.inputAttacherJoints[i]
4177
4178 if jointInfo.jointType == attacherJoint.jointType then
4179 if jointInfo.vehicle:getIsInputAttacherActive(jointInfo.inputAttacherJoint) then
4180 local distSq = MathUtil.vector2LengthSq(x-jointInfo.translation[1], z-jointInfo.translation[3])
4181 if distSq < maxDistanceSq and distSq < pendingInfo.minDistance then
4182 local distY = y-jointInfo.translation[2]
4183 local distSqY = distY*distY
4184
4185 if distSqY < maxDistanceSq*4 and distSqY < pendingInfo.minDistanceY then
4186 if jointInfo.vehicle:getActiveInputAttacherJointDescIndex() == nil or jointInfo.vehicle:getAllowMultipleAttachments() then
4187 local compatibility, notAllowedWarning = getAttacherJointCompatibility(vehicle, attacherJoint, jointInfo.vehicle, jointInfo.inputAttacherJoint)
4188 if compatibility then
4189 local angleInRange
4190 local attachAngleLimitAxis = jointInfo.inputAttacherJoint.attachAngleLimitAxis
4191 if attachAngleLimitAxis == 1 then
4192 local dx, _, _ = localDirectionToLocal(jointInfo.node, attacherJoint.jointTransform, 1, 0, 0)
4193 angleInRange = dx > maxAngle
4194 elseif attachAngleLimitAxis == 2 then
4195 local _, dy, _ = localDirectionToLocal(jointInfo.node, attacherJoint.jointTransform, 0, 1, 0)
4196 angleInRange = dy > maxAngle
4197 else
4198 local _, _, dz = localDirectionToLocal(jointInfo.node, attacherJoint.jointTransform, 0, 0, 1)
4199 angleInRange = dz > maxAngle
4200 end
4201
4202 if angleInRange then
4203 pendingInfo.minDistance = distSq
4204 pendingInfo.minDistanceY = distSqY
4205 pendingInfo.attacherVehicle = vehicle
4206 pendingInfo.attacherVehicleJointDescIndex = attacherJointIndex
4207 pendingInfo.attachable = jointInfo.vehicle
4208 pendingInfo.attachableJointDescIndex = jointInfo.jointIndex
4209 end
4210 else
4211 pendingInfo.warning = pendingInfo.warning or notAllowedWarning
4212 end
4213 end
4214 end
4215 end
4216 end
4217 end
4218 end
4219 end
4220 end
4221 end
4222
4223 if spec.lastInputAttacherCheckIndex == 0 then
4224 attachableInfo.attacherVehicle = pendingInfo.attacherVehicle
4225 attachableInfo.attacherVehicleJointDescIndex = pendingInfo.attacherVehicleJointDescIndex
4226 attachableInfo.attachable = pendingInfo.attachable
4227 attachableInfo.attachableJointDescIndex = pendingInfo.attachableJointDescIndex
4228 attachableInfo.warning = pendingInfo.warning
4229
4230 pendingInfo.minDistance = math.huge
4231 pendingInfo.minDistanceY = math.huge
4232 pendingInfo.attacherVehicle = nil
4233 pendingInfo.attacherVehicleJointDescIndex = nil
4234 pendingInfo.attachable = nil
4235 pendingInfo.attachableJointDescIndex = nil
4236 pendingInfo.warning = nil
4237 end
4238
4239 return attachableInfo.attacherVehicle, attachableInfo.attacherVehicleJointDescIndex, attachableInfo.attachable, attachableInfo.attachableJointDescIndex, attachableInfo.warning
4240 end
4241
4242 return nil, nil, nil, nil
4243end