LUADOC - Farming Simulator 19

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
2910function AttacherJoints.actionEventAttach(self, actionName, inputValue, callbackState, isAnalog)
2911 -- attach or detach something
2912 local info = self.spec_attacherJoints.attachableInfo
2913 if info.attachable ~= nil and g_currentMission.accessHandler:canFarmAccess(self:getActiveFarm(), info.attachable) then
2914 -- attach
2915 if info.attacherVehicle.spec_attacherJoints.attacherJoints[info.attacherVehicleJointDescIndex].jointIndex == 0 then
2916 info.attacherVehicle:attachImplement(info.attachable, info.attachableJointDescIndex, info.attacherVehicleJointDescIndex)
2917 end
2918 else
2919 -- detach
2920 local object = self:getSelectedVehicle()
2921 if object ~= nil and object ~= self and object.isDetachAllowed ~= nil then
2922 local detachAllowed, warning, showWarning = object:isDetachAllowed()
2923 if detachAllowed then
2924 if object.getAttacherVehicle ~= nil then
2925 local attacherVehicle = object:getAttacherVehicle()
2926 if attacherVehicle ~= nil then
2927 attacherVehicle:detachImplementByObject(object)
2928 end
2929 end
2930 elseif showWarning == nil or showWarning then
2931 g_currentMission:showBlinkingWarning(warning or g_i18n:getText("warning_detachNotAllowed"), 2000)
2932 end
2933 end
2934 end
2935end

actionEventLowerAllImplements

Description
Definition
actionEventLowerAllImplements()
Code
2948function AttacherJoints.actionEventLowerAllImplements(self, actionName, inputValue, callbackState, isAnalog)
2949 self:startAttacherJointCombo(true)
2950
2951 self:getRootVehicle():raiseStateChange(Vehicle.STATE_CHANGE_LOWER_ALL_IMPLEMENTS)
2952end

actionEventLowerImplement

Description
Definition
actionEventLowerImplement()
Code
2939function AttacherJoints.actionEventLowerImplement(self, actionName, inputValue, callbackState, isAnalog)
2940 -- self is the implement object to lower, so we call the function on the attacher vehicle
2941 if self.getAttacherVehicle ~= nil then
2942 self:getAttacherVehicle():handleLowerImplementEvent()
2943 end
2944end

activateAttachments

Description
Call "activate" on all attachments
Definition
activateAttachments()
Code
1822function AttacherJoints:activateAttachments()
1823 local spec = self.spec_attacherJoints
1824
1825 for _,v in pairs(spec.attachedImplements) do
1826 if v.object ~= nil then
1827 v.object:activate()
1828 end
1829 end
1830end

addToPhysics

Description
Add to physics
Definition
addToPhysics()
Return Values
booleansuccesssuccess
Code
2340function AttacherJoints:addToPhysics(superFunc)
2341 local spec = self.spec_attacherJoints
2342
2343 if not superFunc(self) then
2344 return false
2345 end
2346
2347 for _, implement in pairs(spec.attachedImplements) do
2348 if not implement.object.isHardAttached then
2349 self:createAttachmentJoint(implement)
2350 end
2351 end
2352
2353 return true
2354end

addToTotalVehicleList

Description
Definition
addToTotalVehicleList()
Code
2416function AttacherJoints:addToTotalVehicleList(superFunc, list)
2417 superFunc(self, list)
2418
2419 for _, implement in pairs(self:getAttachedImplements()) do
2420 implement.object:addToTotalVehicleList(list)
2421 end
2422end

addVehicleToAIImplementList

Description
Definition
addVehicleToAIImplementList()
Code
2406function AttacherJoints:addVehicleToAIImplementList(superFunc, list)
2407 superFunc(self, list)
2408
2409 for _, implement in pairs(self:getAttachedImplements()) do
2410 implement.object:addVehicleToAIImplementList(list)
2411 end
2412end

attachableAddToolCameras

Description
Definition
attachableAddToolCameras()
Code
2475function AttacherJoints:attachableAddToolCameras(superFunc)
2476 local spec = self.spec_attacherJoints
2477 superFunc(self)
2478
2479 for _,implement in pairs(spec.attachedImplements) do
2480 if implement.object ~= nil then
2481 implement.object:attachableAddToolCameras()
2482 end
2483 end
2484end

attachableRemoveToolCameras

Description
Definition
attachableRemoveToolCameras()
Code
2488function AttacherJoints:attachableRemoveToolCameras(superFunc)
2489 local spec = self.spec_attacherJoints
2490 superFunc(self)
2491
2492 for _,implement in pairs(spec.attachedImplements) do
2493 if implement.object ~= nil then
2494 implement.object:attachableRemoveToolCameras()
2495 end
2496 end
2497end

calculateAttacherJointMoveUpperLowerAlpha

Description
Calculate move upper and lower alpha of attacher joint
Definition
calculateAttacherJointMoveUpperLowerAlpha(table jointDesc, table object)
Arguments
tablejointDescjoint desc of used attacher
tableobjectobject of attached vehicle
Code
966function AttacherJoints:calculateAttacherJointMoveUpperLowerAlpha(jointDesc, object)
967 local objectAtttacherJoint = object.spec_attachable.attacherJoint
968
969 if jointDesc.allowsLowering then
970
971 local lowerDistanceToGround = jointDesc.lowerDistanceToGround
972 local upperDistanceToGround = jointDesc.upperDistanceToGround
973
974 if objectAtttacherJoint.heightNode ~= nil and jointDesc.rotationNode ~= nil then
975 self:updateAttacherJointRotationNodes(jointDesc, 1)
976
977 setRotation(jointDesc.jointTransform, unpack(jointDesc.jointOrigRot))
978 local x, y, z = localToLocal(jointDesc.jointTransform, jointDesc.rootNode, 0, 0, 0)
979 local delta = jointDesc.lowerDistanceToGround - y
980 local hx, hy, hz = localToLocal(jointDesc.jointTransform, jointDesc.rootNode, objectAtttacherJoint.heightNodeOffset[1], objectAtttacherJoint.heightNodeOffset[2], objectAtttacherJoint.heightNodeOffset[3])
981 lowerDistanceToGround = hy + delta
982
983 self:updateAttacherJointRotationNodes(jointDesc, 0)
984 x, y, z = localToLocal(jointDesc.jointTransform, jointDesc.rootNode, 0, 0, 0)
985 delta = jointDesc.upperDistanceToGround - y
986 hx, hy, hz = localToLocal(jointDesc.jointTransform, jointDesc.rootNode, objectAtttacherJoint.heightNodeOffset[1], objectAtttacherJoint.heightNodeOffset[2], objectAtttacherJoint.heightNodeOffset[3])
987 upperDistanceToGround = hy + delta
988 end
989
990 local upperAlpha = MathUtil.clamp((objectAtttacherJoint.upperDistanceToGround - upperDistanceToGround) / (lowerDistanceToGround - upperDistanceToGround), 0, 1)
991 local lowerAlpha = MathUtil.clamp((objectAtttacherJoint.lowerDistanceToGround - upperDistanceToGround) / (lowerDistanceToGround - upperDistanceToGround), 0, 1)
992
993 if objectAtttacherJoint.allowsLowering and jointDesc.allowsLowering then
994 return upperAlpha, lowerAlpha
995 else
996 if objectAtttacherJoint.isDefaultLowered then
997 return lowerAlpha,lowerAlpha
998 else
999 return upperAlpha,upperAlpha
1000 end
1001 end
1002 end
1003
1004 if objectAtttacherJoint.isDefaultLowered then
1005 return 1,1
1006 else
1007 return 0,0
1008 end
1009end

callFunctionOnAllImplements

Description
Definition
callFunctionOnAllImplements()
Code
1809function AttacherJoints:callFunctionOnAllImplements(functionName, ...)
1810 for _, implement in pairs(self:getAttachedImplements()) do
1811 local vehicle = implement.object
1812 if vehicle ~= nil then
1813 if vehicle[functionName] ~= nil then
1814 vehicle[functionName](vehicle, ...)
1815 end
1816 end
1817 end
1818end

createAttachmentJoint

Description
Create attacher joint between vehicle and implement
Definition
createAttachmentJoint(table implement, boolean noSmoothAttach)
Arguments
tableimplementimplement to attach
booleannoSmoothAttachdont use smooth attach
Code
1218function AttacherJoints:createAttachmentJoint(implement, noSmoothAttach)
1219
1220 local spec = self.spec_attacherJoints
1221 local jointDesc = spec.attacherJoints[implement.jointDescIndex]
1222 local objectAtttacherJoint = implement.object.spec_attachable.attacherJoint
1223
1224 if self.isServer and objectAtttacherJoint ~= nil then
1225 if getRigidBodyType(jointDesc.rootNode) ~= "Dynamic" or getRigidBodyType(objectAtttacherJoint.rootNode) ~= "Dynamic" then
1226 return
1227 end
1228
1229 local xNew = jointDesc.jointOrigTrans[1] + jointDesc.jointPositionOffset[1]
1230 local yNew = jointDesc.jointOrigTrans[2] + jointDesc.jointPositionOffset[2]
1231 local zNew = jointDesc.jointOrigTrans[3] + jointDesc.jointPositionOffset[3]
1232
1233 -- transform offset position to world coord and to jointTransform coord to get position offset dependend on angle and position
1234 local x,y,z = localToWorld(getParent(jointDesc.jointTransform), xNew, yNew, zNew)
1235 local x1,y1,z1 = worldToLocal(jointDesc.jointTransform, x,y,z)
1236
1237 -- move jointTransform to offset pos
1238 setTranslation(jointDesc.jointTransform, xNew, yNew, zNew)
1239
1240 -- transform it to implement position and angle
1241 x,y,z = localToWorld(objectAtttacherJoint.node,x1,y1,z1)
1242 local x2,y2,z2 = worldToLocal(getParent(objectAtttacherJoint.node), x,y,z)
1243 setTranslation(objectAtttacherJoint.node, x2,y2, z2)
1244
1245
1246 local constr = JointConstructor:new()
1247 constr:setActors(jointDesc.rootNode, objectAtttacherJoint.rootNode)
1248 constr:setJointTransforms(jointDesc.jointTransform, objectAtttacherJoint.node)
1249 --constr:setBreakable(20, 10)
1250
1251 implement.jointRotLimit = {}
1252 implement.jointTransLimit = {}
1253
1254 implement.lowerRotLimit = {}
1255 implement.lowerTransLimit = {}
1256
1257 implement.upperRotLimit = {}
1258 implement.upperTransLimit = {}
1259
1260 if noSmoothAttach == nil or not noSmoothAttach then
1261 local dx,dy,dz = localToLocal(objectAtttacherJoint.node, jointDesc.jointTransform, 0,0,0)
1262 local _,y,z = localDirectionToLocal(objectAtttacherJoint.node, jointDesc.jointTransform, 0,1,0)
1263 local rX = math.atan2(z,y)
1264 local x,_,z = localDirectionToLocal(objectAtttacherJoint.node, jointDesc.jointTransform, 0,0,1)
1265 local rY = math.atan2(x,z)
1266 local x,y,_ = localDirectionToLocal(objectAtttacherJoint.node, jointDesc.jointTransform, 1,0,0)
1267 local rZ = math.atan2(y,x)
1268 implement.attachingTransLimit = { math.abs(dx), math.abs(dy), math.abs(dz) }
1269 implement.attachingRotLimit = { math.abs(rX), math.abs(rY), math.abs(rZ) }
1270 implement.attachingTransLimitSpeed = {}
1271 implement.attachingRotLimitSpeed = {}
1272 for i=1,3 do
1273 implement.attachingTransLimitSpeed[i] = implement.attachingTransLimit[i] / 500
1274 implement.attachingRotLimitSpeed[i] = implement.attachingRotLimit[i] / 500
1275 end
1276 implement.attachingIsInProgress = true
1277 else
1278 implement.attachingTransLimit = { 0,0,0 }
1279 implement.attachingRotLimit = { 0,0,0 }
1280 end
1281
1282 for i=1, 3 do
1283 local lowerRotLimit = jointDesc.lowerRotLimit[i]*objectAtttacherJoint.lowerRotLimitScale[i]
1284 local upperRotLimit = jointDesc.upperRotLimit[i]*objectAtttacherJoint.upperRotLimitScale[i]
1285 if objectAtttacherJoint.fixedRotation then
1286 lowerRotLimit = 0
1287 upperRotLimit = 0
1288 end
1289
1290 local upperTransLimit = jointDesc.lowerTransLimit[i]*objectAtttacherJoint.lowerTransLimitScale[i]
1291 local lowerTransLimit = jointDesc.upperTransLimit[i]*objectAtttacherJoint.upperTransLimitScale[i]
1292 implement.lowerRotLimit[i] = lowerRotLimit
1293 implement.upperRotLimit[i] = upperRotLimit
1294
1295 implement.lowerTransLimit[i] = upperTransLimit
1296 implement.upperTransLimit[i] = lowerTransLimit
1297
1298 if not jointDesc.allowsLowering then
1299 implement.upperRotLimit[i] = lowerRotLimit
1300 implement.upperTransLimit[i] = upperTransLimit
1301 end
1302
1303 local rotLimit = lowerRotLimit
1304 local transLimit = upperTransLimit
1305 if jointDesc.allowsLowering and jointDesc.allowsJointLimitMovement then
1306 if objectAtttacherJoint.allowsJointRotLimitMovement then
1307 rotLimit = MathUtil.lerp(upperRotLimit, lowerRotLimit, jointDesc.moveAlpha)
1308 end
1309 if objectAtttacherJoint.allowsJointTransLimitMovement then
1310 transLimit = MathUtil.lerp(lowerTransLimit, upperTransLimit, jointDesc.moveAlpha)
1311 end
1312 end
1313
1314 local limitRot = rotLimit
1315 local limitTrans = transLimit
1316 if noSmoothAttach == nil or not noSmoothAttach then
1317 limitRot = math.max(rotLimit, implement.attachingRotLimit[i])
1318 limitTrans = math.max(transLimit, implement.attachingTransLimit[i])
1319 end
1320
1321 constr:setRotationLimit(i-1, -limitRot, limitRot)
1322 implement.jointRotLimit[i] = limitRot
1323 constr:setTranslationLimit(i-1, true, -limitTrans, limitTrans)
1324 implement.jointTransLimit[i] = limitTrans
1325 end
1326
1327 if jointDesc.enableCollision then
1328 constr:setEnableCollision(true)
1329 else
1330 for _, component in pairs(self.components) do
1331 if component.node ~= jointDesc.rootNodeBackup and not component.collideWithAttachables then
1332 setPairCollision(component.node, objectAtttacherJoint.rootNode, false)
1333 end
1334 end
1335 end
1336
1337 local springX = math.max(jointDesc.rotLimitSpring[1], objectAtttacherJoint.rotLimitSpring[1])
1338 local springY = math.max(jointDesc.rotLimitSpring[2], objectAtttacherJoint.rotLimitSpring[2])
1339 local springZ = math.max(jointDesc.rotLimitSpring[3], objectAtttacherJoint.rotLimitSpring[3])
1340 local dampingX = math.max(jointDesc.rotLimitDamping[1], objectAtttacherJoint.rotLimitDamping[1])
1341 local dampingY = math.max(jointDesc.rotLimitDamping[2], objectAtttacherJoint.rotLimitDamping[2])
1342 local dampingZ = math.max(jointDesc.rotLimitDamping[3], objectAtttacherJoint.rotLimitDamping[3])
1343 local forceLimitX = Utils.getMaxJointForceLimit(jointDesc.rotLimitForceLimit[1], objectAtttacherJoint.rotLimitForceLimit[1])
1344 local forceLimitY = Utils.getMaxJointForceLimit(jointDesc.rotLimitForceLimit[2], objectAtttacherJoint.rotLimitForceLimit[2])
1345 local forceLimitZ = Utils.getMaxJointForceLimit(jointDesc.rotLimitForceLimit[3], objectAtttacherJoint.rotLimitForceLimit[3])
1346 constr:setRotationLimitSpring(springX, dampingX, springY, dampingY, springZ, dampingZ)
1347 constr:setRotationLimitForceLimit(forceLimitX, forceLimitY, forceLimitZ)
1348
1349 local springX = math.max(jointDesc.transLimitSpring[1], objectAtttacherJoint.transLimitSpring[1])
1350 local springY = math.max(jointDesc.transLimitSpring[2], objectAtttacherJoint.transLimitSpring[2])
1351 local springZ = math.max(jointDesc.transLimitSpring[3], objectAtttacherJoint.transLimitSpring[3])
1352 local dampingX = math.max(jointDesc.transLimitDamping[1], objectAtttacherJoint.transLimitDamping[1])
1353 local dampingY = math.max(jointDesc.transLimitDamping[2], objectAtttacherJoint.transLimitDamping[2])
1354 local dampingZ = math.max(jointDesc.transLimitDamping[3], objectAtttacherJoint.transLimitDamping[3])
1355 local forceLimitX = Utils.getMaxJointForceLimit(jointDesc.transLimitForceLimit[1], objectAtttacherJoint.transLimitForceLimit[1])
1356 local forceLimitY = Utils.getMaxJointForceLimit(jointDesc.transLimitForceLimit[2], objectAtttacherJoint.transLimitForceLimit[2])
1357 local forceLimitZ = Utils.getMaxJointForceLimit(jointDesc.transLimitForceLimit[3], objectAtttacherJoint.transLimitForceLimit[3])
1358 constr:setTranslationLimitSpring(springX, dampingX, springY, dampingY, springZ, dampingZ)
1359 constr:setTranslationLimitForceLimit(forceLimitX, forceLimitY, forceLimitZ)
1360
1361 jointDesc.jointIndex = constr:finalize()
1362
1363 -- restore implement attacher joint position (to ensure correct bottom arm alignment)
1364 setTranslation(objectAtttacherJoint.node, unpack(objectAtttacherJoint.jointOrigTrans))
1365 else
1366 -- set joint index to '1' on client side, so we can check if something is attached
1367 jointDesc.jointIndex = 1
1368 end
1369end

