386 | function DynamicMountAttacher:addDynamicMountedObject(object) |
387 | local spec = self.spec_dynamicMountAttacher |
388 | |
389 | if spec.dynamicMountedObjects[object] == nil then |
390 | spec.dynamicMountedObjects[object] = object |
391 | |
392 | local lockedToPosition = false |
393 | if object.getMountableLockPositions ~= nil then |
394 | local lockPositions = object:getMountableLockPositions() |
395 | for i=1, #lockPositions do |
396 | local position = lockPositions[i] |
397 | if string.endsWith(self.configFileName, position.xmlFilename) then |
398 | local jointNode = I3DUtil.indexToObject(self.components, position.jointNode, self.i3dMappings) |
399 | if jointNode ~= nil then |
400 | local x, y, z = localToWorld(jointNode, position.transOffset[1], position.transOffset[2], position.transOffset[3]) |
401 | local rx, ry, rz = localRotationToWorld(jointNode, position.rotOffset[1], position.rotOffset[2], position.rotOffset[3]) |
402 | self:lockDynamicMountedObject(object, x, y, z, rx, ry, rz) |
403 | |
404 | lockedToPosition = true |
405 | break |
406 | end |
407 | end |
408 | end |
409 | end |
410 | |
411 | if not lockedToPosition then |
412 | for i=1, #spec.lockPositions do |
413 | local position = spec.lockPositions[i] |
414 | if string.endsWith(object.configFileName, position.xmlFilename) then |
415 | local x, y, z = getWorldTranslation(position.jointNode) |
416 | local rx, ry, rz = getWorldRotation(position.jointNode) |
417 | self:lockDynamicMountedObject(object, x, y, z, rx, ry, rz) |
418 | |
419 | ObjectChangeUtil.setObjectChanges(position.objectChanges, true, self, self.setMovingToolDirty) |
420 | |
421 | break |
422 | end |
423 | end |
424 | end |
425 | |
426 | for _, info in pairs(spec.dynamicMountCollisionMasks) do |
427 | setCollisionMask(info.node, info.mountedCollisionMask) |
428 | end |
429 | |
430 | if spec.transferMass then |
431 | if object.setReducedComponentMass ~= nil then |
432 | object:setReducedComponentMass(true) |
433 | self:setMassDirty() |
434 | end |
435 | end |
436 | |
437 | self:setDynamicMountAnimationState(true) |
438 | |
439 | self:raiseDirtyFlags(spec.dynamicMountedObjectsDirtyFlag) |
440 | end |
441 | end |
530 | function DynamicMountAttacher:dynamicMountTriggerCallback(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId) |
531 | local spec = self.spec_dynamicMountAttacher |
532 | |
533 | if getRigidBodyType(otherActorId) == RigidBodyType.DYNAMIC |
534 | and not getHasTrigger(otherActorId) then |
535 | if onEnter then |
536 | local object = g_currentMission:getNodeObject(otherActorId) |
537 | if object == nil then |
538 | object = g_currentMission.nodeToObject[otherActorId] |
539 | end |
540 | if object == self.rootVehicle or (self.spec_attachable ~= nil and self.spec_attachable.attacherVehicle == object) then |
541 | object = nil |
542 | end |
543 | if object ~= nil and object ~= self then |
544 | -- is a mountable object (e.g. bales) |
545 | local isObject = object.getSupportsMountDynamic ~= nil and object:getSupportsMountDynamic() and object.lastMoveTime ~= nil |
546 | |
547 | -- is a mountable vehicle (e.g. pallets) |
548 | local isVehicle = object.getSupportsTensionBelts ~= nil and object:getSupportsTensionBelts() and object.lastMoveTime ~= nil |
549 | |
550 | if isObject or isVehicle then |
551 | spec.pendingDynamicMountObjects[object] = Utils.getNoNil(spec.pendingDynamicMountObjects[object], 0) + 1 |
552 | |
553 | if spec.pendingDynamicMountObjects[object] == 1 then |
554 | self:raiseDirtyFlags(spec.dynamicMountedObjectsDirtyFlag) |
555 | end |
556 | end |
557 | end |
558 | elseif onLeave then |
559 | local object = g_currentMission:getNodeObject(otherActorId) |
560 | if object == nil then |
561 | object = g_currentMission.nodeToObject[otherActorId] |
562 | end |
563 | if object ~= nil then |
564 | if spec.pendingDynamicMountObjects[object] ~= nil then |
565 | local count = spec.pendingDynamicMountObjects[object]-1 |
566 | if count == 0 then |
567 | spec.pendingDynamicMountObjects[object] = nil |
568 | |
569 | if spec.dynamicMountedObjects[object] ~= nil then |
570 | self:removeDynamicMountedObject(object, false) |
571 | object:unmountDynamic() |
572 | |
573 | if object.additionalDynamicMountJointNode ~= nil then |
574 | delete(object.additionalDynamicMountJointNode) |
575 | object.additionalDynamicMountJointNode = nil |
576 | end |
577 | end |
578 | |
579 | self:raiseDirtyFlags(spec.dynamicMountedObjectsDirtyFlag) |
580 | else |
581 | spec.pendingDynamicMountObjects[object] = count |
582 | end |
583 | end |
584 | end |
585 | end |
586 | end |
587 | end |
23 | function DynamicMountAttacher.initSpecialization() |
24 | local schema = Vehicle.xmlSchema |
25 | schema:setXMLSpecializationType("DynamicMountAttacher") |
26 | |
27 | schema:register(XMLValueType.NODE_INDEX, "vehicle.dynamicMountAttacher#node", "Attacher node") |
28 | schema:register(XMLValueType.FLOAT, "vehicle.dynamicMountAttacher#forceLimitScale", "Force limit", 1) |
29 | schema:register(XMLValueType.FLOAT, "vehicle.dynamicMountAttacher#timeToMount", "No movement time until mounting", 1000) |
30 | schema:register(XMLValueType.INT, "vehicle.dynamicMountAttacher#numObjectBits", "Number of object bits to sync", 5) |
31 | |
32 | schema:register(XMLValueType.STRING, "vehicle.dynamicMountAttacher.grab#openMountType", "Open mount type", "TYPE_FORK") |
33 | schema:register(XMLValueType.STRING, "vehicle.dynamicMountAttacher.grab#closedMountType", "Closed mount type", "TYPE_AUTO_ATTACH_XYZ") |
34 | |
35 | schema:register(XMLValueType.NODE_INDEX, "vehicle.dynamicMountAttacher.mountCollisionMask(?)#node", "Collision node") |
36 | schema:register(XMLValueType.NODE_INDEX, "vehicle.dynamicMountAttacher.mountCollisionMask(?)#triggerNode", "Trigger node") |
37 | schema:register(XMLValueType.STRING, "vehicle.dynamicMountAttacher.mountCollisionMask(?)#mountType", "Mount type name", "FORK") |
38 | schema:register(XMLValueType.FLOAT, "vehicle.dynamicMountAttacher.mountCollisionMask(?)#forceLimitScale", "Force limit", 1) |
39 | schema:register(XMLValueType.INT, "vehicle.dynamicMountAttacher.mountCollisionMask(?)#collisionMask", "Collision mask while object mounted") |
40 | |
41 | schema:register(XMLValueType.NODE_INDEX, "vehicle.dynamicMountAttacher#triggerNode", "Trigger node") |
42 | schema:register(XMLValueType.NODE_INDEX, "vehicle.dynamicMountAttacher#rootNode", "Root node") |
43 | schema:register(XMLValueType.NODE_INDEX, "vehicle.dynamicMountAttacher#jointNode", "Joint node") |
44 | schema:register(XMLValueType.FLOAT, "vehicle.dynamicMountAttacher#forceAcceleration", "Force acceleration", 30) |
45 | schema:register(XMLValueType.STRING, "vehicle.dynamicMountAttacher#mountType", "Mount type", "TYPE_AUTO_ATTACH_XZ") |
46 | |
47 | schema:register(XMLValueType.BOOL, "vehicle.dynamicMountAttacher#transferMass", "If this is set to 'true' the mass of the object to mount is tranfered to our own component. This improves phyiscs stability", false) |
48 | |
49 | schema:register(XMLValueType.STRING, "vehicle.dynamicMountAttacher.lockPosition(?)#xmlFilename", "XML filename of vehicle to lock (needs to match only the end of the filename)") |
50 | schema:register(XMLValueType.NODE_INDEX, "vehicle.dynamicMountAttacher.lockPosition(?)#jointNode", "Joint node (Representens the position of the other vehicles root node)") |
51 | |
52 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.dynamicMountAttacher.lockPosition(?)") |
53 | |
54 | schema:register(XMLValueType.STRING, "vehicle.dynamicMountAttacher.animation#name", "Animation name") |
55 | schema:register(XMLValueType.FLOAT, "vehicle.dynamicMountAttacher.animation#speed", "Animation speed", 1) |
56 | |
57 | schema:register(XMLValueType.BOOL, Cylindered.MOVING_TOOL_XML_KEY .. ".dynamicMountAttacher#value", "Update dynamic mount attacher joints") |
58 | schema:register(XMLValueType.BOOL, Cylindered.MOVING_PART_XML_KEY .. ".dynamicMountAttacher#value", "Update dynamic mount attacher joints") |
59 | |
60 | schema:setXMLSpecializationType() |
61 | end |
109 | function DynamicMountAttacher:onLoad(savegame) |
110 | local spec = self.spec_dynamicMountAttacher |
111 | |
112 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.dynamicMountAttacher#index", "vehicle.dynamicMountAttacher#node") --FS17 to FS19 |
113 | |
114 | -- Allow mountable object to attach them selfs to us |
115 | spec.dynamicMountAttacherNode = self.xmlFile:getValue("vehicle.dynamicMountAttacher#node", nil, self.components, self.i3dMappings) |
116 | spec.dynamicMountAttacherForceLimitScale = self.xmlFile:getValue("vehicle.dynamicMountAttacher#forceLimitScale", 1) |
117 | spec.dynamicMountAttacherTimeToMount = self.xmlFile:getValue("vehicle.dynamicMountAttacher#timeToMount", 1000) |
118 | spec.numObjectBits = self.xmlFile:getValue("vehicle.dynamicMountAttacher#numObjectBits", 5) |
119 | spec.maxNumObjectsToSend = 2 ^ spec.numObjectBits - 1 |
120 | |
121 | local grabKey = "vehicle.dynamicMountAttacher.grab" |
122 | if self.xmlFile:hasProperty(grabKey) then |
123 | spec.dynamicMountAttacherGrab = {} |
124 | self:loadDynamicMountGrabFromXML(self.xmlFile, grabKey, spec.dynamicMountAttacherGrab) |
125 | end |
126 | |
127 | spec.pendingDynamicMountObjects = {} |
128 | spec.dynamicMountCollisionMasks = {} |
129 | spec.lockPositions = {} |
130 | |
131 | if self.isServer then |
132 | self.xmlFile:iterate("vehicle.dynamicMountAttacher.mountCollisionMask", function(index, key) |
133 | local mountCollision = {} |
134 | mountCollision.node = self.xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings) |
135 | mountCollision.triggerNode = self.xmlFile:getValue(key.."#triggerNode", nil, self.components, self.i3dMappings) |
136 | mountCollision.mountedCollisionMask = self.xmlFile:getValue(key.."#collisionMask") |
137 | if mountCollision.node ~= nil and mountCollision.mountedCollisionMask ~= nil then |
138 | local mountTypeStr = self.xmlFile:getValue(key.."#mountType", "FORK") |
139 | mountCollision.mountType = DynamicMountUtil["TYPE_" .. mountTypeStr] or DynamicMountUtil.TYPE_FORK |
140 | mountCollision.forceLimitScale = self.xmlFile:getValue(key .. "#forceLimitScale", spec.dynamicMountAttacherForceLimitScale) |
141 | |
142 | mountCollision.unmountedCollisionMask = getCollisionMask(mountCollision.node) |
143 | |
144 | table.insert(spec.dynamicMountCollisionMasks, mountCollision) |
145 | else |
146 | Logging.xmlWarning(self.xmlFile, "Missing node or collisionMask in '%s'", key) |
147 | end |
148 | end) |
149 | |
150 | local dynamicMountTrigger = {} |
151 | dynamicMountTrigger.triggerNode = self.xmlFile:getValue("vehicle.dynamicMountAttacher#triggerNode", nil, self.components, self.i3dMappings) |
152 | dynamicMountTrigger.rootNode = self.xmlFile:getValue("vehicle.dynamicMountAttacher#rootNode", nil, self.components, self.i3dMappings) |
153 | dynamicMountTrigger.jointNode = self.xmlFile:getValue("vehicle.dynamicMountAttacher#jointNode", nil, self.components, self.i3dMappings) |
154 | if dynamicMountTrigger.triggerNode ~= nil and dynamicMountTrigger.rootNode ~= nil and dynamicMountTrigger.jointNode ~= nil then |
155 | local collisionMask = getCollisionMask(dynamicMountTrigger.triggerNode) |
156 | if collisionMask == CollisionMask.TRIGGER_DYNAMIC_MOUNT then |
157 | addTrigger(dynamicMountTrigger.triggerNode, "dynamicMountTriggerCallback", self) |
158 | |
159 | dynamicMountTrigger.forceAcceleration = self.xmlFile:getValue("vehicle.dynamicMountAttacher#forceAcceleration", 30) |
160 | local mountTypeString = self.xmlFile:getValue("vehicle.dynamicMountAttacher#mountType", "TYPE_AUTO_ATTACH_XZ") |
161 | dynamicMountTrigger.mountType = Utils.getNoNil(DynamicMountUtil[mountTypeString], DynamicMountUtil.TYPE_AUTO_ATTACH_XZ) |
162 | dynamicMountTrigger.currentMountType = dynamicMountTrigger.mountType |
163 | dynamicMountTrigger.component = self:getParentComponent(dynamicMountTrigger.triggerNode) |
164 | |
165 | spec.dynamicMountAttacherTrigger = dynamicMountTrigger |
166 | else |
167 | Logging.xmlWarning(self.xmlFile, "Dynamic Mount trigger has invalid collision mask (should be %d)!", CollisionMask.TRIGGER_DYNAMIC_MOUNT) |
168 | end |
169 | end |
170 | |
171 | spec.transferMass = self.xmlFile:getValue("vehicle.dynamicMountAttacher#transferMass", false) |
172 | |
173 | self.xmlFile:iterate("vehicle.dynamicMountAttacher.lockPosition", function(index, key) |
174 | local entry = {} |
175 | |
176 | entry.xmlFilename = self.xmlFile:getValue(key .. "#xmlFilename") |
177 | entry.jointNode = self.xmlFile:getValue(key .. "#jointNode", nil, self.components, self.i3dMappings) |
178 | if entry.xmlFilename ~= nil and entry.jointNode ~= nil then |
179 | entry.xmlFilename = entry.xmlFilename:gsub("$data", "data") |
180 | |
181 | entry.objectChanges = {} |
182 | ObjectChangeUtil.loadObjectChangeFromXML(self.xmlFile, key, entry.objectChanges, self.components, self) |
183 | |
184 | table.insert(spec.lockPositions, entry) |
185 | else |
186 | Logging.xmlWarning(self.xmlFile, "Invalid lock position '%s'. Missing xmlFilename or jointNode!", key) |
187 | end |
188 | end) |
189 | end |
190 | |
191 | spec.animationName = self.xmlFile:getValue("vehicle.dynamicMountAttacher.animation#name") |
192 | spec.animationSpeed = self.xmlFile:getValue("vehicle.dynamicMountAttacher.animation#speed", 1) |
193 | if spec.animationName ~= nil then |
194 | self:playAnimation(spec.animationName, spec.animationSpeed, self:getAnimationTime(spec.animationName), true) |
195 | end |
196 | |
197 | spec.dynamicMountedObjects = {} |
198 | spec.dynamicMountedObjectsDirtyFlag = self:getNextDirtyFlag() |
199 | end |
246 | function DynamicMountAttacher:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
247 | if self.isServer then |
248 | local spec = self.spec_dynamicMountAttacher |
249 | if self:getAllowDynamicMountObjects() then |
250 | for object,_ in pairs(spec.pendingDynamicMountObjects) do |
251 | -- raise active as long as we got pending objects to give the objects a chance to be mounted if they are not moving |
252 | self:raiseActive() |
253 | |
254 | if spec.dynamicMountedObjects[object] == nil then |
255 | if object.lastMoveTime + self:getDynamicMountTimeToMount() < g_currentMission.time then |
256 | local doAttach = false |
257 | local objectRoot |
258 | if object.components ~= nil then |
259 | if object.getCanByMounted ~= nil then |
260 | doAttach = object:getCanByMounted() |
261 | elseif entityExists(object.components[1].node) then |
262 | doAttach = true |
263 | end |
264 | |
265 | objectRoot = object.components[1].node |
266 | end |
267 | if object.nodeId ~= nil then |
268 | if object.getCanByMounted ~= nil then |
269 | doAttach = object:getCanByMounted() |
270 | elseif entityExists(object.nodeId) then |
271 | doAttach = true |
272 | end |
273 | objectRoot = object.nodeId |
274 | end |
275 | if doAttach then |
276 | local trigger = spec.dynamicMountAttacherTrigger |
277 | local objectJoint = createTransformGroup("dynamicMountObjectJoint") |
278 | link(trigger.jointNode, objectJoint) |
279 | setWorldTranslation(objectJoint, getWorldTranslation(objectRoot)) |
280 | |
281 | local couldMount = object:mountDynamic(self, trigger.rootNode, objectJoint, trigger.mountType, trigger.forceAcceleration) |
282 | if couldMount then |
283 | object.additionalDynamicMountJointNode = objectJoint |
284 | self:addDynamicMountedObject(object) |
285 | else |
286 | delete(objectJoint) |
287 | end |
288 | else |
289 | spec.pendingDynamicMountObjects[object] = nil |
290 | |
291 | self:raiseDirtyFlags(spec.dynamicMountedObjectsDirtyFlag) |
292 | end |
293 | end |
294 | end |
295 | end |
296 | else |
297 | for object,_ in pairs(spec.dynamicMountedObjects) do |
298 | self:removeDynamicMountedObject(object, false) |
299 | object:unmountDynamic() |
300 | |
301 | if object.additionalDynamicMountJointNode ~= nil then |
302 | delete(object.additionalDynamicMountJointNode) |
303 | object.additionalDynamicMountJointNode = nil |
304 | end |
305 | end |
306 | end |
307 | |
308 | if spec.dynamicMountAttacherGrab ~= nil then |
309 | for object,_ in pairs(spec.dynamicMountedObjects) do |
310 | local usedMountType = spec.dynamicMountAttacherGrab.closedMountType |
311 | |
312 | if self:getIsDynamicMountGrabOpened(spec.dynamicMountAttacherGrab) then |
313 | usedMountType = spec.dynamicMountAttacherGrab.openMountType |
314 | end |
315 | |
316 | if spec.dynamicMountAttacherGrab.currentMountType ~= usedMountType then |
317 | spec.dynamicMountAttacherGrab.currentMountType = usedMountType |
318 | |
319 | local x, y, z = getWorldTranslation(spec.dynamicMountAttacherNode) |
320 | setJointPosition(object.dynamicMountJointIndex, 1, x,y,z) |
321 | if usedMountType == DynamicMountUtil.TYPE_FORK then |
322 | |
323 | setJointRotationLimit(object.dynamicMountJointIndex, 0, true, 0, 0) |
324 | setJointRotationLimit(object.dynamicMountJointIndex, 1, true, 0, 0) |
325 | setJointRotationLimit(object.dynamicMountJointIndex, 2, true, 0, 0) |
326 | |
327 | if object.dynamicMountSingleAxisFreeX then |
328 | setJointTranslationLimit(object.dynamicMountJointIndex, 0, false, 0, 0) |
329 | else |
330 | setJointTranslationLimit(object.dynamicMountJointIndex, 0, true, -0.01, 0.01) |
331 | end |
332 | if object.dynamicMountSingleAxisFreeY then |
333 | setJointTranslationLimit(object.dynamicMountJointIndex, 1, false, 0, 0) |
334 | else |
335 | setJointTranslationLimit(object.dynamicMountJointIndex, 1, true, -0.01, 0.01) |
336 | end |
337 | setJointTranslationLimit(object.dynamicMountJointIndex, 2, false, 0, 0) |
338 | else |
339 | setJointRotationLimit(object.dynamicMountJointIndex, 0, true, 0, 0) |
340 | setJointRotationLimit(object.dynamicMountJointIndex, 1, true, 0, 0) |
341 | setJointRotationLimit(object.dynamicMountJointIndex, 2, true, 0, 0) |
342 | |
343 | if usedMountType == DynamicMountUtil.TYPE_AUTO_ATTACH_XYZ or usedMountType == DynamicMountUtil.TYPE_FIX_ATTACH then |
344 | setJointTranslationLimit(object.dynamicMountJointIndex, 0, true, -0.01, 0.01) |
345 | setJointTranslationLimit(object.dynamicMountJointIndex, 1, true, -0.01, 0.01) |
346 | setJointTranslationLimit(object.dynamicMountJointIndex, 2, true, -0.01, 0.01) |
347 | elseif usedMountType == DynamicMountUtil.TYPE_AUTO_ATTACH_XZ then |
348 | setJointTranslationLimit(object.dynamicMountJointIndex, 0, true, -0.01, 0.01) |
349 | setJointTranslationLimit(object.dynamicMountJointIndex, 1, false, 0, 0) |
350 | setJointTranslationLimit(object.dynamicMountJointIndex, 2, true, -0.01, 0.01) |
351 | elseif usedMountType == DynamicMountUtil.TYPE_AUTO_ATTACH_Y then |
352 | setJointTranslationLimit(object.dynamicMountJointIndex, 0, false, 0, 0) |
353 | setJointTranslationLimit(object.dynamicMountJointIndex, 1, true, -0.01, 0.01) |
354 | setJointTranslationLimit(object.dynamicMountJointIndex, 2, false, 0, 0) |
355 | end |
356 | end |
357 | end |
358 | end |
359 | end |
360 | end |
361 | end |
65 | function DynamicMountAttacher.registerFunctions(vehicleType) |
66 | SpecializationUtil.registerFunction(vehicleType, "writeDynamicMountObjectsToStream", DynamicMountAttacher.writeDynamicMountObjectsToStream) |
67 | SpecializationUtil.registerFunction(vehicleType, "readDynamicMountObjectsFromStream", DynamicMountAttacher.readDynamicMountObjectsFromStream) |
68 | SpecializationUtil.registerFunction(vehicleType, "getAllowDynamicMountObjects", DynamicMountAttacher.getAllowDynamicMountObjects) |
69 | SpecializationUtil.registerFunction(vehicleType, "dynamicMountTriggerCallback", DynamicMountAttacher.dynamicMountTriggerCallback) |
70 | SpecializationUtil.registerFunction(vehicleType, "lockDynamicMountedObject", DynamicMountAttacher.lockDynamicMountedObject) |
71 | SpecializationUtil.registerFunction(vehicleType, "addDynamicMountedObject", DynamicMountAttacher.addDynamicMountedObject) |
72 | SpecializationUtil.registerFunction(vehicleType, "removeDynamicMountedObject", DynamicMountAttacher.removeDynamicMountedObject) |
73 | SpecializationUtil.registerFunction(vehicleType, "setDynamicMountAnimationState", DynamicMountAttacher.setDynamicMountAnimationState) |
74 | SpecializationUtil.registerFunction(vehicleType, "getAllowDynamicMountFillLevelInfo", DynamicMountAttacher.getAllowDynamicMountFillLevelInfo) |
75 | SpecializationUtil.registerFunction(vehicleType, "loadDynamicMountGrabFromXML", DynamicMountAttacher.loadDynamicMountGrabFromXML) |
76 | SpecializationUtil.registerFunction(vehicleType, "getIsDynamicMountGrabOpened", DynamicMountAttacher.getIsDynamicMountGrabOpened) |
77 | SpecializationUtil.registerFunction(vehicleType, "getDynamicMountTimeToMount", DynamicMountAttacher.getDynamicMountTimeToMount) |
78 | SpecializationUtil.registerFunction(vehicleType, "getHasDynamicMountedObjects", DynamicMountAttacher.getHasDynamicMountedObjects) |
79 | SpecializationUtil.registerFunction(vehicleType, "forceDynamicMountPendingObjects", DynamicMountAttacher.forceDynamicMountPendingObjects) |
80 | SpecializationUtil.registerFunction(vehicleType, "forceUnmountDynamicMountedObjects", DynamicMountAttacher.forceUnmountDynamicMountedObjects) |
81 | SpecializationUtil.registerFunction(vehicleType, "getDynamicMountAttacherSettingsByNode", DynamicMountAttacher.getDynamicMountAttacherSettingsByNode) |
82 | end |