deactivateAttachments

Description
Call "deactivate" on all attachments
Definition
deactivateAttachments()
Code
1834function AttacherJoints:deactivateAttachments()
1835 local spec = self.spec_attacherJoints
1836
1837 for _,v in pairs(spec.attachedImplements) do
1838 if v.object ~= nil then
1839 v.object:deactivate()
1840 end
1841 end
1842end

deactivateAttachmentsLights

Description
Call "deactivateLights" on all attachments
Definition
deactivateAttachmentsLights()
Code
1846function AttacherJoints:deactivateAttachmentsLights()
1847 local spec = self.spec_attacherJoints
1848
1849 for _,v in pairs(spec.attachedImplements) do
1850 if v.object ~= nil and v.object.deactivateLights ~= nil then
1851 v.object:deactivateLights()
1852 end
1853 end
1854end

detachImplement

Description
Detach implement
Definition
detachImplement(integer implementIndex, boolean noEventSend)
Arguments
integerimplementIndexindex of implement in self.attachedImplements
booleannoEventSendno event send
Return Values
booleansuccesssuccess
Code
1514function AttacherJoints:detachImplement(implementIndex, noEventSend)
1515 local spec = self.spec_attacherJoints
1516
1517 if noEventSend == nil or noEventSend == false then
1518 if g_server ~= nil then
1519 g_server:broadcastEvent(VehicleDetachEvent:new(self, spec.attachedImplements[implementIndex].object), nil, nil, self)
1520 else
1521 -- Send detach request to server and return
1522 local implement = spec.attachedImplements[implementIndex]
1523 if implement.object ~= nil then
1524 g_client:getServerConnection():sendEvent(VehicleDetachEvent:new(self, implement.object))
1525 end
1526 return
1527 end
1528 end
1529
1530 local implement = spec.attachedImplements[implementIndex]
1531
1532 SpecializationUtil.raiseEvent(self, "onPreDetachImplement", implement)
1533 implement.object:preDetach(self, implement)
1534
1535 local jointDesc
1536 if implement.object ~= nil then
1537 jointDesc = spec.attacherJoints[implement.jointDescIndex]
1538 if jointDesc.transNode ~= nil then
1539 setTranslation(jointDesc.transNode, unpack(jointDesc.transNodeOrgTrans))
1540 end
1541 if not implement.object.isHardAttached then
1542 if self.isServer then
1543 if jointDesc.jointIndex ~= 0 then
1544 removeJoint(jointDesc.jointIndex)
1545 end
1546
1547 if not jointDesc.enableCollision then
1548 for _, component in pairs(self.components) do
1549 if component.node ~= jointDesc.rootNodeBackup and not component.collideWithAttachables then
1550 local attacherJoint = implement.object:getActiveInputAttacherJoint()
1551 setPairCollision(component.node, attacherJoint.rootNode, true)
1552 end
1553 end
1554 end
1555 end
1556 end
1557 jointDesc.jointIndex = 0
1558 end
1559
1560 ObjectChangeUtil.setObjectChanges(jointDesc.changeObjects, false, self, self.setMovingToolDirty)
1561
1562 if implement.object ~= nil then
1563 local object = implement.object
1564
1565 if object.isHardAttached then
1566 self:hardDetachImplement(implement)
1567 end
1568
1569 if self.isClient then
1570 if jointDesc.topArm ~= nil then
1571 setRotation(jointDesc.topArm.rotationNode, jointDesc.topArm.rotX, jointDesc.topArm.rotY, jointDesc.topArm.rotZ)
1572 if jointDesc.topArm.translationNode ~= nil then
1573 setTranslation(jointDesc.topArm.translationNode, 0, 0, 0)
1574 end
1575 if jointDesc.topArm.scaleNode ~= nil then
1576 setScale(jointDesc.topArm.scaleNode, 1, 1, 1)
1577 end
1578 if jointDesc.topArm.toggleVisibility then
1579 setVisibility(jointDesc.topArm.rotationNode, false)
1580 end
1581 end
1582 if jointDesc.bottomArm ~= nil then
1583 setRotation(jointDesc.bottomArm.rotationNode, jointDesc.bottomArm.rotX, jointDesc.bottomArm.rotY, jointDesc.bottomArm.rotZ)
1584 if jointDesc.bottomArm.translationNode ~= nil then
1585 setTranslation(jointDesc.bottomArm.translationNode, 0, 0, 0)
1586 end
1587 if self.setMovingToolDirty ~= nil then
1588 self:setMovingToolDirty(jointDesc.bottomArm.rotationNode)
1589 end
1590 if jointDesc.bottomArm.toolbar ~= nil then
1591 setVisibility(jointDesc.bottomArm.toolbar, false)
1592 end
1593 if jointDesc.bottomArm.toggleVisibility then
1594 setVisibility(jointDesc.bottomArm.rotationNode, false)
1595 end
1596 end
1597 end
1598 -- restore original translation
1599 setTranslation(jointDesc.jointTransform, unpack(jointDesc.jointOrigTrans))
1600 local attacherJoint = object:getActiveInputAttacherJoint()
1601 setTranslation(attacherJoint.node, unpack(attacherJoint.jointOrigTrans))
1602 if jointDesc.rotationNode ~= nil then
1603 setRotation(jointDesc.rotationNode, jointDesc.rotX, jointDesc.rotY, jointDesc.rotZ)
1604 end
1605
1606 SpecializationUtil.raiseEvent(self, "onPostDetachImplement", implementIndex)
1607 object:postDetach(implementIndex)
1608 end
1609
1610 table.remove(spec.attachedImplements, implementIndex)
1611
1612 self:playDetachSound(jointDesc)
1613
1614 local data = {attacherVehicle=self, attachedVehicle=implement.object}
1615 implement.object:raiseStateChange(Vehicle.STATE_CHANGE_DETACH, data)
1616 local rootVehicle = self:getRootVehicle()
1617 rootVehicle:raiseStateChange(Vehicle.STATE_CHANGE_DETACH, data)
1618
1619 self:getRootVehicle():updateSelectableObjects()
1620 self:getRootVehicle():setSelectedVehicle(self, nil, true)
1621 self:getRootVehicle():requestActionEventUpdate() -- do action event update independent of a successful selection (important since we cannot select every vehicle)
1622 implement.object:updateSelectableObjects()
1623 implement.object:setSelectedVehicle(implement.object, nil, true)
1624 implement.object:requestActionEventUpdate() -- do action event update independent of a successful selection (important since we cannot select every vehicle)
1625
1626 return true
1627end

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
1634function AttacherJoints:detachImplementByObject(object, noEventSend)
1635 local spec = self.spec_attacherJoints
1636
1637 for i,implement in ipairs(spec.attachedImplements) do
1638 if implement.object == object then
1639 self:detachImplement(i, noEventSend)
1640 break
1641 end
1642 end
1643
1644 return true
1645end

detachingIsPossible

Description
Returns true if it is possible to detach selected implement
Definition
detachingIsPossible()
Return Values
booleanpossibleToDetachpossible to detach selected implement
Code
1729function AttacherJoints:detachingIsPossible()
1730 local implement = self:getImplementByObject(self:getSelectedVehicle())
1731 if implement ~= nil then
1732 local object = implement.object
1733 if object ~= nil and object.attacherVehicle ~= nil and object:isDetachAllowed() then
1734 local implementIndex = object.attacherVehicle:getImplementIndexByObject(object)
1735 if implementIndex ~= nil then
1736 return true
1737 end
1738 end
1739 end
1740 return false
1741end

findVehicleInAttachRange

Description
Definition
findVehicleInAttachRange()
Code
2838function AttacherJoints.findVehicleInAttachRange(vehicle, maxDistanceSq, maxAngle)
2839 local spec = vehicle.spec_attacherJoints
2840
2841 if spec ~= nil then
2842 local minDist = math.huge
2843 local minDistY = math.huge
2844 local attacherVehicle = nil
2845 local attacherVehicleJointDescIndex = nil
2846 local attachable = nil
2847 local attachableJointDescIndex = nil
2848
2849 -- first, check if attached implements can attach something
2850 if vehicle.getAttachedImplements ~= nil then
2851 local implements = vehicle:getAttachedImplements()
2852 for _,implement in pairs(implements) do
2853 if implement.object ~= nil then
2854 attacherVehicle, attacherVehicleJointDescIndex, attachable, attachableJointDescIndex = AttacherJoints.findVehicleInAttachRange(implement.object, maxDistanceSq, maxAngle)
2855 if attacherVehicle ~= nil then
2856 return attacherVehicle, attacherVehicleJointDescIndex, attachable, attachableJointDescIndex
2857 end
2858 end
2859 end
2860 end
2861
2862 -- now, check if the current vehicle can attach something
2863 for attacherJointIndex,attacherJoint in pairs(spec.attacherJoints) do
2864 if spec.attacherJoints[attacherJointIndex].jointIndex == 0 then
2865
2866 for _,vehicle2 in pairs(g_currentMission.vehicles) do
2867 if vehicle2 ~= vehicle and vehicle2.getInputAttacherJoints ~= nil then
2868
2869 if vehicle2:getActiveInputAttacherJointDescIndex() == nil then
2870
2871 local inputAttacherJoints = vehicle2:getInputAttacherJoints()
2872 if inputAttacherJoints ~= nil then
2873 for inputAttacherJointIndex,inputAttacherJoint in pairs(inputAttacherJoints) do
2874
2875 if vehicle2:getIsInputAttacherActive(inputAttacherJoint) and attacherJoint.jointType == inputAttacherJoint.jointType then
2876 local x,y,z = localToLocal(inputAttacherJoint.node, attacherJoint.jointTransform, 0,0,0)
2877 local distSq = MathUtil.vector2LengthSq(x,z)
2878 local distSqY = y*y
2879
2880 -- we check x-z-distance plus an extra check in y (doubled distance) to better handle height differences
2881 if distSq < maxDistanceSq and distSq < minDist and distSqY < maxDistanceSq*2 and distSqY < minDistY then
2882
2883 local dx,_,_ = localDirectionToLocal(inputAttacherJoint.node, attacherJoint.jointTransform, 1, 0, 0)
2884 if dx > maxAngle then
2885 minDist = distSq
2886 minDistY = distSqY
2887 attacherVehicle = vehicle
2888 attacherVehicleJointDescIndex = attacherJointIndex
2889 attachable = vehicle2
2890 attachableJointDescIndex = inputAttacherJointIndex
2891 end
2892
2893 end
2894 end
2895 end
2896 end
2897 end
2898 end
2899 end
2900 end
2901 end
2902 return attacherVehicle, attacherVehicleJointDescIndex, attachable, attachableJointDescIndex
2903 end
2904
2905 return nil, nil, nil, nil
2906end

getAICollisionTriggers

Description
Definition
getAICollisionTriggers()
Code
2442function AttacherJoints:getAICollisionTriggers(superFunc, collisionTriggers)
2443 local spec = self.spec_attacherJoints
2444
2445 for _, implement in pairs(spec.attachedImplements) do
2446 local object = implement.object
2447 if object.getAIImplementCollisionTriggers ~= nil then
2448 object:getAIImplementCollisionTriggers(collisionTriggers)
2449 end
2450
2451 if object.getAICollisionTriggers ~= nil then
2452 object:getAICollisionTriggers(collisionTriggers)
2453 end
2454 end
2455
2456 return superFunc(self)
2457end

getAirConsumerUsage

Description
Returns air consumer usage of attached vehicles
Definition
getAirConsumerUsage()
Return Values
floatusageair usage
Code
2390function AttacherJoints:getAirConsumerUsage(superFunc)
2391 local spec = self.spec_attacherJoints
2392 local usage = superFunc(self)
2393
2394 for _, implement in pairs(spec.attachedImplements) do
2395 local object = implement.object
2396 if object.getAttachbleAirConsumerUsage ~= nil then
2397 usage = usage + object:getAttachbleAirConsumerUsage()
2398 end
2399 end
2400
2401 return usage
2402end

getAttachedImplements

Description
Definition
getAttachedImplements()
Code
785function AttacherJoints:getAttachedImplements()
786 return self.spec_attacherJoints.attachedImplements
787end

getAttacherJointByJointDescIndex

Description
Definition
getAttacherJointByJointDescIndex()
Code
797function AttacherJoints:getAttacherJointByJointDescIndex(jointDescIndex)
798 return self.spec_attacherJoints.attacherJoints[jointDescIndex]
799end

getAttacherJointDescFromObject

Description
Definition
getAttacherJointDescFromObject()
Code
825function AttacherJoints:getAttacherJointDescFromObject(object)
826 local spec = self.spec_attacherJoints
827 for _,attachedImplement in pairs(spec.attachedImplements) do
828 if attachedImplement.object == object then
829 return spec.attacherJoints[attachedImplement.jointDescIndex]
830 end
831 end
832end

getAttacherJointIndexFromImplementIndex

Description
Definition
getAttacherJointIndexFromImplementIndex()
Code
836function AttacherJoints:getAttacherJointIndexFromImplementIndex(implementIndex)
837 local spec = self.spec_attacherJoints
838 local attachedImplement = spec.attachedImplements[implementIndex]
839 if attachedImplement ~= nil then
840 return attachedImplement.jointDescIndex
841 end
842 return nil
843end

getAttacherJointIndexFromObject

Description
Definition
getAttacherJointIndexFromObject()
Code
814function AttacherJoints:getAttacherJointIndexFromObject(object)
815 local spec = self.spec_attacherJoints
816 for _,attachedImplement in pairs(spec.attachedImplements) do
817 if attachedImplement.object == object then
818 return attachedImplement.jointDescIndex
819 end
820 end
821end

getAttacherJoints

Description
Definition
getAttacherJoints()
Code
791function AttacherJoints:getAttacherJoints()
792 return self.spec_attacherJoints.attacherJoints
793end

getCanToggleAttach

Description
Definition
getCanToggleAttach()
Code
1670function AttacherJoints:getCanToggleAttach()
1671 return true
1672end

getChildVehicles

Description
Inserts all child vehicles into the given table
Definition
getChildVehicles(table vehicles)
Arguments
tablevehicleschild vehicles table
Code
2377function AttacherJoints:getChildVehicles(superFunc, vehicles)
2378 local spec = self.spec_attacherJoints
2379
2380 for _, implement in pairs(spec.attachedImplements) do
2381 implement.object:getChildVehicles(vehicles)
2382 end
2383
2384 return superFunc(self, vehicles)
2385end

getDirectionSnapAngle

Description
Definition
getDirectionSnapAngle()
Code
2426function AttacherJoints:getDirectionSnapAngle(superFunc)
2427 local spec = self.spec_attacherJoints
2428 local maxAngle = superFunc(self)
2429
2430 for _, implement in pairs(spec.attachedImplements) do
2431 local object = implement.object
2432 if object.getDirectionSnapAngle ~= nil then
2433 maxAngle = math.max(maxAngle + object:getDirectionSnapAngle())
2434 end
2435 end
2436
2437 return maxAngle
2438end

getFillLevelInformation

Description
Definition
getFillLevelInformation()
Code
2461function AttacherJoints:getFillLevelInformation(superFunc, fillLevelInformations)
2462 local spec = self.spec_attacherJoints
2463
2464 superFunc(self, fillLevelInformations)
2465
2466 for _, implement in pairs(spec.attachedImplements) do
2467 if implement.object ~= nil then
2468 implement.object:getFillLevelInformation(fillLevelInformations)
2469 end
2470 end
2471end

getImplementByJointDescIndex

Description
Returns implement by jointDescIndex
Definition
getImplementByJointDescIndex(integer jointDescIndex)
Arguments
integerjointDescIndexjoint desc index
Return Values
tableimplementimplement
Code
1763function AttacherJoints:getImplementByJointDescIndex(jointDescIndex)
1764 local spec = self.spec_attacherJoints
1765
1766 for i, implement in pairs(spec.attachedImplements) do
1767 if implement.jointDescIndex == jointDescIndex then
1768 return implement
1769 end
1770 end
1771
1772 return nil
1773end

getImplementByObject

Description
Returns implement by object
Definition
getImplementByObject(table object)
Arguments
tableobjectobject of attached implement
Return Values
tableimplementimplement
Code
1795function AttacherJoints:getImplementByObject(object)
1796 local spec = self.spec_attacherJoints
1797
1798 for i, implement in pairs(spec.attachedImplements) do
1799 if implement.object == object then
1800 return implement
1801 end
1802 end
1803
1804 return nil
1805end

getImplementFromAttacherJointIndex

Description
Definition
getImplementFromAttacherJointIndex()
Code
803function AttacherJoints:getImplementFromAttacherJointIndex(attacherJointIndex)
804 local spec = self.spec_attacherJoints
805 for _,attachedImplement in pairs(spec.attachedImplements) do
806 if attachedImplement.jointDescIndex == attacherJointIndex then
807 return attachedImplement
808 end
809 end
810end

getImplementIndexByJointDescIndex

Description
Returns implement index in 'self.attachedImplements' by jointDescIndex
Definition
getImplementIndexByJointDescIndex(integer jointDescIndex)
Arguments
integerjointDescIndexjoint desc index
Return Values
integerindexindex of implement
Code
1747function AttacherJoints:getImplementIndexByJointDescIndex(jointDescIndex)
1748 local spec = self.spec_attacherJoints
1749
1750 for i, implement in pairs(spec.attachedImplements) do
1751 if implement.jointDescIndex == jointDescIndex then
1752 return i
1753 end
1754 end
1755
1756 return nil
1757end

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
1779function AttacherJoints:getImplementIndexByObject(object)
1780 local spec = self.spec_attacherJoints
1781
1782 for i, implement in pairs(spec.attachedImplements) do
1783 if implement.object == object then
1784 return i
1785 end
1786 end
1787
1788 return nil
1789end

getIsDashboardGroupActive

Description
Definition
getIsDashboardGroupActive()
Code
2543function AttacherJoints:getIsDashboardGroupActive(superFunc, group)
2544 local hasAttachment = #group.attacherJointIndices == 0
2545 for _, jointIndex in ipairs(group.attacherJointIndices) do
2546 if self:getImplementFromAttacherJointIndex(jointIndex) ~= nil then
2547 hasAttachment = true
2548 end
2549 end
2550
2551 return superFunc(self, group) and hasAttachment
2552end

getIsFoldAllowed

Description
Definition
getIsFoldAllowed()
Code
2583function AttacherJoints:getIsFoldAllowed(superFunc, direction, onAiTurnOn)
2584 local spec = self.spec_attacherJoints
2585 for attacherJointIndex, attacherJoint in ipairs(spec.attacherJoints) do
2586 if not attacherJoint.allowFoldingWhileAttached then
2587 if attacherJoint.jointIndex ~= 0 then
2588 return false
2589 end
2590 end
2591 end
2592
2593 return superFunc(self, direction, onAiTurnOn)
2594end

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
1884function AttacherJoints:getIsHardAttachAllowed(jointDescIndex)
1885 local spec = self.spec_attacherJoints
1886
1887 return spec.attacherJoints[jointDescIndex].supportsHardAttach
1888end

getIsReadyForAutomatedTrainTravel

Description
Definition
getIsReadyForAutomatedTrainTravel()
Code
2515function AttacherJoints:getIsReadyForAutomatedTrainTravel(superFunc)
2516 local spec = self.spec_attacherJoints
2517 for _,implement in pairs(spec.attachedImplements) do
2518 if implement.object ~= nil and implement.object.getIsReadyForAutomatedTrainTravel ~= nil then
2519 if not implement.object:getIsReadyForAutomatedTrainTravel() then
2520 return false
2521 end
2522 end
2523 end
2524
2525 return superFunc(self)
2526end

getIsWheelFoliageDestructionAllowed

Description
Returns true if foliage destruction is allowed
Definition
getIsWheelFoliageDestructionAllowed()
Return Values
booleanisAllowedtfoliage destruction is allowed
Code
2599function AttacherJoints:getIsWheelFoliageDestructionAllowed(superFunc, wheel)
2600 if not superFunc(self, wheel) then
2601 return false
2602 end
2603
2604 local spec = self.spec_attacherJoints
2605 for _,implement in pairs(spec.attachedImplements) do
2606 local object = implement.object
2607 if object ~= nil then
2608 if object.getBlockFoliageDestruction ~= nil then
2609 if object:getBlockFoliageDestruction() then
2610 return false
2611 end
2612 end
2613 end
2614 end
2615
2616 return true
2617end

getObjectFromImplementIndex

Description
Definition
getObjectFromImplementIndex()
Code
847function AttacherJoints:getObjectFromImplementIndex(implementIndex)
848 local spec = self.spec_attacherJoints
849 local attachedImplement = spec.attachedImplements[implementIndex]
850 if attachedImplement ~= nil then
851 return attachedImplement.object
852 end
853 return nil
854end

getSelectedImplement

Description
Definition
getSelectedImplement()
Code
1655function AttacherJoints:getSelectedImplement()
1656 local spec = self.spec_attacherJoints
1657
1658 -- check if implement is still attached
1659 if spec.selectedImplement ~= nil then
1660 if spec.selectedImplement.object:getAttacherVehicle() ~= self then
1661 return nil
1662 end
1663 end
1664
1665 return spec.selectedImplement
1666end

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
2360function AttacherJoints:getTotalMass(superFunc, onlyGivenVehicle)
2361 local spec = self.spec_attacherJoints
2362 local mass = superFunc(self)
2363
2364 if onlyGivenVehicle == nil or not onlyGivenVehicle then
2365 for _, implement in pairs(spec.attachedImplements) do
2366 local object = implement.object
2367 mass = mass + object:getTotalMass(onlyGivenVehicle)
2368 end
2369 end
2370
2371 return mass
2372end

handleLowerImplementByAttacherJointIndex

Description
Definition
handleLowerImplementByAttacherJointIndex()
Code
762function AttacherJoints:handleLowerImplementByAttacherJointIndex(attacherJointIndex, direction)
763 if attacherJointIndex ~= nil then
764 local implement = self:getImplementByJointDescIndex(attacherJointIndex)
765 if implement ~= nil then
766 local object = implement.object
767 local attacherJoints = self:getAttacherJoints()
768 local attacherJoint = attacherJoints[attacherJointIndex]
769
770 local allowsLowering, warning = object:getAllowsLowering()
771 if allowsLowering and attacherJoint.allowsLowering then
772 if direction == nil then
773 direction = not attacherJoint.moveDown
774 end
775 self:setJointMoveDown(implement.jointDescIndex, direction, false)
776 elseif not allowsLowering and warning ~= nil then
777 g_currentMission:showBlinkingWarning(warning, 2000)
778 end
779 end
780 end
781end

handleLowerImplementEvent

Description
Definition
handleLowerImplementEvent()
Code
744function AttacherJoints:handleLowerImplementEvent()
745 local implement = self:getImplementByObject(self:getSelectedVehicle())
746 if implement ~= nil then
747 local object = implement.object
748 if object ~= nil and object.getAttacherVehicle ~= nil then
749
750 local attacherVehicle = object:getAttacherVehicle()
751 if attacherVehicle ~= nil then
752
753 local attacherJointIndex = attacherVehicle:getAttacherJointIndexFromObject(object)
754 attacherVehicle:handleLowerImplementByAttacherJointIndex(attacherJointIndex)
755 end
756 end
757 end
758end

hardAttachImplement

Description
Hard attach implement
Definition
hardAttachImplement(table implement)
Arguments
tableimplementimplement to attach
Code
1374function AttacherJoints:hardAttachImplement(implement)
1375 local spec = self.spec_attacherJoints
1376
1377 local implements = {}
1378 local attachedImplements
1379 if implement.object.getAttachedImplements ~= nil then
1380 attachedImplements = implement.object:getAttachedImplements()
1381 end
1382 if attachedImplements ~= nil then
1383 for i = table.getn(attachedImplements), 1, -1 do
1384 local impl = attachedImplements[i]
1385 local object = impl.object
1386 local jointDescIndex = impl.jointDescIndex
1387 local jointDesc = implement.object.spec_attacherJoints.attacherJoints[jointDescIndex]
1388 local inputJointDescIndex = object.spec_attachable.inputAttacherJointDescIndex
1389 local moveDown = jointDesc.moveDown
1390 table.insert(implements, 1, {object=object, implementIndex=i, jointDescIndex=jointDescIndex, inputJointDescIndex=inputJointDescIndex, moveDown=moveDown})
1391 implement.object:detachImplement(1, true)
1392 end
1393 end
1394
1395 local attacherJoint = spec.attacherJoints[implement.jointDescIndex]
1396 local implementJoint = implement.object.spec_attachable.attacherJoint
1397
1398 local baseVehicleComponentNode = self:getParentComponent(attacherJoint.jointTransform)
1399 local attachedVehicleComponentNode = implement.object:getParentComponent(implement.object.spec_attachable.attacherJoint.node)
1400
1401 -- remove all components from physics
1402 local currentVehicle = self
1403 while currentVehicle ~= nil do
1404 currentVehicle:removeFromPhysics()
1405 currentVehicle = currentVehicle.attacherVehicle
1406 end
1407 implement.object:removeFromPhysics()
1408
1409 -- set valid baseVehicle compound
1410 if spec.attacherVehicle == nil then
1411 setIsCompound(baseVehicleComponentNode, true)
1412 end
1413 -- set attachedVehicle to compound child
1414 setIsCompoundChild(attachedVehicleComponentNode, true)
1415
1416 -- set direction and local position
1417 local dirX, dirY, dirZ = localDirectionToLocal(attachedVehicleComponentNode, implementJoint.node, 0, 0, 1)
1418 local upX, upY, upZ = localDirectionToLocal(attachedVehicleComponentNode, implementJoint.node, 0, 1, 0)
1419 setDirection(attachedVehicleComponentNode, dirX, dirY, dirZ, upX, upY, upZ)
1420 local x,y,z = localToLocal(attachedVehicleComponentNode, implementJoint.node, 0, 0, 0)
1421 setTranslation(attachedVehicleComponentNode, x, y, z)
1422 link(attacherJoint.jointTransform, attachedVehicleComponentNode)
1423
1424 -- link visual and set to correct position
1425 if implementJoint.visualNode ~= nil and attacherJoint.jointTransformVisual ~= nil then
1426 local dirX, dirY, dirZ = localDirectionToLocal(implementJoint.visualNode, implementJoint.node, 0, 0, 1)
1427 local upX, upY, upZ = localDirectionToLocal(implementJoint.visualNode, implementJoint.node, 0, 1, 0)
1428 setDirection(implementJoint.visualNode, dirX, dirY, dirZ, upX, upY, upZ)
1429 local x,y,z = localToLocal(implementJoint.visualNode, implementJoint.node, 0, 0, 0)
1430 setTranslation(implementJoint.visualNode, x, y, z)
1431 link(attacherJoint.jointTransformVisual, implementJoint.visualNode)
1432 end
1433
1434 implement.object.isHardAttached = true
1435
1436 -- add to physics again
1437 local currentVehicle = self
1438 while currentVehicle ~= nil do
1439 currentVehicle:addToPhysics()
1440 currentVehicle = currentVehicle.attacherVehicle
1441 end
1442
1443 -- set new joint rootNodes
1444 for _, attacherJoint in pairs(implement.object.spec_attacherJoints.attacherJoints) do
1445 attacherJoint.rootNode = self.rootNode
1446 end
1447
1448 for _, impl in pairs(implements) do
1449 implement.object:attachImplement(impl.object, impl.inputJointDescIndex, impl.jointDescIndex, true, impl.implementIndex, impl.moveDown, true)
1450 end
1451
1452 if self.isServer then
1453 self:raiseDirtyFlags(self.vehicleDirtyFlag)
1454 end
1455
1456 return true
1457end

hardDetachImplement

Description
Hard detach implement
Definition
hardDetachImplement(table implement)
Arguments
tableimplementimplement to detach
Code
1462function AttacherJoints:hardDetachImplement(implement)
1463 -- restore original joint rootNode
1464 for _, attacherJoint in pairs(implement.object.spec_attacherJoints.attacherJoints) do
1465 attacherJoint.rootNode = attacherJoint.rootNodeBackup
1466 end
1467
1468 local implementJoint = implement.object.spec_attachable.attacherJoint
1469
1470 local attachedVehicleComponentNode = implement.object:getParentComponent(implementJoint.node)
1471
1472 local currentVehicle = self
1473 while currentVehicle ~= nil do
1474 currentVehicle:removeFromPhysics()
1475 currentVehicle = currentVehicle.attacherVehicle
1476 end
1477 --implement.object:removeFromPhysics()
1478
1479 setIsCompound(attachedVehicleComponentNode, true)
1480
1481 local x,y,z = getWorldTranslation(attachedVehicleComponentNode)
1482 setTranslation(attachedVehicleComponentNode, x,y,z)
1483 local dirX, dirY, dirZ = localDirectionToWorld(implement.object.rootNode, 0, 0, 1)
1484 local upX, upY, upZ = localDirectionToWorld(implement.object.rootNode, 0, 1, 0)
1485 setDirection(attachedVehicleComponentNode, dirX, dirY, dirZ, upX, upY, upZ)
1486 link(getRootNode(), attachedVehicleComponentNode)
1487
1488 if implementJoint.visualNode ~= nil and getParent(implementJoint.visualNode) ~= implementJoint.visualNodeData.parent then
1489 link(implementJoint.visualNodeData.parent, implementJoint.visualNode, implementJoint.visualNodeData.index)
1490 setRotation(implementJoint.visualNode, implementJoint.visualNodeData.rotation[1], implementJoint.visualNodeData.rotation[2], implementJoint.visualNodeData.rotation[3])
1491 setTranslation(implementJoint.visualNode, implementJoint.visualNodeData.translation[1], implementJoint.visualNodeData.translation[2], implementJoint.visualNodeData.translation[3])
1492 end
1493
1494 local currentVehicle = self
1495 while currentVehicle ~= nil do
1496 currentVehicle:addToPhysics()
1497 currentVehicle = currentVehicle.attacherVehicle
1498 end
1499 implement.object:addToPhysics()
1500 implement.object.isHardAttached = false
1501
1502 if self.isServer then
1503 self:raiseDirtyFlags(self.vehicleDirtyFlag)
1504 end
1505
1506 return true
1507end

initSpecialization

Description
Definition
initSpecialization()
Code
21function AttacherJoints.initSpecialization()
22 g_configurationManager:addConfigurationType("attacherJoint", g_i18n:getText("configuration_attacherJoint"), "attacherJoints", nil, nil, nil, ConfigurationUtil.SELECTOR_MULTIOPTION)
23
24 Vehicle.registerStateChange("ATTACH")
25 Vehicle.registerStateChange("DETACH")
26 Vehicle.registerStateChange("LOWER_ALL_IMPLEMENTS")
27end

isDetachAllowed

Description
Returns true if detach is allowed
Definition
isDetachAllowed()
Return Values
booleandetachAlloweddetach is allowed
Code
2557function AttacherJoints:isDetachAllowed(superFunc)
2558 local detachAllowed, warning, showWarning = superFunc(self)
2559 if not detachAllowed then
2560 return detachAllowed, warning, showWarning
2561 end
2562
2563 local spec = self.spec_attacherJoints
2564 for attacherJointIndex, attacherJoint in ipairs(spec.attacherJoints) do
2565 if not attacherJoint.allowDetachingWhileLifted then
2566 if not attacherJoint.moveDown then
2567 local implement = self:getImplementByJointDescIndex(attacherJointIndex)
2568 if implement ~= nil then
2569 local inputAttacherJoint = implement.object:getInputAttacherJointByJointDescIndex(implement.inputJointDescIndex)
2570 if inputAttacherJoint ~= nil and not inputAttacherJoint.forceAllowDetachWhileLifted then
2571 return false, string.format(g_i18n:getText("warning_lowerImplementFirst"), implement.object.typeDesc)
2572 end
2573 end
2574 end
2575 end
2576 end
2577
2578 return true
2579end

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
1896function AttacherJoints:loadAttacherJointFromXML(attacherJoint, xmlFile, baseName, index)
1897 local spec = self.spec_attacherJoints
1898
1899 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#index", baseName .. "#node") -- FS17
1900 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#indexVisual", baseName .. "#nodeVisual") -- FS17
1901 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#ptoOutputNode", "vehicle.powerTakeOffs.output") -- FS17 to FS19
1902 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#lowerDistanceToGround", baseName..".distanceToGround#lower") -- FS17 to FS19
1903 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#upperDistanceToGround", baseName..".distanceToGround#upper") -- FS17 to FS19
1904 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#rotationNode", baseName..".rotationNode#node") -- FS17 to FS19
1905 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#upperRotation", baseName..".rotationNode#upperRotation") -- FS17 to FS19
1906 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#lowerRotation", baseName..".rotationNode#lowerRotation") -- FS17 to FS19
1907 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#startRotation", baseName..".rotationNode#startRotation") -- FS17 to FS19
1908 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#rotationNode2", baseName..".rotationNode2#node") -- FS17 to FS19
1909 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#upperRotation2", baseName..".rotationNode2#upperRotation") -- FS17 to FS19
1910 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#lowerRotation2", baseName..".rotationNode2#lowerRotation") -- FS17 to FS19
1911 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#transNode", baseName..".transNode#node") -- FS17 to FS19
1912 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#transNodeMinY", baseName..".transNode#minY") -- FS17 to FS19
1913 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#transNodeMaxY", baseName..".transNode#maxY") -- FS17 to FS19
1914 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#transNodeHeight", baseName..".transNode#height") -- FS17 to FS19
1915
1916
1917 local node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. "#node"), self.i3dMappings)
1918 if node == nil then
1919 g_logManager:xmlWarning(self.configFileName, "Missing node for attacherJoint '%s'", baseName)
1920 return false
1921 end
1922
1923 attacherJoint.jointTransform = node
1924
1925 attacherJoint.jointTransformVisual = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName .. "#nodeVisual"), self.i3dMappings)
1926 attacherJoint.supportsHardAttach = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#supportsHardAttach"), true)
1927
1928 local jointTypeStr = getXMLString(xmlFile, baseName.. "#jointType")
1929 local jointType
1930 if jointTypeStr ~= nil then
1931 jointType = AttacherJoints.jointTypeNameToInt[jointTypeStr]
1932 if jointType == nil then
1933 g_logManager:xmlWarning(self.configFileName, "Invalid jointType '%s' for attacherJoint '%s'!", tostring(jointTypeStr), baseName)
1934 end
1935 end
1936 if jointType == nil then
1937 jointType = AttacherJoints.JOINTTYPE_IMPLEMENT
1938 end
1939 attacherJoint.jointType = jointType
1940 attacherJoint.allowsJointLimitMovement = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#allowsJointLimitMovement"), true)
1941 attacherJoint.allowsLowering = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#allowsLowering"), true)
1942 attacherJoint.isDefaultLowered = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#isDefaultLowered"), false)
1943
1944 attacherJoint.allowDetachingWhileLifted = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#allowDetachingWhileLifted"), true)
1945 attacherJoint.allowFoldingWhileAttached = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#allowFoldingWhileAttached"), true)
1946
1947 if jointType == AttacherJoints.JOINTTYPE_TRAILER or jointType == AttacherJoints.JOINTTYPE_TRAILERLOW then
1948 attacherJoint.allowsLowering = false
1949 end
1950
1951 attacherJoint.canTurnOnImplement = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#canTurnOnImplement"), true)
1952
1953 local rotationNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".rotationNode#node"), self.i3dMappings)
1954 if rotationNode ~= nil then
1955 attacherJoint.rotationNode = rotationNode
1956 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName..".rotationNode#lowerRotation"))
1957 attacherJoint.lowerRotation = { math.rad(Utils.getNoNil(x, 0)), math.rad(Utils.getNoNil(y, 0)), math.rad(Utils.getNoNil(z, 0)) }
1958
1959 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName..".rotationNode#upperRotation"))
1960 local rx,ry,rz = getRotation(rotationNode)
1961 attacherJoint.upperRotation = { Utils.getNoNilRad(x, rx), Utils.getNoNilRad(y, ry), Utils.getNoNilRad(z, rz) }
1962
1963 local startRot = StringUtil.getRadiansFromString(getXMLString(xmlFile, baseName..".rotationNode#startRotation"), 3)
1964 if startRot ~= nil then
1965 attacherJoint.rotX, attacherJoint.rotY, attacherJoint.rotZ = startRot[1],startRot[2],startRot[3]
1966 else
1967 attacherJoint.rotX, attacherJoint.rotY, attacherJoint.rotZ = getRotation(rotationNode)
1968 end
1969
1970 local lowerValues = {attacherJoint.lowerRotation[1], attacherJoint.lowerRotation[2], attacherJoint.lowerRotation[3]}
1971 local upperValues = {attacherJoint.upperRotation[1], attacherJoint.upperRotation[2], attacherJoint.upperRotation[3]}
1972
1973 for i=1, 3 do
1974 local l = lowerValues[i]
1975 local u = upperValues[i]
1976
1977 if l > u then
1978 upperValues[i] = l
1979 lowerValues[i] = u
1980 end
1981 end
1982
1983 attacherJoint.rotX = MathUtil.clamp(attacherJoint.rotX, lowerValues[1], upperValues[1])
1984 attacherJoint.rotY = MathUtil.clamp(attacherJoint.rotY, lowerValues[2], upperValues[2])
1985 attacherJoint.rotZ = MathUtil.clamp(attacherJoint.rotZ, lowerValues[3], upperValues[3])
1986 end
1987 local rotationNode2 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".rotationNode2#node"), self.i3dMappings)
1988 if rotationNode2 ~= nil then
1989 attacherJoint.rotationNode2 = rotationNode2
1990 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName..".rotationNode2#lowerRotation"))
1991 if x ~= nil and y ~= nil and z ~= nil then
1992 attacherJoint.lowerRotation2 = { math.rad(Utils.getNoNil(x, 0)), math.rad(Utils.getNoNil(y, 0)), math.rad(Utils.getNoNil(z, 0)) }
1993 else
1994 attacherJoint.lowerRotation2 = { -attacherJoint.lowerRotation[1], -attacherJoint.lowerRotation[2], -attacherJoint.lowerRotation[3] }
1995 end
1996
1997 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName..".rotationNode2#upperRotation"))
1998 if x ~= nil and y ~= nil and z ~= nil then
1999 attacherJoint.upperRotation2 = { math.rad(Utils.getNoNil(x, 0)), math.rad(Utils.getNoNil(y, 0)), math.rad(Utils.getNoNil(z, 0)) }
2000 else
2001 attacherJoint.upperRotation2 = { -attacherJoint.upperRotation[1], -attacherJoint.upperRotation[2], -attacherJoint.upperRotation[3] }
2002 end
2003 end
2004
2005 attacherJoint.transNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName..".transNode#node"), self.i3dMappings)
2006 if attacherJoint.transNode ~= nil then
2007 attacherJoint.transNodeOrgTrans = {getTranslation(attacherJoint.transNode)}
2008 attacherJoint.transNodeHeight = Utils.getNoNil(getXMLFloat(xmlFile, baseName..".transNode#height"), 0.12)
2009 attacherJoint.transNodeMinY = getXMLFloat(xmlFile, baseName..".transNode#minY")
2010 attacherJoint.transNodeMaxY = getXMLFloat(xmlFile, baseName..".transNode#maxY")
2011 end
2012
2013 -- lowerDistanceToGround is a mandatory attribute if a rotationNode is available
2014 if (attacherJoint.rotationNode ~= nil or attacherJoint.transNode ~= nil) and getXMLFloat(xmlFile, baseName..".distanceToGround#lower") == nil then
2015 g_logManager:xmlWarning(self.configFileName, "Missing '.distanceToGround#lower' for attacherJoint '%s'. Use console command 'gsVehicleAnalyze' to get correct values!", baseName)
2016 end
2017 attacherJoint.lowerDistanceToGround = Utils.getNoNil(getXMLFloat(xmlFile, baseName..".distanceToGround#lower"), 0.7)
2018
2019 -- upperDistanceToGround is a mandatory attribute if a rotationNode is available
2020 if (attacherJoint.rotationNode ~= nil or attacherJoint.transNode ~= nil) and getXMLFloat(xmlFile, baseName..".distanceToGround#upper") == nil then
2021 g_logManager:xmlWarning(self.configFileName, "Missing '.distanceToGround#upper' for attacherJoint '%s'. Use console command 'gsVehicleAnalyze' to get correct values!", baseName)
2022 end
2023 attacherJoint.upperDistanceToGround = Utils.getNoNil(getXMLFloat(xmlFile, baseName..".distanceToGround#upper"), 1.0)
2024
2025 if attacherJoint.lowerDistanceToGround > attacherJoint.upperDistanceToGround then
2026 g_logManager:xmlWarning(self.configFileName, "distanceToGround#lower may not be larger than distanceToGround#upper for attacherJoint '%s'. Switching values!", baseName)
2027 local copy = attacherJoint.lowerDistanceToGround
2028 attacherJoint.lowerDistanceToGround = attacherJoint.upperDistanceToGround
2029 attacherJoint.upperDistanceToGround = copy
2030 end
2031
2032 attacherJoint.lowerRotationOffset = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#lowerRotationOffset"), 0))
2033 attacherJoint.upperRotationOffset = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#upperRotationOffset"), 0))
2034
2035 attacherJoint.lockDownRotLimit = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#lockDownRotLimit"), false)
2036 attacherJoint.lockUpRotLimit = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#lockUpRotLimit"), false)
2037 -- only use translimit in +y. Set -y to 0
2038 attacherJoint.lockDownTransLimit = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#lockDownTransLimit"), true)
2039 attacherJoint.lockUpTransLimit = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#lockUpTransLimit"), false)
2040
2041 local lowerRotLimitStr = "20 20 20"
2042 if jointType ~= AttacherJoints.JOINTTYPE_IMPLEMENT then
2043 lowerRotLimitStr = "0 0 0"
2044 end
2045 local lx, ly, lz = StringUtil.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, baseName.."#lowerRotLimit"), lowerRotLimitStr))
2046 attacherJoint.lowerRotLimit = {}
2047 attacherJoint.lowerRotLimit[1] = math.rad(math.abs(Utils.getNoNil(lx, 20)))
2048 attacherJoint.lowerRotLimit[2] = math.rad(math.abs(Utils.getNoNil(ly, 20)))
2049 attacherJoint.lowerRotLimit[3] = math.rad(math.abs(Utils.getNoNil(lz, 20)))
2050 local ux, uy, uz = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName.."#upperRotLimit"))
2051 attacherJoint.upperRotLimit = {}
2052 attacherJoint.upperRotLimit[1] = math.rad(math.abs(Utils.getNoNil(Utils.getNoNil(ux, lx), 20)))
2053 attacherJoint.upperRotLimit[2] = math.rad(math.abs(Utils.getNoNil(Utils.getNoNil(uy, ly), 20)))
2054 attacherJoint.upperRotLimit[3] = math.rad(math.abs(Utils.getNoNil(Utils.getNoNil(uz, lz), 20)))
2055
2056 local lowerTransLimitStr = "0.5 0.5 0.5"
2057 if jointType ~= AttacherJoints.JOINTTYPE_IMPLEMENT then
2058 lowerTransLimitStr = "0 0 0"
2059 end
2060 local lx, ly, lz = StringUtil.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, baseName.."#lowerTransLimit"), lowerTransLimitStr))
2061 attacherJoint.lowerTransLimit = {}
2062 attacherJoint.lowerTransLimit[1] = math.abs(Utils.getNoNil(lx, 0))
2063 attacherJoint.lowerTransLimit[2] = math.abs(Utils.getNoNil(ly, 0))
2064 attacherJoint.lowerTransLimit[3] = math.abs(Utils.getNoNil(lz, 0))
2065 local ux, uy, uz = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName.."#upperTransLimit"))
2066 attacherJoint.upperTransLimit = {}
2067 attacherJoint.upperTransLimit[1] = math.abs(Utils.getNoNil(Utils.getNoNil(ux, lx), 0))
2068 attacherJoint.upperTransLimit[2] = math.abs(Utils.getNoNil(Utils.getNoNil(uy, ly), 0))
2069 attacherJoint.upperTransLimit[3] = math.abs(Utils.getNoNil(Utils.getNoNil(uz, lz), 0))
2070
2071
2072 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName.."#jointPositionOffset"))
2073 attacherJoint.jointPositionOffset = {}
2074 attacherJoint.jointPositionOffset[1] = Utils.getNoNil(x, 0)
2075 attacherJoint.jointPositionOffset[2] = Utils.getNoNil(y, 0)
2076 attacherJoint.jointPositionOffset[3] = Utils.getNoNil(z, 0)
2077
2078 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName.."#rotLimitSpring"))
2079 attacherJoint.rotLimitSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) }
2080 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName.."#rotLimitDamping"))
2081 attacherJoint.rotLimitDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) }
2082 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName.."#rotLimitForceLimit"))
2083 attacherJoint.rotLimitForceLimit = { Utils.getNoNil(x, -1), Utils.getNoNil(y, -1), Utils.getNoNil(z, -1) }
2084
2085 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName.."#transLimitSpring"))
2086 attacherJoint.transLimitSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) }
2087 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName.."#transLimitDamping"))
2088 attacherJoint.transLimitDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) }
2089 local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, baseName.."#transLimitForceLimit"))
2090 attacherJoint.transLimitForceLimit = { Utils.getNoNil(x, -1), Utils.getNoNil(y, -1), Utils.getNoNil(z, -1) }
2091
2092 attacherJoint.moveDefaultTime = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#moveTime"), 0.5)*1000
2093 attacherJoint.moveTime = attacherJoint.moveDefaultTime
2094
2095 attacherJoint.enableCollision = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#enableCollision"), false)
2096
2097 local topArmFilename = getXMLString(xmlFile, baseName.. ".topArm#filename")
2098 if topArmFilename ~= nil then
2099 local baseNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".topArm#baseNode"), self.i3dMappings)
2100 if baseNode ~= nil then
2101 local i3dNode = g_i3DManager:loadSharedI3DFile(topArmFilename,self.baseDirectory, false, false, false)
2102 if i3dNode ~= 0 then
2103 local rootNode = getChildAt(i3dNode, 0)
2104 link(baseNode, rootNode)
2105 delete(i3dNode)
2106 setTranslation(rootNode, 0,0,0)
2107 local translationNode = getChildAt(rootNode, 0)
2108 local referenceNode = getChildAt(translationNode, 0)
2109
2110
2111 local topArm = {}
2112 topArm.rotationNode = rootNode
2113 topArm.rotX, topArm.rotY, topArm.rotZ = 0,0,0
2114 topArm.translationNode = translationNode
2115
2116 local _,_,referenceDistance = getTranslation(referenceNode)
2117 topArm.referenceDistance = referenceDistance
2118
2119 topArm.zScale = 1
2120 local zScale = MathUtil.sign(Utils.getNoNil(getXMLFloat(xmlFile, baseName.. ".topArm#zScale"), 1))
2121 if zScale < 0 then
2122 topArm.rotY = math.pi
2123 setRotation(rootNode, topArm.rotX, topArm.rotY, topArm.rotZ)
2124 end
2125
2126 if getNumOfChildren(rootNode) > 1 then
2127 topArm.scaleNode = getChildAt(rootNode, 1)
2128 local scaleReferenceNode = getChildAt(topArm.scaleNode, 0)
2129 local _,_,scaleReferenceDistance = getTranslation(scaleReferenceNode)
2130 topArm.scaleReferenceDistance = scaleReferenceDistance
2131 end
2132
2133 topArm.toggleVisibility = Utils.getNoNil(getXMLBool(xmlFile, baseName.. ".topArm#toggleVisibility"), false)
2134 if topArm.toggleVisibility then
2135 setVisibility(topArm.rotationNode, false)
2136 end
2137
2138 local colorValueStr = getXMLString(xmlFile, baseName..".topArm#color")
2139 local colorValue = g_brandColorManager:getBrandColorByName(colorValueStr)
2140 if colorValue == nil then
2141 colorValue = StringUtil.getVectorNFromString(colorValueStr, 3)
2142 end
2143
2144 local colorValue2Str = getXMLString(xmlFile, baseName..".topArm#color2")
2145 local colorValue2 = g_brandColorManager:getBrandColorByName(colorValue2Str)
2146 if colorValue2 == nil then
2147 colorValue2 = StringUtil.getVectorNFromString(colorValue2Str, 3)
2148 end
2149
2150 local decalColorStr = getXMLString(xmlFile, baseName..".topArm#decalColor")
2151 local decalColor = g_brandColorManager:getBrandColorByName(decalColorStr)
2152 if decalColor == nil then
2153 decalColor = StringUtil.getVectorNFromString(decalColorStr, 3)
2154 end
2155
2156 -- on dark colors we use a white decal and on bright colors a black decal
2157 if decalColor == nil and colorValue ~= nil then
2158 local brightness = MathUtil.getBrightnessFromColor(colorValue[1], colorValue[2], colorValue[3])
2159 brightness = (brightness > 0.075 and 1) or 0
2160
2161 decalColor = {1-brightness, 1-brightness, 1-brightness}
2162 end
2163
2164 if colorValue ~= nil then
2165 local material = getXMLInt(xmlFile, baseName..".topArm#material")
2166 I3DUtil.setShaderParameterRec(rootNode, "colorMat0", colorValue[1], colorValue[2], colorValue[3], material or colorValue[4])
2167 end
2168 if colorValue2 ~= nil then
2169 local material2 = getXMLInt(xmlFile, baseName..".topArm#material2")
2170 I3DUtil.setShaderParameterRec(rootNode, "colorMat1", colorValue2[1], colorValue2[2], colorValue2[3], material2 or colorValue2[4])
2171 end
2172 if decalColor ~= nil then
2173 I3DUtil.setShaderParameterRec(rootNode, "colorMat2", decalColor[1], decalColor[2], decalColor[3], 1)
2174 end
2175
2176 attacherJoint.topArm = topArm
2177 end
2178 end
2179 else
2180 local rotationNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".topArm#rotationNode"), self.i3dMappings)
2181 local translationNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".topArm#translationNode"), self.i3dMappings)
2182 local referenceNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".topArm#referenceNode"), self.i3dMappings)
2183 if rotationNode ~= nil then
2184 local topArm = {}
2185 topArm.rotationNode = rotationNode
2186 topArm.rotX, topArm.rotY, topArm.rotZ = getRotation(rotationNode)
2187 if translationNode ~= nil and referenceNode ~= nil then
2188 topArm.translationNode = translationNode
2189
2190 local x,y,z = getTranslation(translationNode)
2191 if math.abs(x) >= 0.0001 or math.abs(y) >= 0.0001 or math.abs(z) >= 0.0001 then
2192 g_logManager:xmlWarning(self.configFileName, "TopArm translation of attacherJoint '%s' is not 0/0/0!", baseName)
2193 end
2194 topArm.referenceDistance = calcDistanceFrom(referenceNode, translationNode)
2195 end
2196 topArm.zScale = MathUtil.sign(Utils.getNoNil(getXMLFloat(xmlFile, baseName.. ".topArm#zScale"), 1))
2197 topArm.toggleVisibility = Utils.getNoNil(getXMLBool(xmlFile, baseName.. ".topArm#toggleVisibility"), false)
2198 if topArm.toggleVisibility then
2199 setVisibility(topArm.rotationNode, false)
2200 end
2201
2202 attacherJoint.topArm = topArm
2203 end
2204 end
2205
2206 local rotationNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".bottomArm#rotationNode"), self.i3dMappings)
2207 local translationNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".bottomArm#translationNode"), self.i3dMappings)
2208 local referenceNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".bottomArm#referenceNode"), self.i3dMappings)
2209 if rotationNode ~= nil then
2210 local bottomArm = {}
2211 bottomArm.rotationNode = rotationNode
2212 bottomArm.lastDirection = {0, 0, 0}
2213 local startRot = StringUtil.getRadiansFromString(getXMLString(xmlFile, baseName..".bottomArm#startRotation"), 3)
2214 if startRot ~= nil then
2215 bottomArm.rotX, bottomArm.rotY, bottomArm.rotZ = startRot[1],startRot[2],startRot[3]
2216 else
2217 bottomArm.rotX, bottomArm.rotY, bottomArm.rotZ = getRotation(rotationNode)
2218 end
2219 if translationNode ~= nil and referenceNode ~= nil then
2220 bottomArm.translationNode = translationNode
2221
2222 local x,y,z = getTranslation(translationNode)
2223 if math.abs(x) >= 0.0001 or math.abs(y) >= 0.0001 or math.abs(z) >= 0.0001 then
2224 g_logManager:xmlWarning(self.configFileName, "BottomArm translation of attacherJoint '%s' is not 0/0/0!", baseName)
2225 end
2226 bottomArm.referenceDistance = calcDistanceFrom(referenceNode, translationNode)
2227 end
2228 bottomArm.zScale = MathUtil.sign(Utils.getNoNil(getXMLFloat(xmlFile, baseName.. ".bottomArm#zScale"), 1))
2229 bottomArm.lockDirection = Utils.getNoNil(getXMLBool(xmlFile, baseName.. ".bottomArm#lockDirection"), true)
2230
2231 bottomArm.toggleVisibility = Utils.getNoNil(getXMLBool(xmlFile, baseName.. ".bottomArm#toggleVisibility"), false)
2232 if bottomArm.toggleVisibility then
2233 setVisibility(bottomArm.rotationNode, false)
2234 end
2235
2236 if jointType == AttacherJoints.JOINTTYPE_IMPLEMENT then
2237 local toolbarFilename = Utils.getNoNil(getXMLString(xmlFile, baseName.. ".toolbar#filename"), "$data/shared/assets/toolbar.i3d")
2238 local i3dNode = g_i3DManager:loadSharedI3DFile(toolbarFilename, self.baseDirectory, false, false, false)
2239 if i3dNode ~= 0 then
2240 local rootNode = getChildAt(i3dNode, 0)
2241 link(referenceNode, rootNode)
2242 delete(i3dNode)
2243 setTranslation(rootNode, 0,0,0)
2244 bottomArm.toolbar = rootNode
2245 setVisibility(rootNode, false)
2246 end
2247 end
2248
2249 attacherJoint.bottomArm = bottomArm
2250 end
2251
2252 if self.isClient then
2253 attacherJoint.sampleAttach = g_soundManager:loadSampleFromXML(xmlFile, baseName, "attachSound", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
2254 end
2255
2256 attacherJoint.steeringBarLeftNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".steeringBars#leftNode"), self.i3dMappings)
2257 attacherJoint.steeringBarRightNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".steeringBars#rightNode"), self.i3dMappings)
2258
2259 attacherJoint.changeObjects = {}
2260 ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, baseName, attacherJoint.changeObjects, self.components, self)
2261 -- ObjectChangeUtil.setObjectChanges(attacherJoint.changeObjects, false, self, self.setMovingToolDirty)
2262
2263 attacherJoint.rootNode = Utils.getNoNil(I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.."#rootNode"), self.i3dMappings), self.components[1].node)
2264 attacherJoint.rootNodeBackup = attacherJoint.rootNode
2265 attacherJoint.jointIndex = 0
2266
2267 local t = getXMLFloat(xmlFile, baseName .. "#comboTime")
2268 if t ~= nil then
2269 table.insert(spec.attacherJointCombos.joints, {jointIndex = index + 1, time = MathUtil.clamp(t, 0, 1) * spec.attacherJointCombos.duration})
2270 end
2271
2272 local schemaKey = baseName.. ".schema"
2273 if hasXMLProperty(xmlFile, schemaKey) then
2274 local x, y = StringUtil.getVectorFromString(getXMLString(xmlFile, schemaKey .. "#position"))
2275 local liftedOffsetX, liftedOffsetY = StringUtil.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, schemaKey.."#liftedOffset"), "0 5"))
2276
2277 self.schemaOverlay:addAttacherJoint(x, y,
2278 math.rad(Utils.getNoNil(getXMLFloat(xmlFile, schemaKey .. "#rotation"), 0)),
2279 Utils.getNoNil(getXMLBool(xmlFile, schemaKey .. "#invertX"), false),
2280 liftedOffsetX, liftedOffsetY)
2281 else
2282 g_logManager:xmlWarning(self.configFileName, "Missing schema overlay attacherJoint '%s'!", baseName)
2283 end
2284
2285 return true
2286end

loadAttachmentsFromXMLFile

Description
Called on loading
Definition
loadAttachmentsFromXMLFile(table savegame)
Arguments
tablesavegamesavegame
Code
703function AttacherJoints:loadAttachmentsFromXMLFile(xmlFile, key, idsToVehicle)
704 local spec = self.spec_attacherJoints
705
706 local i = 0
707 while true do
708 local attachmentKey = string.format("%s.attachment(%d)", key, i)
709 if not hasXMLProperty(xmlFile, attachmentKey) then
710 break
711 end
712
713 local attachmentId = getXMLString(xmlFile, attachmentKey.."#attachmentId")
714 local jointIndex = getXMLInt(xmlFile, attachmentKey.."#jointIndex")
715 local inputJointDescIndex = Utils.getNoNil(getXMLInt(xmlFile, attachmentKey.."#inputJointDescIndex"), 1)
716 if attachmentId ~= nil and jointIndex ~= nil then
717 local attachment = idsToVehicle[attachmentId]
718
719 local inputAttacherJoints
720 if attachment ~= nil and attachment.getInputAttacherJoints ~= nil then
721 inputAttacherJoints = attachment:getInputAttacherJoints()
722 end
723 local inputAttacherJoint
724 if inputAttacherJoints ~= nil then
725 inputAttacherJoint = inputAttacherJoints[inputJointDescIndex]
726 end
727
728 if inputAttacherJoint ~= nil and spec.attacherJoints[jointIndex] ~= nil and spec.attacherJoints[jointIndex].jointIndex == 0 then
729 self:attachImplement(attachment, inputJointDescIndex, jointIndex, true, nil, nil, false, true)
730
731 local moveDown = getXMLBool(xmlFile, attachmentKey.."#moveDown")
732 if moveDown ~= nil then
733 self:setJointMoveDown(jointIndex, moveDown, true)
734 end
735 end
736 end
737
738 i = i + 1
739 end
740end

loadDashboardGroupFromXML

Description
Definition
loadDashboardGroupFromXML()
Code
2530function AttacherJoints:loadDashboardGroupFromXML(superFunc, xmlFile, key, group)
2531 if not superFunc(self, xmlFile, key, group) then
2532 return false
2533 end
2534
2535 local attacherJointIndicesString = Utils.getNoNil(getXMLString(xmlFile, key .. "#attacherJointIndices"), "")
2536 group.attacherJointIndices = {StringUtil.getVectorFromString(attacherJointIndicesString)}
2537
2538 return true
2539end

onActivate

Description
Called on activate
Definition
onActivate()
Code
2667function AttacherJoints:onActivate()
2668 self:activateAttachments()
2669end

onBeaconLightsVisibilityChanged

Description
Definition
onBeaconLightsVisibilityChanged()
Code
2768function AttacherJoints:onBeaconLightsVisibilityChanged(visibility)
2769 local spec = self.spec_attacherJoints
2770 for _, implement in pairs(spec.attachedImplements) do
2771 local vehicle = implement.object
2772 if vehicle ~= nil and vehicle.setBeaconLightsVisibility ~= nil then
2773 vehicle:setBeaconLightsVisibility(visibility, true, true)
2774 end
2775 end
2776end

onBrake

Description
Definition
onBrake()
Code
2780function AttacherJoints:onBrake(brakePedal)
2781 local spec = self.spec_attacherJoints
2782 for _, implement in pairs(spec.attachedImplements) do
2783 local vehicle = implement.object
2784 if vehicle ~= nil and vehicle.brake ~= nil then
2785 vehicle:brake(brakePedal)
2786 end
2787 end
2788end

onBrakeLightsVisibilityChanged

Description
Definition
onBrakeLightsVisibilityChanged()
Code
2744function AttacherJoints:onBrakeLightsVisibilityChanged(visibility)
2745 local spec = self.spec_attacherJoints
2746 for _, implement in pairs(spec.attachedImplements) do
2747 local vehicle = implement.object
2748 if vehicle ~= nil and vehicle.setBrakeLightsVisibility ~= nil then
2749 vehicle:setBrakeLightsVisibility(visibility)
2750 end
2751 end
2752end

onDeactivate

Description
Called on deactivate
Definition
onDeactivate()
Code
2673function AttacherJoints:onDeactivate()
2674 self:deactivateAttachments()
2675 if self.isClient then
2676 local spec = self.spec_attacherJoints
2677 g_soundManager:stopSample(spec.samples.hydraulic)
2678 spec.isHydraulicSamplePlaying = false
2679 end
2680end

onDeactivateLights

Description
Called on deactivating lights
Definition
onDeactivateLights()
Code
2696function AttacherJoints:onDeactivateLights()
2697 self:deactivateAttachmentsLights()
2698end

onDelete

Description
Called on deleting
Definition
onDelete()
Code
306function AttacherJoints:onDelete()
307 local spec = self.spec_attacherJoints
308
309 if self.isClient then
310 for _, jointDesc in pairs(spec.attacherJoints) do
311 g_soundManager:deleteSample(jointDesc.sampleAttach)
312 end
313
314 g_soundManager:deleteSample(spec.samples.hydraulic)
315 g_soundManager:deleteSample(spec.samples.attach)
316 end
317end

onDraw

Description
Called on draw
Definition
onDraw(boolean isActiveForInput, boolean isSelected)
Arguments
booleanisActiveForInputtrue if vehicle is active for input
booleanisSelectedtrue if vehicle is selected
Code
684function AttacherJoints:onDraw(isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
685 local spec = self.spec_attacherJoints
686
687 if self == g_currentMission.controlledVehicle then
688 -- call draw on all attached implements, selection check is done in the implement
689 for _, implement in ipairs(spec.attachedImplements) do
690 local object = implement.object
691 if object ~= nil then
692 if object.draw ~= nil then
693 object.draw(object, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
694 end
695 end
696 end
697 end
698end

onLeaveVehicle

Description
Definition
onLeaveVehicle()
Code
2826function AttacherJoints:onLeaveVehicle()
2827 local spec = self.spec_attacherJoints
2828 for _, implement in pairs(spec.attachedImplements) do
2829 local vehicle = implement.object
2830 if vehicle ~= nil then
2831 SpecializationUtil.raiseEvent(vehicle, "onLeaveRootVehicle")
2832 end
2833 end
2834end

onLightsTypesMaskChanged

Description
Definition
onLightsTypesMaskChanged()
Code
2720function AttacherJoints:onLightsTypesMaskChanged(lightsTypesMask)
2721 local spec = self.spec_attacherJoints
2722 for _, implement in pairs(spec.attachedImplements) do
2723 local vehicle = implement.object
2724 if vehicle ~= nil and vehicle.setLightsTypesMask ~= nil then
2725 vehicle:setLightsTypesMask(lightsTypesMask, true, true)
2726 end
2727 end
2728end

onLoad

Description
Called on loading
Definition
onLoad(table savegame)
Arguments
tablesavegamesavegame
Code
179function AttacherJoints:onLoad(savegame)
180
181 local spec = self.spec_attacherJoints
182
183 spec.attacherJointCombos = {}
184 spec.attacherJointCombos.duration = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.attacherJoints#comboDuration"), 2) * 1000
185 spec.attacherJointCombos.currentTime = 0
186 spec.attacherJointCombos.direction = -1
187 spec.attacherJointCombos.isRunning = false
188 spec.attacherJointCombos.joints = {}
189
190 spec.attacherJoints = {}
191 local i=0
192 while true do
193 local baseName = string.format("vehicle.attacherJoints.attacherJoint(%d)", i)
194 if not hasXMLProperty(self.xmlFile, baseName) then
195 break
196 end
197 local attacherJoint = {}
198 if self:loadAttacherJointFromXML(attacherJoint, self.xmlFile, baseName, i) then
199 table.insert(spec.attacherJoints, attacherJoint)
200 attacherJoint.index = #spec.attacherJoints
201 end
202 i = i + 1
203 end
204
205 if self.configurations["attacherJoint"] ~= nil then
206 local attacherConfigs = string.format("vehicle.attacherJoints.attacherJointConfigurations.attacherJointConfiguration(%d)", self.configurations["attacherJoint"]-1)
207 local i=0
208 while true do
209 local baseName = string.format(attacherConfigs..".attacherJoint(%d)", i)
210 if not hasXMLProperty(self.xmlFile, baseName) then
211 break
212 end
213 local attacherJoint = {}
214 if self:loadAttacherJointFromXML(attacherJoint, self.xmlFile, baseName, i) then
215 table.insert(spec.attacherJoints, attacherJoint)
216 end
217 i = i + 1
218 end
219 ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.attacherJoints.attacherJointConfigurations.attacherJointConfiguration", self.configurations["attacherJoint"], self.components, self)
220 end
221
222 spec.attachedImplements = {}
223 spec.selectedImplement = nil
224
225 -- data structure to store information about eventually attachable vehicles
226 spec.attachableInfo = {}
227 spec.attachableInfo.attacherVehicle = nil
228 spec.attachableInfo.attacherVehicleJointDescIndex = nil
229 spec.attachableInfo.attachable = nil
230 spec.attachableInfo.attachableJointDescIndex = nil
231
232 if self.isClient then
233 spec.samples = {}
234 spec.isHydraulicSamplePlaying = false
235 spec.samples.hydraulic = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.attacherJoints.sounds", "hydraulic", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
236 spec.samples.attach = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.attacherJoints.sounds", "attach", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
237 end
238
239 if self.isClient and g_isDevelopmentVersion then
240 for k, attacherJoint in ipairs(spec.attacherJoints) do
241 if spec.samples.attach == nil and attacherJoint.sampleAttach == nil then
242 g_logManager:xmlDevWarning(self.configFileName, "Missing attach sound for attacherjoint '%d'", k)
243 end
244 if attacherJoint.rotationNode ~= nil and spec.samples.hydraulic == nil then
245 g_logManager:xmlDevWarning(self.configFileName, "Missing hydraulic sound for attacherjoint '%d'", k)
246 end
247 end
248 end
249
250 spec.showAttachNotAllowedText = 0
251end

onPostLoad

Description
Called after loading
Definition
onPostLoad(table savegame)
Arguments
tablesavegamesavegame
Code
256function AttacherJoints:onPostLoad(savegame)
257 local spec = self.spec_attacherJoints
258
259 for _, attacherJoint in pairs(spec.attacherJoints) do
260 attacherJoint.jointOrigRot = { getRotation(attacherJoint.jointTransform) }
261 attacherJoint.jointOrigTrans = { getTranslation(attacherJoint.jointTransform) }
262 if attacherJoint.transNode ~= nil then
263 attacherJoint.transNodeMinY = Utils.getNoNil(attacherJoint.transNodeMinY, attacherJoint.jointOrigTrans[2])
264 attacherJoint.transNodeMaxY = Utils.getNoNil(attacherJoint.transNodeMaxY, attacherJoint.jointOrigTrans[2])
265 _, attacherJoint.transNodeOffsetY, _ = localToLocal(attacherJoint.jointTransform, attacherJoint.transNode, 0, 0, 0)
266 _, attacherJoint.transNodeMinY, _ = localToLocal(getParent(attacherJoint.transNode), self.rootNode, 0, attacherJoint.transNodeMinY, 0)
267 _, attacherJoint.transNodeMaxY, _ = localToLocal(getParent(attacherJoint.transNode), self.rootNode, 0, attacherJoint.transNodeMaxY, 0)
268 end
269
270 if attacherJoint.bottomArm ~= nil then
271 setRotation(attacherJoint.bottomArm.rotationNode, attacherJoint.bottomArm.rotX, attacherJoint.bottomArm.rotY, attacherJoint.bottomArm.rotZ)
272 if self.setMovingToolDirty ~= nil then
273 self:setMovingToolDirty(attacherJoint.bottomArm.rotationNode)
274 end
275 end
276 if attacherJoint.rotationNode ~= nil then
277 setRotation(attacherJoint.rotationNode, attacherJoint.rotX, attacherJoint.rotY, attacherJoint.rotZ)
278 end
279 end
280
281 if savegame ~= nil and not savegame.resetVehicles then
282 if spec.attacherJointCombos ~= nil then
283 local comboDirection = getXMLInt(savegame.xmlFile, savegame.key..".attacherJoints#comboDirection")
284 if comboDirection ~= nil then
285 spec.attacherJointCombos.direction = comboDirection
286 if comboDirection == 1 then
287 spec.attacherJointCombos.currentTime = spec.attacherJointCombos.duration
288 end
289 end
290 end
291 end
292end

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
443function AttacherJoints:onPostUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
444 local spec = self.spec_attacherJoints
445
446 for _,implement in pairs(spec.attachedImplements) do
447 if not implement.attachingIsInProgress then
448 local attacherJoint = implement.object:getActiveInputAttacherJoint()
449 if attacherJoint ~= nil then
450 if spec.attacherJoints[implement.jointDescIndex].steeringBarLeftNode ~= nil and attacherJoint.steeringBarLeftMovingPart ~= nil then
451 Cylindered.updateMovingPart(self, attacherJoint.steeringBarLeftMovingPart, nil, true)
452 end
453 if spec.attacherJoints[implement.jointDescIndex].steeringBarRightNode ~= nil and attacherJoint.steeringBarRightMovingPart ~= nil then
454 Cylindered.updateMovingPart(self, attacherJoint.steeringBarRightMovingPart, nil, true)
455 end
456 end
457 end
458 end
459end

onPreDelete

Description
Called on before deleting
Definition
onPreDelete()
Code
296function AttacherJoints:onPreDelete()
297 local spec = self.spec_attacherJoints
298
299 for i=table.getn(spec.attachedImplements), 1, -1 do
300 self:detachImplement(1, true)
301 end
302end

onReadStream

Description
Called on client side on join
Definition
onReadStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
365function AttacherJoints:onReadStream(streamId, connection)
366 local numImplements = streamReadInt8(streamId)
367 for i=1, numImplements do
368 local object = NetworkUtil.readNodeObject(streamId)
369 local inputJointDescIndex = streamReadInt8(streamId)
370 local jointDescIndex = streamReadInt8(streamId)
371 local moveDown = streamReadBool(streamId)
372 if object ~= nil then
373 self:attachImplement(object, inputJointDescIndex, jointDescIndex, true, i)
374 self:setJointMoveDown(jointDescIndex, moveDown, true)
375 end
376 end
377end

onRegisterActionEvents

Description
Definition
onRegisterActionEvents()
Code
2621function AttacherJoints:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
2622 if self.isClient then
2623 local spec = self.spec_attacherJoints
2624 self:clearActionEventsTable(spec.actionEvents)
2625
2626 -- ignore vehicle selection on 'getIsActiveForInput', so we can select the target vehicle and attach or lower it
2627 if isActiveForInputIgnoreSelection then
2628 if table.getn(spec.attacherJoints) > 0 then
2629 -- only display lower and attach action if selected implement is direct child of vehicle, not sub child
2630 local selectedImplement = self:getSelectedImplement()
2631 if selectedImplement ~= nil and selectedImplement.object ~= self then
2632 for _, attachedImplement in pairs(spec.attachedImplements) do
2633 if attachedImplement == selectedImplement then
2634 -- custom registration of the action event. This allows us to overwritte it in the implement (e.g in Foldable)
2635 selectedImplement.object:registerLoweringActionEvent(spec.actionEvents, InputAction.LOWER_IMPLEMENT, selectedImplement.object, AttacherJoints.actionEventLowerImplement, false, true, false, true, nil, nil, true)
2636 end
2637 end
2638 end
2639
2640 local _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.LOWER_ALL_IMPLEMENTS, self, AttacherJoints.actionEventLowerAllImplements, false, true, false, true, nil, nil, true)
2641 g_inputBinding:setActionEventTextVisibility(actionEventId, false)
2642 end
2643
2644 if self:getSelectedVehicle() == self then
2645 local state, _ = self:registerSelfLoweringActionEvent(spec.actionEvents, InputAction.LOWER_IMPLEMENT, self, AttacherJoints.actionEventLowerImplement, false, true, false, true, nil, nil, true)
2646
2647 -- if the selected attacher vehicle can not be lowered and we got only one implement that can be lowered
2648 -- we add the lowering action for the first implement
2649 if state == nil or not state then
2650 if #spec.attachedImplements == 1 then
2651 local firstImplement = spec.attachedImplements[1]
2652 if firstImplement ~= nil then
2653 firstImplement.object:registerLoweringActionEvent(spec.actionEvents, InputAction.LOWER_IMPLEMENT, firstImplement.object, AttacherJoints.actionEventLowerImplement, false, true, false, true, nil, nil, true)
2654 end
2655 end
2656 end
2657 end
2658
2659 local _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.ATTACH, self, AttacherJoints.actionEventAttach, false, true, false, true, nil, nil, true)
2660 g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_HIGH)
2661 end
2662 end
2663end

onReverseDirectionChanged

Description
Definition
onReverseDirectionChanged()
Code
2684function AttacherJoints:onReverseDirectionChanged(direction)
2685 local spec = self.spec_attacherJoints
2686
2687 if spec.attacherJointCombos ~= nil then
2688 for _, joint in pairs(spec.attacherJointCombos.joints) do
2689 joint.time = math.abs(joint.time - spec.attacherJointCombos.duration)
2690 end
2691 end
2692end

onReverseLightsVisibilityChanged

Description
Definition
onReverseLightsVisibilityChanged()
Code
2756function AttacherJoints:onReverseLightsVisibilityChanged(visibility)
2757 local spec = self.spec_attacherJoints
2758 for _, implement in pairs(spec.attachedImplements) do
2759 local vehicle = implement.object
2760 if vehicle ~= nil and vehicle.setReverseLightsVisibility ~= nil then
2761 vehicle:setReverseLightsVisibility(visibility)
2762 end
2763 end
2764end

onStateChange

Description
Definition
onStateChange()
Code
2702function AttacherJoints:onStateChange(state, data)
2703 local spec = self.spec_attacherJoints
2704
2705 for _, implement in pairs(spec.attachedImplements) do
2706 if implement.object ~= nil then
2707 implement.object:raiseStateChange(state, data)
2708 end
2709 end
2710
2711 if state == Vehicle.STATE_CHANGE_LOWER_ALL_IMPLEMENTS then
2712 if table.getn(spec.attacherJoints) > 0 then
2713 self:startAttacherJointCombo()
2714 end
2715 end
2716end

onTurnedOff

Description
Definition
onTurnedOff()
Code
2809function AttacherJoints:onTurnedOff()
2810 local spec = self.spec_attacherJoints
2811 for _, implement in pairs(spec.attachedImplements) do
2812 local vehicle = implement.object
2813 if vehicle ~= nil then
2814 local turnedOnVehicleSpec = vehicle.spec_turnOnVehicle
2815 if turnedOnVehicleSpec then
2816 if turnedOnVehicleSpec.turnedOnByAttacherVehicle then
2817 vehicle:setIsTurnedOn(false, true)
2818 end
2819 end
2820 end
2821 end
2822end

onTurnedOn

Description
Definition
onTurnedOn()
Code
2792function AttacherJoints:onTurnedOn()
2793 local spec = self.spec_attacherJoints
2794 for _, implement in pairs(spec.attachedImplements) do
2795 local vehicle = implement.object
2796 if vehicle ~= nil then
2797 local turnedOnVehicleSpec = vehicle.spec_turnOnVehicle
2798 if turnedOnVehicleSpec then
2799 if turnedOnVehicleSpec.turnedOnByAttacherVehicle then
2800 vehicle:setIsTurnedOn(true, true)
2801 end
2802 end
2803 end
2804 end
2805end

onTurnLightStateChanged

Description
Definition
onTurnLightStateChanged()
Code
2732function AttacherJoints:onTurnLightStateChanged(state)
2733 local spec = self.spec_attacherJoints
2734 for _, implement in pairs(spec.attachedImplements) do
2735 local vehicle = implement.object
2736 if vehicle ~= nil and vehicle.setTurnLightState ~= nil then
2737 vehicle:setTurnLightState(state, true, true)
2738 end
2739 end
2740end

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
407function AttacherJoints:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
408 local spec = self.spec_attacherJoints
409
410 for _, implement in pairs(spec.attachedImplements) do
411 if implement.object ~= nil then
412 if self.isServer or (self.updateLoopIndex == implement.object.updateLoopIndex) then
413 self:updateAttacherJointGraphics(implement, dt)
414 end
415 end
416 end
417
418 if not self.isServer and self.getAttacherVehicle ~= nil then
419 local attacherVehicle = self:getAttacherVehicle()
420 if attacherVehicle ~= nil then
421 if self.updateLoopIndex == attacherVehicle.updateLoopIndex then
422 local implement = attacherVehicle:getImplementByObject(self)
423 if implement ~= nil then
424 attacherVehicle:updateAttacherJointGraphics(implement, dt)
425 end
426 end
427 end
428 end
429
430 if self.isClient then
431 spec.showAttachNotAllowedText = math.max(spec.showAttachNotAllowedText - dt, 0)
432 if spec.showAttachNotAllowedText > 0 then
433 g_currentMission:addExtraPrintText(g_i18n:getText("info_attach_not_allowed"))
434 end
435 end
436end

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
466function AttacherJoints:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
467 local spec = self.spec_attacherJoints
468
469 local playHydraulicSound = false
470
471 for _, implement in pairs(spec.attachedImplements) do
472 if implement.object ~= nil then
473 local jointDesc = spec.attacherJoints[implement.jointDescIndex]
474
475 if not implement.object.isHardAttached then
476 if self.isServer then
477 if implement.attachingIsInProgress then
478 local done = true
479 for i=1,3 do
480 local lastRotLimit = implement.attachingRotLimit[i]
481 local lastTransLimit = implement.attachingTransLimit[i]
482 implement.attachingRotLimit[i] = math.max(0, implement.attachingRotLimit[i] - implement.attachingRotLimitSpeed[i] * dt)
483 implement.attachingTransLimit[i] = math.max(0, implement.attachingTransLimit[i] - implement.attachingTransLimitSpeed[i] * dt)
484 if (implement.attachingRotLimit[i] > 0 or implement.attachingTransLimit[i] > 0) or
485 (lastRotLimit > 0 or lastTransLimit > 0)
486 then
487 done = false
488 end
489 end
490 implement.attachingIsInProgress = not done
491
492 if done then
493 if implement.object.spec_attachable.attacherJoint.hardAttach and self:getIsHardAttachAllowed(implement.jointDescIndex) then
494 self:hardAttachImplement(implement)
495 end
496 self:postAttachImplement(implement)
497 end
498 end
499 end
500 if not implement.attachingIsInProgress then
501 local jointFrameInvalid = false
502 if jointDesc.allowsLowering then
503 local moveAlpha = Utils.getMovedLimitedValue(jointDesc.moveAlpha, jointDesc.lowerAlpha, jointDesc.upperAlpha, jointDesc.moveTime, dt, not jointDesc.moveDown)
504 if moveAlpha ~= jointDesc.moveAlpha then
505 playHydraulicSound = true
506 jointDesc.moveAlpha = moveAlpha
507 jointDesc.moveLimitAlpha = 1- (moveAlpha-jointDesc.lowerAlpha) / (jointDesc.upperAlpha-jointDesc.lowerAlpha)
508 jointFrameInvalid = true
509 self:updateAttacherJointRotationNodes(jointDesc, jointDesc.moveAlpha)
510 self:updateAttacherJointRotation(jointDesc, implement.object)
511 end
512 end
513
514 jointFrameInvalid = jointFrameInvalid or jointDesc.jointFrameInvalid
515 if jointFrameInvalid then
516 jointDesc.jointFrameInvalid = false
517 if self.isServer then
518 setJointFrame(jointDesc.jointIndex, 0, jointDesc.jointTransform)
519 end
520 end
521 end
522 if self.isServer then
523 local force = implement.attachingIsInProgress
524 if force or (jointDesc.allowsLowering and jointDesc.allowsJointLimitMovement) then
525 if jointDesc.jointIndex ~= nil and jointDesc.jointIndex ~= 0 then
526 if force or implement.object.spec_attachable.attacherJoint.allowsJointRotLimitMovement then
527 for i=1,3 do
528 local newRotLimit = MathUtil.lerp( math.max(implement.attachingRotLimit[i], implement.upperRotLimit[i]),
529 math.max(implement.attachingRotLimit[i], implement.lowerRotLimit[i]), jointDesc.moveLimitAlpha)
530 if force or math.abs(newRotLimit - implement.jointRotLimit[i]) > 0.0005 then
531 local rotLimitDown = -newRotLimit
532 local rotLimitUp = newRotLimit
533 if i == 3 then
534 if jointDesc.lockDownRotLimit then
535 rotLimitDown = math.min(-implement.attachingRotLimit[i], 0)
536 end
537 if jointDesc.lockUpRotLimit then
538 rotLimitUp = math.max(implement.attachingRotLimit[i], 0)
539 end
540 end
541 setJointRotationLimit(jointDesc.jointIndex, i-1, true, rotLimitDown, rotLimitUp)
542 implement.jointRotLimit[i] = newRotLimit
543 end
544 end
545 end
546
547 if force or implement.object.spec_attachable.attacherJoint.allowsJointTransLimitMovement then
548 for i=1,3 do
549 local newTransLimit = MathUtil.lerp( math.max(implement.attachingTransLimit[i], implement.upperTransLimit[i]),
550 math.max(implement.attachingTransLimit[i], implement.lowerTransLimit[i]), jointDesc.moveLimitAlpha)
551
552 if force or math.abs(newTransLimit - implement.jointTransLimit[i]) > 0.0005 then
553 local transLimitDown = -newTransLimit
554 local transLimitUp = newTransLimit
555 if i == 2 then
556 if jointDesc.lockDownTransLimit then
557 transLimitDown = math.min(-implement.attachingTransLimit[i], 0)
558 end
559 if jointDesc.lockUpTransLimit then
560 transLimitUp = math.max(implement.attachingTransLimit[i], 0)
561 end
562 end
563
564 setJointTranslationLimit(jointDesc.jointIndex, i-1, true, transLimitDown, transLimitUp)
565 implement.jointTransLimit[i] = newTransLimit
566 end
567 end
568 end
569 end
570 end
571 end
572 end
573 end
574 end
575
576 if self.isClient and spec.samples.hydraulic ~= nil then
577 if playHydraulicSound then
578 if not spec.isHydraulicSamplePlaying then
579 g_soundManager:playSample(spec.samples.hydraulic)
580 spec.isHydraulicSamplePlaying = true
581 end
582 else
583 if spec.isHydraulicSamplePlaying then
584 g_soundManager:stopSample(spec.samples.hydraulic)
585 spec.isHydraulicSamplePlaying = false
586 end
587 end
588 end
589
590 local combos = spec.attacherJointCombos
591 if combos ~= nil and combos.isRunning then
592 for _, joint in pairs(combos.joints) do
593 local doLowering
594 if combos.direction == 1 and combos.currentTime >= joint.time then
595 doLowering = true
596 elseif combos.direction == -1 and combos.currentTime <= combos.duration-joint.time then
597 doLowering = false
598 end
599
600 if doLowering ~= nil then
601 local implement = self:getImplementFromAttacherJointIndex(joint.jointIndex)
602 if implement ~= nil then
603 if implement.object.setLoweredAll ~= nil then
604 implement.object:setLoweredAll(doLowering, joint.jointIndex)
605 end
606 end
607 end
608 end
609
610 if (combos.direction == -1 and combos.currentTime == 0) or
611 (combos.direction == 1 and combos.currentTime == combos.duration) then
612 combos.isRunning = false
613 end
614
615 combos.currentTime = MathUtil.clamp(combos.currentTime + dt*combos.direction, 0, combos.duration)
616 end
617
618 -- find attachables in range
619 local info = spec.attachableInfo
620 info.attacherVehicle = nil
621
622 if self.isClient then
623 if spec.actionEvents ~= nil then
624 local attachActionEvent = spec.actionEvents[InputAction.ATTACH]
625 if attachActionEvent ~= nil then
626 local visible = false
627
628 if self:getCanToggleAttach() then
629 info.attacherVehicle, info.attacherVehicleJointDescIndex, info.attachable, info.attachableJointDescIndex = AttacherJoints.findVehicleInAttachRange(self, AttacherJoints.MAX_ATTACH_DISTANCE_SQ, AttacherJoints.MAX_ATTACH_ANGLE)
630
631 local text = ""
632 local prio = GS_PRIO_VERY_LOW
633
634 local selectedVehicle = self:getSelectedVehicle()
635 if selectedVehicle ~= nil and not selectedVehicle.isDeleted and selectedVehicle.isDetachAllowed ~= nil and selectedVehicle:isDetachAllowed() then
636 if selectedVehicle:getAttacherVehicle() ~= nil then
637 visible = true
638 text = g_i18n:getText("action_detach")
639 end
640 end
641
642 if info.attacherVehicle ~= nil then
643 if g_currentMission.accessHandler:canFarmAccess(self:getActiveFarm(), info.attachable) then
644 visible = true
645 text = g_i18n:getText("action_attach")
646 g_currentMission:showAttachContext(info.attachable)
647 prio = GS_PRIO_VERY_HIGH
648 else
649 spec.showAttachNotAllowedText = 100
650 end
651 end
652
653 g_inputBinding:setActionEventText(attachActionEvent.actionEventId, text)
654 g_inputBinding:setActionEventTextPriority(attachActionEvent.actionEventId, prio)
655 end
656
657 g_inputBinding:setActionEventTextVisibility(attachActionEvent.actionEventId, visible)
658 end
659
660 local lowerActionEvent = spec.actionEvents[InputAction.LOWER_IMPLEMENT]
661 if lowerActionEvent ~= nil then
662 local showLower = false
663 local text = ""
664 local selectedImplement = self:getSelectedImplement()
665 for _,attachedImplement in pairs(spec.attachedImplements) do
666 if attachedImplement == selectedImplement then
667 showLower, text = attachedImplement.object:getLoweringActionEventState()
668 break
669 end
670 end
671
672 g_inputBinding:setActionEventActive(lowerActionEvent.actionEventId, showLower)
673 g_inputBinding:setActionEventText(lowerActionEvent.actionEventId, text)
674 g_inputBinding:setActionEventTextPriority(lowerActionEvent.actionEventId, GS_PRIO_NORMAL)
675 end
676 end
677 end
678end

onWriteStream

Description
Called on server side on join
Definition
onWriteStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
383function AttacherJoints:onWriteStream(streamId, connection)
384 local spec = self.spec_attacherJoints
385
386 -- write attached implements
387 streamWriteInt8(streamId, table.getn(spec.attachedImplements))
388 for i=1, table.getn(spec.attachedImplements) do
389 local implement = spec.attachedImplements[i]
390 local inputJointDescIndex = implement.object.spec_attachable.inputAttacherJointDescIndex
391 local jointDescIndex = implement.jointDescIndex
392 local jointDesc = spec.attacherJoints[jointDescIndex]
393 local moveDown = jointDesc.moveDown
394 NetworkUtil.writeNodeObject(streamId, implement.object)
395 streamWriteInt8(streamId, inputJointDescIndex)
396 streamWriteInt8(streamId, jointDescIndex)
397 streamWriteBool(streamId, moveDown)
398 end
399end

playAttachSound

Description
Play attach sound
Definition
playAttachSound(table jointDesc)
Arguments
tablejointDescjoint desc
Return Values
booleansuccesssuccess
Code
1694function AttacherJoints:playAttachSound(jointDesc)
1695 local spec = self.spec_attacherJoints
1696
1697 if self.isClient then
1698 if jointDesc ~= nil and jointDesc.sampleAttach ~= nil then
1699 g_soundManager:playSample(jointDesc.sampleAttach)
1700 else
1701 g_soundManager:playSample(spec.samples.attach)
1702 end
1703 end
1704
1705 return true
1706end

playDetachSound

Description
Play detach sound
Definition
playDetachSound(table jointDesc)
Arguments
tablejointDescjoint desc
Return Values
booleansuccesssuccess
Code
1712function AttacherJoints:playDetachSound(jointDesc)
1713 local spec = self.spec_attacherJoints
1714
1715 if self.isClient then
1716 if jointDesc ~= nil and jointDesc.sampleAttach ~= nil then
1717 g_soundManager:playSample(jointDesc.sampleAttach)
1718 else
1719 g_soundManager:playSample(spec.samples.attach)
1720 end
1721 end
1722
1723 return true
1724end

postAttachImplement

Description
Definition
postAttachImplement()
Code
1200function AttacherJoints:postAttachImplement(implement)
1201
1202 local object = implement.object
1203 local inputJointDescIndex = implement.inputJointDescIndex
1204 local jointDescIndex = implement.jointDescIndex
1205
1206 SpecializationUtil.raiseEvent(self, "onPostAttachImplement", object, inputJointDescIndex, jointDescIndex)
1207 object:postAttach(self, inputJointDescIndex, jointDescIndex, implement.loadFromSavegame)
1208
1209 local data = {attacherVehicle=self, attachedVehicle=implement.object}
1210 local rootVehicle = self:getRootVehicle()
1211 rootVehicle:raiseStateChange(Vehicle.STATE_CHANGE_ATTACH, data)
1212end

prerequisitesPresent

Description
Definition
prerequisitesPresent()
Code
61function AttacherJoints.prerequisitesPresent(specializations)
62 return true
63end

raiseActive

Description
Definition
raiseActive()
Code
2290function AttacherJoints:raiseActive(superFunc)
2291 local spec = self.spec_attacherJoints
2292
2293 superFunc(self)
2294 for _,implement in pairs(spec.attachedImplements) do
2295 if implement.object ~= nil then
2296 implement.object:raiseActive()
2297 end
2298 end
2299end

registerActionEvents

Description
Definition
registerActionEvents()
Code
2303function AttacherJoints:registerActionEvents(superFunc, excludedVehicle)
2304 local spec = self.spec_attacherJoints
2305
2306 superFunc(self, excludedVehicle)
2307 if self ~= excludedVehicle then
2308 -- at first we register the inputs of the selected vehicle
2309 -- so they got the higest prio and cannot be overwritten by another vehicle
2310 local selectedObject = self:getSelectedObject()
2311 if selectedObject ~= nil and self ~= selectedObject.vehicle and excludedVehicle ~= selectedObject.vehicle then
2312 selectedObject.vehicle:registerActionEvents()
2313 end
2314
2315 for _,implement in pairs(spec.attachedImplements) do
2316 if implement.object ~= nil then
2317 implement.object:registerActionEvents(selectedObject.vehicle)
2318 end
2319 end
2320 end
2321end

registerEventListeners

Description
Definition
registerEventListeners()
Code
149function AttacherJoints.registerEventListeners(vehicleType)
150 SpecializationUtil.registerEventListener(vehicleType, "onLoad", AttacherJoints)
151 SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", AttacherJoints)
152 SpecializationUtil.registerEventListener(vehicleType, "onPreDelete", AttacherJoints)
153 SpecializationUtil.registerEventListener(vehicleType, "onDelete", AttacherJoints)
154 SpecializationUtil.registerEventListener(vehicleType, "onReadStream", AttacherJoints)
155 SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", AttacherJoints)
156 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", AttacherJoints)
157 SpecializationUtil.registerEventListener(vehicleType, "onPostUpdate", AttacherJoints)
158 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", AttacherJoints)
159 SpecializationUtil.registerEventListener(vehicleType, "onDraw", AttacherJoints)
160 SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", AttacherJoints)
161 SpecializationUtil.registerEventListener(vehicleType, "onStateChange", AttacherJoints)
162 SpecializationUtil.registerEventListener(vehicleType, "onLightsTypesMaskChanged", AttacherJoints)
163 SpecializationUtil.registerEventListener(vehicleType, "onTurnLightStateChanged", AttacherJoints)
164 SpecializationUtil.registerEventListener(vehicleType, "onBrakeLightsVisibilityChanged", AttacherJoints)
165 SpecializationUtil.registerEventListener(vehicleType, "onReverseLightsVisibilityChanged", AttacherJoints)
166 SpecializationUtil.registerEventListener(vehicleType, "onBeaconLightsVisibilityChanged", AttacherJoints)
167 SpecializationUtil.registerEventListener(vehicleType, "onBrake", AttacherJoints)
168 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOn", AttacherJoints)
169 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOff", AttacherJoints)
170 SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", AttacherJoints)
171 SpecializationUtil.registerEventListener(vehicleType, "onActivate", AttacherJoints)
172 SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", AttacherJoints)
173 SpecializationUtil.registerEventListener(vehicleType, "onReverseDirectionChanged", AttacherJoints)
174end

registerEvents

Description
Definition
registerEvents()
Code
67function AttacherJoints.registerEvents(vehicleType)
68 SpecializationUtil.registerEvent(vehicleType, "onPreAttachImplement")
69 SpecializationUtil.registerEvent(vehicleType, "onPostAttachImplement")
70 SpecializationUtil.registerEvent(vehicleType, "onPreDetachImplement")
71 SpecializationUtil.registerEvent(vehicleType, "onPostDetachImplement")
72end

registerFunctions

Description
Definition
registerFunctions()
Code
76function AttacherJoints.registerFunctions(vehicleType)
77 SpecializationUtil.registerFunction(vehicleType, "saveAttachmentsToXMLFile", AttacherJoints.saveAttachmentsToXMLFile)
78 SpecializationUtil.registerFunction(vehicleType, "loadAttachmentsFromXMLFile", AttacherJoints.loadAttachmentsFromXMLFile)
79 SpecializationUtil.registerFunction(vehicleType, "handleLowerImplementEvent", AttacherJoints.handleLowerImplementEvent)
80 SpecializationUtil.registerFunction(vehicleType, "handleLowerImplementByAttacherJointIndex", AttacherJoints.handleLowerImplementByAttacherJointIndex)
81 SpecializationUtil.registerFunction(vehicleType, "getAttachedImplements", AttacherJoints.getAttachedImplements)
82 SpecializationUtil.registerFunction(vehicleType, "getAttacherJoints", AttacherJoints.getAttacherJoints)
83 SpecializationUtil.registerFunction(vehicleType, "getAttacherJointByJointDescIndex", AttacherJoints.getAttacherJointByJointDescIndex)
84 SpecializationUtil.registerFunction(vehicleType, "getImplementFromAttacherJointIndex", AttacherJoints.getImplementFromAttacherJointIndex)
85 SpecializationUtil.registerFunction(vehicleType, "getAttacherJointIndexFromObject", AttacherJoints.getAttacherJointIndexFromObject)
86 SpecializationUtil.registerFunction(vehicleType, "getAttacherJointDescFromObject", AttacherJoints.getAttacherJointDescFromObject)
87 SpecializationUtil.registerFunction(vehicleType, "getAttacherJointIndexFromImplementIndex", AttacherJoints.getAttacherJointIndexFromImplementIndex)
88 SpecializationUtil.registerFunction(vehicleType, "getObjectFromImplementIndex", AttacherJoints.getObjectFromImplementIndex)
89 SpecializationUtil.registerFunction(vehicleType, "updateAttacherJointGraphics", AttacherJoints.updateAttacherJointGraphics)
90 SpecializationUtil.registerFunction(vehicleType, "calculateAttacherJointMoveUpperLowerAlpha", AttacherJoints.calculateAttacherJointMoveUpperLowerAlpha)
91 SpecializationUtil.registerFunction(vehicleType, "updateAttacherJointRotation", AttacherJoints.updateAttacherJointRotation)
92 SpecializationUtil.registerFunction(vehicleType, "updateAttacherJointRotationNodes", AttacherJoints.updateAttacherJointRotationNodes)
93 SpecializationUtil.registerFunction(vehicleType, "attachImplement", AttacherJoints.attachImplement)
94 SpecializationUtil.registerFunction(vehicleType, "postAttachImplement", AttacherJoints.postAttachImplement)
95 SpecializationUtil.registerFunction(vehicleType, "createAttachmentJoint", AttacherJoints.createAttachmentJoint)
96 SpecializationUtil.registerFunction(vehicleType, "hardAttachImplement", AttacherJoints.hardAttachImplement)
97 SpecializationUtil.registerFunction(vehicleType, "hardDetachImplement", AttacherJoints.hardDetachImplement)
98 SpecializationUtil.registerFunction(vehicleType, "detachImplement", AttacherJoints.detachImplement)
99 SpecializationUtil.registerFunction(vehicleType, "detachImplementByObject", AttacherJoints.detachImplementByObject)
100 SpecializationUtil.registerFunction(vehicleType, "playAttachSound", AttacherJoints.playAttachSound)
101 SpecializationUtil.registerFunction(vehicleType, "playDetachSound", AttacherJoints.playDetachSound)
102 SpecializationUtil.registerFunction(vehicleType, "detachingIsPossible", AttacherJoints.detachingIsPossible)
103 SpecializationUtil.registerFunction(vehicleType, "getImplementIndexByJointDescIndex", AttacherJoints.getImplementIndexByJointDescIndex)
104 SpecializationUtil.registerFunction(vehicleType, "getImplementByJointDescIndex", AttacherJoints.getImplementByJointDescIndex)
105 SpecializationUtil.registerFunction(vehicleType, "getImplementIndexByObject", AttacherJoints.getImplementIndexByObject)
106 SpecializationUtil.registerFunction(vehicleType, "getImplementByObject", AttacherJoints.getImplementByObject)
107 SpecializationUtil.registerFunction(vehicleType, "callFunctionOnAllImplements", AttacherJoints.callFunctionOnAllImplements)
108 SpecializationUtil.registerFunction(vehicleType, "activateAttachments", AttacherJoints.activateAttachments)
109 SpecializationUtil.registerFunction(vehicleType, "deactivateAttachments", AttacherJoints.deactivateAttachments)
110 SpecializationUtil.registerFunction(vehicleType, "deactivateAttachmentsLights", AttacherJoints.deactivateAttachmentsLights)
111 SpecializationUtil.registerFunction(vehicleType, "setJointMoveDown", AttacherJoints.setJointMoveDown)
112 SpecializationUtil.registerFunction(vehicleType, "getIsHardAttachAllowed", AttacherJoints.getIsHardAttachAllowed)
113 SpecializationUtil.registerFunction(vehicleType, "loadAttacherJointFromXML", AttacherJoints.loadAttacherJointFromXML)
114 SpecializationUtil.registerFunction(vehicleType, "setSelectedImplementByObject", AttacherJoints.setSelectedImplementByObject)
115 SpecializationUtil.registerFunction(vehicleType, "getSelectedImplement", AttacherJoints.getSelectedImplement)
116 SpecializationUtil.registerFunction(vehicleType, "getCanToggleAttach", AttacherJoints.getCanToggleAttach)
117 SpecializationUtil.registerFunction(vehicleType, "startAttacherJointCombo", AttacherJoints.startAttacherJointCombo)
118 SpecializationUtil.registerFunction(vehicleType, "registerSelfLoweringActionEvent", AttacherJoints.registerSelfLoweringActionEvent)
119end

registerJointType

Description
Registration of attacher joint type
Definition
registerJointType(string name)
Arguments
stringnamename if attacher type
Code
32function AttacherJoints.registerJointType(name)
33 local key = "JOINTTYPE_"..string.upper(name)
34 if AttacherJoints[key] == nil then
35 AttacherJoints.NUM_JOINTTYPES = AttacherJoints.NUM_JOINTTYPES+1
36 AttacherJoints[key] = AttacherJoints.NUM_JOINTTYPES
37 AttacherJoints.jointTypeNameToInt[name] = AttacherJoints.NUM_JOINTTYPES
38 end
39end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
123function AttacherJoints.registerOverwrittenFunctions(vehicleType)
124 SpecializationUtil.registerOverwrittenFunction(vehicleType, "raiseActive", AttacherJoints.raiseActive)
125 SpecializationUtil.registerOverwrittenFunction(vehicleType, "registerActionEvents", AttacherJoints.registerActionEvents)
126 SpecializationUtil.registerOverwrittenFunction(vehicleType, "removeActionEvents", AttacherJoints.removeActionEvents)
127 SpecializationUtil.registerOverwrittenFunction(vehicleType, "addToPhysics", AttacherJoints.addToPhysics)
128 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getTotalMass", AttacherJoints.getTotalMass)
129 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getChildVehicles", AttacherJoints.getChildVehicles)
130 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAirConsumerUsage", AttacherJoints.getAirConsumerUsage)
131 SpecializationUtil.registerOverwrittenFunction(vehicleType, "addVehicleToAIImplementList", AttacherJoints.addVehicleToAIImplementList)
132 SpecializationUtil.registerOverwrittenFunction(vehicleType, "addToTotalVehicleList", AttacherJoints.addToTotalVehicleList)
133 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDirectionSnapAngle", AttacherJoints.getDirectionSnapAngle)
134 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAICollisionTriggers", AttacherJoints.getAICollisionTriggers)
135 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getFillLevelInformation", AttacherJoints.getFillLevelInformation)
136 SpecializationUtil.registerOverwrittenFunction(vehicleType, "attachableAddToolCameras", AttacherJoints.attachableAddToolCameras)
137 SpecializationUtil.registerOverwrittenFunction(vehicleType, "attachableRemoveToolCameras", AttacherJoints.attachableRemoveToolCameras)
138 SpecializationUtil.registerOverwrittenFunction(vehicleType, "registerSelectableObjects", AttacherJoints.registerSelectableObjects)
139 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsReadyForAutomatedTrainTravel", AttacherJoints.getIsReadyForAutomatedTrainTravel)
140 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadDashboardGroupFromXML", AttacherJoints.loadDashboardGroupFromXML)
141 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsDashboardGroupActive", AttacherJoints.getIsDashboardGroupActive)
142 SpecializationUtil.registerOverwrittenFunction(vehicleType, "isDetachAllowed", AttacherJoints.isDetachAllowed)
143 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsFoldAllowed", AttacherJoints.getIsFoldAllowed)
144 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsWheelFoliageDestructionAllowed", AttacherJoints.getIsWheelFoliageDestructionAllowed)
145end

registerSelectableObjects

Description
Definition
registerSelectableObjects()
Code
2501function AttacherJoints:registerSelectableObjects(superFunc, selectableObjects)
2502 superFunc(self, selectableObjects)
2503
2504 local spec = self.spec_attacherJoints
2505 for _,implement in pairs(spec.attachedImplements) do
2506 local object = implement.object
2507 if object ~= nil then
2508 object:registerSelectableObjects(selectableObjects)
2509 end
2510 end
2511end

registerSelfLoweringActionEvent

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

removeActionEvents

Description
Definition
removeActionEvents()
Code
2325function AttacherJoints:removeActionEvents(superFunc)
2326 local spec = self.spec_attacherJoints
2327
2328 superFunc(self)
2329
2330 for _,implement in pairs(spec.attachedImplements) do
2331 if implement.object ~= nil then
2332 implement.object:removeActionEvents()
2333 end
2334 end
2335end

saveAttachmentsToXMLFile

Description
Definition
saveAttachmentsToXMLFile()
Code
330function AttacherJoints:saveAttachmentsToXMLFile(xmlFile, key, vehiclesToId)
331 local spec = self.spec_attacherJoints
332 local added = false
333
334 local id = vehiclesToId[self]
335 if id ~= nil then
336 local i = 0
337 for _, implement in ipairs(spec.attachedImplements) do
338 local object = implement.object
339 if object ~= nil and vehiclesToId[object] ~= nil then
340 local attachmentKey = string.format("%s.attachment(%d)", key, i)
341 local jointDescIndex = implement.jointDescIndex
342 local jointDesc = spec.attacherJoints[jointDescIndex]
343 local inputJointDescIndex = object:getActiveInputAttacherJointDescIndex()
344 setXMLInt(xmlFile, attachmentKey.."#attachmentId", vehiclesToId[object])
345 setXMLInt(xmlFile, attachmentKey.."#inputJointDescIndex", inputJointDescIndex)
346 setXMLInt(xmlFile, attachmentKey.."#jointIndex", jointDescIndex)
347 setXMLBool(xmlFile, attachmentKey.."#moveDown", jointDesc.moveDown)
348 added = true
349 i = i + 1
350 end
351 end
352
353 if added then
354 setXMLInt(xmlFile, key.."#rootVehicleId", id)
355 end
356 end
357
358 return added
359end

saveToXMLFile

Description
Definition
saveToXMLFile()
Code
321function AttacherJoints:saveToXMLFile(xmlFile, key, usedModNames)
322 local spec = self.spec_attacherJoints
323 if spec.attacherJointCombos ~= nil then
324 setXMLInt(xmlFile, key.."#comboDirection", spec.attacherJointCombos.direction)
325 end
326end

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
1862function AttacherJoints:setJointMoveDown(jointDescIndex, moveDown, noEventSend)
1863 local spec = self.spec_attacherJoints
1864
1865 VehicleLowerImplementEvent.sendEvent(self, jointDescIndex, moveDown, noEventSend)
1866 local jointDesc = spec.attacherJoints[jointDescIndex]
1867 jointDesc.moveDown = moveDown
1868
1869 local implementIndex = self:getImplementIndexByJointDescIndex(jointDescIndex)
1870 if implementIndex ~= nil then
1871 local implement = spec.attachedImplements[implementIndex]
1872 if implement.object ~= nil then
1873 implement.object:setLowered(moveDown)
1874 end
1875 end
1876
1877 return true
1878end

setSelectedImplementByObject

Description
Definition
setSelectedImplementByObject()
Code
1649function AttacherJoints:setSelectedImplementByObject(object)
1650 self.spec_attacherJoints.selectedImplement = self:getImplementByObject(object)
1651end

startAttacherJointCombo

Description
Definition
startAttacherJointCombo()
Code
1676function AttacherJoints:startAttacherJointCombo(force)
1677 local spec = self.spec_attacherJoints
1678
1679 if not spec.attacherJointCombos.isRunning or force then
1680 spec.attacherJointCombos.direction = -spec.attacherJointCombos.direction
1681 spec.attacherJointCombos.isRunning = true
1682 end
1683end

updateAttacherJointGraphics

Description
Update attacher joint graphics
Definition
updateAttacherJointGraphics(table implement, float dt)
Arguments
tableimplementimplement
floatdttime since last call in ms
Code
860function AttacherJoints:updateAttacherJointGraphics(implement, dt)
861 local spec = self.spec_attacherJoints
862
863 if implement.object ~= nil then
864 local jointDesc = spec.attacherJoints[implement.jointDescIndex]
865
866 local attacherJoint = implement.object:getActiveInputAttacherJoint()
867
868 if jointDesc.topArm ~= nil and attacherJoint.topReferenceNode ~= nil then
869 local ax, ay, az = getWorldTranslation(jointDesc.topArm.rotationNode)
870 local bx, by, bz = getWorldTranslation(attacherJoint.topReferenceNode)
871
872 local x, y, z = worldDirectionToLocal(getParent(jointDesc.topArm.rotationNode), bx-ax, by-ay, bz-az)
873 local distance = MathUtil.vector3Length(x,y,z)
874
875 local _, upY, upZ = 0,1,0
876 if math.abs(y) > 0.99*distance then
877 -- direction and up is parallel
878 upY = 0
879 if y > 0 then
880 upZ = 1
881 else
882 upZ = -1
883 end
884 end
885
886 -- different approach I) rotate actual direction of topArm by 90degree around x-axis
887 local alpha = math.rad(-90)
888 -- check if rotationNode is at back of tractor => inverted rotation direction, could be dismissed by rotating TG in i3d
889 local px,py,pz = getWorldTranslation(jointDesc.topArm.rotationNode)
890 local _,_,lz = worldToLocal(self.components[1].node, px,py,pz)
891 if lz < 0 then
892 alpha = math.rad(90)
893 end
894
895 local dx, dy, dz = localDirectionToWorld(jointDesc.topArm.rotationNode, 0,0,1)
896 dx, dy, dz = worldDirectionToLocal(getParent(jointDesc.topArm.rotationNode), dx, dy, dz)
897 local upX = dx
898 local upY = math.cos(alpha)*dy - math.sin(alpha)*dz
899 local upZ = math.sin(alpha)*dy + math.cos(alpha)*dz
900
901 setDirection(jointDesc.topArm.rotationNode, x*jointDesc.topArm.zScale, y*jointDesc.topArm.zScale, z*jointDesc.topArm.zScale, upX, upY, upZ)
902 if jointDesc.topArm.translationNode ~= nil and not implement.attachingIsInProgress then
903 local translation = (distance-jointDesc.topArm.referenceDistance)
904 setTranslation(jointDesc.topArm.translationNode, 0, 0, translation*jointDesc.topArm.zScale)
905 if jointDesc.topArm.scaleNode ~= nil then
906 setScale(jointDesc.topArm.scaleNode, 1, 1, math.max((translation+jointDesc.topArm.scaleReferenceDistance)/jointDesc.topArm.scaleReferenceDistance, 0))
907 end
908 end
909 end
910 if jointDesc.bottomArm ~= nil then
911 local ax, ay, az = getWorldTranslation(jointDesc.bottomArm.rotationNode)
912 local bx, by, bz = getWorldTranslation(attacherJoint.node)
913
914 local x, y, z = worldDirectionToLocal(getParent(jointDesc.bottomArm.rotationNode), bx-ax, by-ay, bz-az)
915 local distance = MathUtil.vector3Length(x,y,z)
916 local upX, upY, upZ = 0,1,0
917 if math.abs(y) > 0.99*distance then
918 -- direction and up is parallel
919 upY = 0
920 if y > 0 then
921 upZ = 1
922 else
923 upZ = -1
924 end
925 end
926 local dirX = 0
927 if not jointDesc.bottomArm.lockDirection then
928 dirX = x*jointDesc.bottomArm.zScale
929 end
930 if math.abs(jointDesc.bottomArm.lastDirection[1] - dirX) > 0.001 or
931 math.abs(jointDesc.bottomArm.lastDirection[2] - y*jointDesc.bottomArm.zScale) > 0.001 or
932 math.abs(jointDesc.bottomArm.lastDirection[3] - z*jointDesc.bottomArm.zScale) > 0.001 then
933 setDirection(jointDesc.bottomArm.rotationNode, dirX, y*jointDesc.bottomArm.zScale, z*jointDesc.bottomArm.zScale, upX, upY, upZ)
934
935 jointDesc.bottomArm.lastDirection[1] = dirX
936 jointDesc.bottomArm.lastDirection[2] = y*jointDesc.bottomArm.zScale
937 jointDesc.bottomArm.lastDirection[3] = z*jointDesc.bottomArm.zScale
938 end
939
940 if jointDesc.bottomArm.translationNode ~= nil and not implement.attachingIsInProgress then
941 setTranslation(jointDesc.bottomArm.translationNode, 0, 0, (distance-jointDesc.bottomArm.referenceDistance)*jointDesc.bottomArm.zScale)
942 end
943 if self.setMovingToolDirty ~= nil then
944 self:setMovingToolDirty(jointDesc.bottomArm.rotationNode)
945 end
946
947 if attacherJoint.needsToolbar and jointDesc.bottomArm.toolbar ~= nil then
948 local parent = getParent(jointDesc.bottomArm.toolbar)
949
950 local _, yDir, zDir = localDirectionToLocal(attacherJoint.node, jointDesc.rootNode, 1, 0, 0)
951 local xDir, yDir, zDir = localDirectionToLocal(jointDesc.rootNode, parent, 0, yDir, zDir)
952
953 local _, yUp, zUp = localDirectionToLocal(attacherJoint.node, jointDesc.rootNode, 0, 1, 0)
954 local xUp, yUp, zUp = localDirectionToLocal(jointDesc.rootNode, parent, 0, yUp, zUp)
955
956 setDirection(jointDesc.bottomArm.toolbar, xDir, yDir, zDir, xUp, yUp, zUp)
957 end
958 end
959 end
960end

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
1015function AttacherJoints:updateAttacherJointRotation(jointDesc, object)
1016 local objectAtttacherJoint = object.spec_attachable.attacherJoint
1017
1018 -- rotate attacher such that
1019 local targetRot = MathUtil.lerp(objectAtttacherJoint.upperRotationOffset, objectAtttacherJoint.lowerRotationOffset, jointDesc.moveAlpha)
1020 local curRot = MathUtil.lerp(jointDesc.upperRotationOffset, jointDesc.lowerRotationOffset, jointDesc.moveAlpha)
1021 local rotDiff = targetRot - curRot
1022
1023 setRotation(jointDesc.jointTransform, unpack(jointDesc.jointOrigRot))
1024 rotateAboutLocalAxis(jointDesc.jointTransform, rotDiff, 0, 0, 1)
1025end

updateAttacherJointRotationNodes

Description
Definition
updateAttacherJointRotationNodes()
Code
1029function AttacherJoints:updateAttacherJointRotationNodes(jointDesc, alpha)
1030 if jointDesc.rotationNode ~= nil then
1031 setRotation(jointDesc.rotationNode, MathUtil.vector3ArrayLerp(jointDesc.upperRotation, jointDesc.lowerRotation, alpha))
1032 end
1033 if jointDesc.rotationNode2 ~= nil then
1034 setRotation(jointDesc.rotationNode2, MathUtil.vector3ArrayLerp(jointDesc.upperRotation2, jointDesc.lowerRotation2, alpha))
1035 end
1036end