LUADOC - Farming Simulator 17

Printable Version

Script v1.4.4.0

Engine v7.0.0.2

Foundation Reference

AttacherJoints

Description
This is the specialization for vehicles with attacherJoints
Functions

registerJointType

Description
Registration of attacher joint type
Definition
registerJointType(string name)
Arguments
stringnamename if attacher type
Code
19function AttacherJoints.registerJointType(name)
20 local key = "JOINTTYPE_"..string.upper(name);
21 if AttacherJoints[key] == nil then
22 AttacherJoints.NUM_JOINTTYPES = AttacherJoints.NUM_JOINTTYPES+1;
23 AttacherJoints[key] = AttacherJoints.NUM_JOINTTYPES;
24 AttacherJoints.jointTypeNameToInt[name] = AttacherJoints.NUM_JOINTTYPES;
25 end
26end

preLoad

Description
Called before loading
Definition
preLoad(table savegame)
Arguments
tablesavegamesavegame
Code
51function AttacherJoints:preLoad(savegame)
52 self.getIsHardAttachAllowed = Utils.overwrittenFunction(self.getIsHardAttachAllowed, AttacherJoints.getIsHardAttachAllowed);
53 self.createAttachmentJoint = Utils.overwrittenFunction(self.createAttachmentJoint, AttacherJoints.createAttachmentJoint);
54 self.isLowered = Utils.overwrittenFunction(self.isLowered, AttacherJoints.isLowered);
55 self.attachImplement = Utils.overwrittenFunction(self.attachImplement, AttacherJoints.attachImplement);
56 self.hardAttachImplement = Utils.overwrittenFunction(self.hardAttachImplement, AttacherJoints.hardAttachImplement);
57 self.hardDetachImplement = Utils.overwrittenFunction(self.hardDetachImplement, AttacherJoints.hardDetachImplement);
58 self.detachImplement = Utils.overwrittenFunction(self.detachImplement, AttacherJoints.detachImplement);
59 self.getIsSelectable = Utils.overwrittenFunction(self.getIsSelectable, AttacherJoints.getIsSelectable);
60 self.playAttachSound = Utils.overwrittenFunction(self.playAttachSound, AttacherJoints.playAttachSound);
61 self.playDetachSound = Utils.overwrittenFunction(self.playDetachSound, AttacherJoints.playDetachSound);
62 self.setJointMoveDown = Utils.overwrittenFunction(self.setJointMoveDown, AttacherJoints.setJointMoveDown);
63 self.detachingIsPossible = Utils.overwrittenFunction(self.detachingIsPossible, AttacherJoints.detachingIsPossible);
64 self.setSelectedImplement = Utils.overwrittenFunction(self.setSelectedImplement, AttacherJoints.setSelectedImplement);
65 self.getImplementByObject = Utils.overwrittenFunction(self.getImplementByObject, AttacherJoints.getImplementByObject);
66 self.validateAttacherJoint = Utils.overwrittenFunction(self.validateAttacherJoint, AttacherJoints.validateAttacherJoint);
67 self.getIsAChildSelectable = Utils.overwrittenFunction(self.getIsAChildSelectable, AttacherJoints.getIsAChildSelectable);
68 self.onActivateAttachments = Utils.overwrittenFunction(self.onActivateAttachments, AttacherJoints.onActivateAttachments);
69 self.onDeactivateAttachments = Utils.overwrittenFunction(self.onDeactivateAttachments, AttacherJoints.onDeactivateAttachments);
70 self.loadAttachmentFromNodes = Utils.overwrittenFunction(self.loadAttachmentFromNodes, AttacherJoints.loadAttachmentFromNodes);
71 self.getAttachmentsSaveNodes = Utils.overwrittenFunction(self.getAttachmentsSaveNodes, AttacherJoints.getAttachmentsSaveNodes);
72 self.detachImplementByObject = Utils.overwrittenFunction(self.detachImplementByObject, AttacherJoints.detachImplementByObject);
73 self.loadAttacherJointFromXML = Utils.overwrittenFunction(self.loadAttacherJointFromXML, AttacherJoints.loadAttacherJointFromXML);
74 self.getImplementIndexByObject = Utils.overwrittenFunction(self.getImplementIndexByObject, AttacherJoints.getImplementIndexByObject);
75 self.updateAttacherJointRotation = Utils.overwrittenFunction(self.updateAttacherJointRotation, AttacherJoints.updateAttacherJointRotation);
76 self.updateAttacherJointGraphics = Utils.overwrittenFunction(self.updateAttacherJointGraphics, AttacherJoints.updateAttacherJointGraphics);
77 self.selectNextSelectableImplement = Utils.overwrittenFunction(self.selectNextSelectableImplement, AttacherJoints.selectNextSelectableImplement);
78 self.onDeactivateAttachmentsSounds = Utils.overwrittenFunction(self.onDeactivateAttachmentsSounds, AttacherJoints.onDeactivateAttachmentsSounds);
79 self.onDeactivateAttachmentsLights = Utils.overwrittenFunction(self.onDeactivateAttachmentsLights, AttacherJoints.onDeactivateAttachmentsLights);
80 self.getImplementIndexByJointDescIndex = Utils.overwrittenFunction(self.getImplementIndexByJointDescIndex, AttacherJoints.getImplementIndexByJointDescIndex);
81 self.calculateAttacherJointMoveUpperLowerAlpha = Utils.overwrittenFunction(self.calculateAttacherJointMoveUpperLowerAlpha, AttacherJoints.calculateAttacherJointMoveUpperLowerAlpha);
82 self.addToPhysics = Utils.overwrittenFunction(self.addToPhysics, AttacherJoints.addToPhysics);
83 self.loadPowerTakeoff = Utils.overwrittenFunction(self.loadPowerTakeoff, AttacherJoints.loadPowerTakeoff);
84 self.updatePowerTakeoff = Utils.overwrittenFunction(self.updatePowerTakeoff, AttacherJoints.updatePowerTakeoff);
85end

load

Description
Called on loading
Definition
load(table savegame)
Arguments
tablesavegamesavegame
Code
90function AttacherJoints:load(savegame)
91 self.onAttachImplement = SpecializationUtil.callSpecializationsFunction("onAttachImplement");
92 self.onDetachImplement = SpecializationUtil.callSpecializationsFunction("onDetachImplement");
93
94 self.attacherJoints = {};
95 local i=0;
96 while true do
97 local baseName = string.format("vehicle.attacherJoints.attacherJoint(%d)", i);
98 if not hasXMLProperty(self.xmlFile, baseName) then
99 break;
100 end
101 local attacherJoint = {};
102 if self:loadAttacherJointFromXML(attacherJoint, self.xmlFile, baseName, i) then
103 table.insert(self.attacherJoints, attacherJoint);
104 end
105 i = i + 1;
106 end
107
108 if self.configurations["attacherJoint"] ~= nil then
109 local attacherConfigs = string.format("vehicle.attacherJointConfigurations.attacherJointConfiguration(%d)", self.configurations["attacherJoint"]-1);
110 local i=0;
111 while true do
112 local baseName = string.format(attacherConfigs..".attacherJoint(%d)", i);
113 if not hasXMLProperty(self.xmlFile, baseName) then
114 break;
115 end
116 local attacherJoint = {};
117 if self:loadAttacherJointFromXML(attacherJoint, self.xmlFile, baseName, i) then
118 table.insert(self.attacherJoints, attacherJoint);
119 end
120 i = i + 1;
121 end
122 ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.attacherJointConfigurations.attacherJointConfiguration", self.configurations["attacherJoint"], self.components, self);
123 end
124
125 if self.isClient then
126 self.sampleAttach = SoundUtil.loadSample(self.xmlFile, {}, "vehicle.attachSound", nil, self.baseDirectory);
127 end
128
129 self.attachedImplements = {};
130 self.selectedImplement = nil;
131
132 if hasXMLProperty(self.xmlFile, "vehicle.trailerAttacherJoints") then
133 print("Warning: vehicle.trailerAttacherJoints.trailerAttacherJoint is not supported anymore. Use vehicle.attacherJoints.attacherJoint with jointType='trailer/trailerLow' instead");
134 end
135end

postLoad

Description
Called after loading
Definition
postLoad(table savegame)
Arguments
tablesavegamesavegame
Code
140function AttacherJoints:postLoad(savegame)
141 for _, attacherJoint in pairs(self.attacherJoints) do
142 if attacherJoint.bottomArm ~= nil then
143 setRotation(attacherJoint.bottomArm.rotationNode, attacherJoint.bottomArm.rotX, attacherJoint.bottomArm.rotY, attacherJoint.bottomArm.rotZ);
144 if self.setMovingToolDirty ~= nil then
145 self:setMovingToolDirty(attacherJoint.bottomArm.rotationNode);
146 end
147 end
148 if attacherJoint.rotationNode ~= nil then
149 setRotation(attacherJoint.rotationNode, attacherJoint.rotX, attacherJoint.rotY, attacherJoint.rotZ);
150 end
151 end
152end

preDelete

Description
Called on before deleting
Definition
preDelete()
Code
156function AttacherJoints:preDelete()
157 for i=table.getn(self.attachedImplements), 1, -1 do
158 self:detachImplement(1, true);
159 end
160end

delete

Description
Called on deleting
Definition
delete()
Code
164function AttacherJoints:delete()
165 if self.isClient then
166 SoundUtil.deleteSample(self.sampleAttach);
167 end
168 for _, jointDesc in pairs(self.attacherJoints) do
169 if jointDesc.ptoOutput ~= nil then
170 delete(jointDesc.ptoOutput.rootNode);
171 delete(jointDesc.ptoOutput.attachNode);
172 end
173 if jointDesc.pto2Output ~= nil then
174 delete(jointDesc.pto2Output.rootNode);
175 delete(jointDesc.pto2Output.attachNode);
176 end
177 if jointDesc.sampleAttach ~= nil then
178 SoundUtil.deleteSample(jointDesc.sampleAttach)
179 end
180 end
181end

readStream

Description
Called on client side on join
Definition
readStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
187function AttacherJoints:readStream(streamId, connection)
188 local numImplements = streamReadInt8(streamId);
189 for i=1, numImplements do
190 local object = readNetworkNodeObject(streamId);
191 local inputJointDescIndex = streamReadInt8(streamId);
192 local jointDescIndex = streamReadInt8(streamId);
193 local moveDown = streamReadBool(streamId);
194 if object ~= nil then
195 self:attachImplement(object, inputJointDescIndex, jointDescIndex, true, i);
196 self:setJointMoveDown(jointDescIndex, moveDown, true);
197 end
198 end
199end

writeStream

Description
Called on server side on join
Definition
writeStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
205function AttacherJoints:writeStream(streamId, connection)
206 -- write attached implements
207 streamWriteInt8(streamId, table.getn(self.attachedImplements));
208 for i=1, table.getn(self.attachedImplements) do
209 local implement = self.attachedImplements[i];
210 local inputJointDescIndex = implement.object.inputAttacherJointDescIndex;
211 local jointDescIndex = implement.jointDescIndex;
212 local jointDesc = self.attacherJoints[jointDescIndex];
213 local moveDown = jointDesc.moveDown;
214 writeNetworkNodeObject(streamId, implement.object);
215 streamWriteInt8(streamId, inputJointDescIndex);
216 streamWriteInt8(streamId, jointDescIndex);
217 streamWriteBool(streamId, moveDown);
218 end
219end

update

Description
Called on update
Definition
update(float dt)
Arguments
floatdttime since last call in ms
Code
230function AttacherJoints:update(dt)
231 if self:getIsActive() then
232 for _, implement in pairs(self.attachedImplements) do
233 if implement.object ~= nil then
234 if self.isServer or (self.updateLoopIndex == implement.object.updateLoopIndex) then
235 self:updateAttacherJointGraphics(implement, dt);
236 end
237 end
238 end
239 if not self.isServer and self.attacherVehicle ~= nil then
240 if self.updateLoopIndex == self.attacherVehicle.updateLoopIndex then
241 local implement = self.attacherVehicle:getImplementByObject(self);
242 if implement ~= nil then
243 self.attacherVehicle:updateAttacherJointGraphics(implement, dt);
244 end
245 end
246 end
247 end
248end

postUpdate

Description
Called on after update
Definition
postUpdate(float dt)
Arguments
floatdttime since last call in ms
Code
253function AttacherJoints:postUpdate(dt)
254 if self.forcePtoUpdate then
255 for _,implement in pairs(self.attachedImplements) do
256 self:updatePowerTakeoff(implement, dt, "pto");
257 self:updatePowerTakeoff(implement, dt, "pto2");
258
259 if implement.object.attacherJoint ~= nil then
260 if self.attacherJoints[implement.jointDescIndex].steeringBarLeftNode ~= nil and implement.object.attacherJoint.steeringBarLeftMovingPart ~= nil then
261 Cylindered.updateMovingPart(self, implement.object.attacherJoint.steeringBarLeftMovingPart, nil, true)
262 end
263 if self.attacherJoints[implement.jointDescIndex].steeringBarRightNode ~= nil and implement.object.attacherJoint.steeringBarRightMovingPart ~= nil then
264 Cylindered.updateMovingPart(self, implement.object.attacherJoint.steeringBarRightMovingPart, nil, true)
265 end
266 end
267 end
268 end
269end

updateTick

Description
Called on update tick
Definition
updateTick(float dt)
Arguments
floatdttime since last call in ms
Code
274function AttacherJoints:updateTick(dt)
275 local forceUpdate = false;
276 for _, implement in pairs(self.attachedImplements) do
277 if implement.object ~= nil then
278 if implement.attachingIsInProgress then
279 forceUpdate = true;
280 end
281 end
282 end
283
284 if self:getIsActive() or forceUpdate then
285 local playHydraulicSound = false;
286
287 for _, implement in pairs(self.attachedImplements) do
288 if implement.object ~= nil then
289 local jointDesc = self.attacherJoints[implement.jointDescIndex];
290
291 if not implement.object.isHardAttached then
292 if self.isServer then
293 if implement.attachingIsInProgress then
294 local done = true;
295 for i=1,3 do
296 local lastRotLimit = implement.attachingRotLimit[i];
297 local lastTransLimit = implement.attachingTransLimit[i];
298 implement.attachingRotLimit[i] = math.max(0, implement.attachingRotLimit[i] - implement.attachingRotLimitSpeed[i] * dt);
299 implement.attachingTransLimit[i] = math.max(0, implement.attachingTransLimit[i] - implement.attachingTransLimitSpeed[i] * dt);
300 if (implement.attachingRotLimit[i] > 0 or implement.attachingTransLimit[i] > 0) or
301 (lastRotLimit > 0 or lastTransLimit > 0)
302 then
303 done = false;
304 end
305 end
306 implement.attachingIsInProgress = not done;
307
308 if done and implement.object.attacherJoint.hardAttach and self:getIsHardAttachAllowed(implement.jointDescIndex) then
309 self:hardAttachImplement(implement)
310 end
311 end
312 end
313 if not implement.attachingIsInProgress then
314 local jointFrameInvalid = false;
315 if jointDesc.allowsLowering then
316 local moveAlpha = Utils.getMovedLimitedValue(jointDesc.moveAlpha, jointDesc.lowerAlpha, jointDesc.upperAlpha, jointDesc.moveTime, dt, not jointDesc.moveDown);
317 if moveAlpha ~= jointDesc.moveAlpha then
318 playHydraulicSound = true;
319 jointDesc.moveAlpha = moveAlpha;
320 jointDesc.moveLimitAlpha = 1- (moveAlpha-jointDesc.lowerAlpha) / (jointDesc.upperAlpha-jointDesc.lowerAlpha);
321 jointFrameInvalid = true;
322 if jointDesc.rotationNode ~= nil then
323 setRotation(jointDesc.rotationNode, Utils.vector3ArrayLerp(jointDesc.upperRotation, jointDesc.lowerRotation, jointDesc.moveAlpha));
324 end
325 if jointDesc.rotationNode2 ~= nil then
326 setRotation(jointDesc.rotationNode2, Utils.vector3ArrayLerp(jointDesc.upperRotation2, jointDesc.lowerRotation2, jointDesc.moveAlpha));
327 end
328 self:updateAttacherJointRotation(jointDesc, implement.object);
329 end
330 end
331
332 jointFrameInvalid = jointFrameInvalid or self:validateAttacherJoint(implement, jointDesc, dt);
333 jointFrameInvalid = jointFrameInvalid or jointDesc.jointFrameInvalid;
334 if jointFrameInvalid then
335 jointDesc.jointFrameInvalid = false;
336 if self.isServer then
337 setJointFrame(jointDesc.jointIndex, 0, jointDesc.jointTransform);
338 end
339 end
340 end
341 if self.isServer then
342 local force = implement.attachingIsInProgress;
343 if force or (jointDesc.allowsLowering and jointDesc.allowsJointLimitMovement) then
344 if force or implement.object.attacherJoint.allowsJointRotLimitMovement then
345 for i=1,3 do
346 local newRotLimit = Utils.lerp( math.max(implement.attachingRotLimit[i], implement.upperRotLimit[i]),
347 math.max(implement.attachingRotLimit[i], implement.lowerRotLimit[i]), jointDesc.moveLimitAlpha);
348 if force or math.abs(newRotLimit - implement.jointRotLimit[i]) > 0.0005 then
349 local rotLimitDown = -newRotLimit;
350 local rotLimitUp = newRotLimit;
351 if i == 3 then
352 if jointDesc.lockDownRotLimit then
353 rotLimitDown = math.min(-implement.attachingRotLimit[i], 0);
354 end
355 if jointDesc.lockUpRotLimit then
356 rotLimitUp = math.max(implement.attachingRotLimit[i], 0);
357 end
358 end
359 setJointRotationLimit(jointDesc.jointIndex, i-1, true, rotLimitDown, rotLimitUp);
360 implement.jointRotLimit[i] = newRotLimit;
361 end
362 end
363 end
364
365 if force or implement.object.attacherJoint.allowsJointTransLimitMovement then
366 for i=1,3 do
367 local newTransLimit = Utils.lerp( math.max(implement.attachingTransLimit[i], implement.upperTransLimit[i]),
368 math.max(implement.attachingTransLimit[i], implement.lowerTransLimit[i]), jointDesc.moveLimitAlpha);
369
370 if force or math.abs(newTransLimit - implement.jointTransLimit[i]) > 0.0005 then
371 local transLimitDown = -newTransLimit;
372 local transLimitUp = newTransLimit
373 if i == 2 then
374 if jointDesc.lockDownTransLimit then
375 transLimitDown = math.min(-implement.attachingTransLimit[i], 0);
376 end
377 if jointDesc.lockUpTransLimit then
378 transLimitUp = math.max(implement.attachingTransLimit[i], 0);
379 end
380 end
381
382 setJointTranslationLimit(jointDesc.jointIndex, i-1, true, transLimitDown, transLimitUp);
383 implement.jointTransLimit[i] = newTransLimit;
384 end
385 end
386 end
387 end
388 end
389 end
390 end
391 end
392
393 if self:getIsActiveForSound() and self.sampleHydraulic ~= nil and self.sampleHydraulic.sample ~= nil then
394 if playHydraulicSound then
395 if not self.sampleHydraulic.isPlaying then
396 SoundUtil.playSample(self.sampleHydraulic, 0, 0, nil);
397 end
398 elseif self.sampleHydraulic.isPlaying then
399 SoundUtil.stopSample(self.sampleHydraulic, true);
400 end
401 end
402 end
403end

draw

Description
Called on draw
Definition
draw()
Code
407function AttacherJoints:draw()
408end

updateAttacherJointGraphics

Description
Update attacher joint graphics
Definition
updateAttacherJointGraphics(table implement, float dt)
Arguments
tableimplementimplement
floatdttime since last call in ms
Code
414function AttacherJoints:updateAttacherJointGraphics(superFunc, implement, dt)
415 if superFunc ~= nil then
416 superFunc(self, implement, dt);
417 end
418
419 if implement.object ~= nil then
420 local jointDesc = self.attacherJoints[implement.jointDescIndex];
421 local attacherJoint = implement.object.attacherJoint;
422 if jointDesc.topArm ~= nil and attacherJoint.topReferenceNode ~= nil then
423 local ax, ay, az = getWorldTranslation(jointDesc.topArm.rotationNode);
424 local bx, by, bz = getWorldTranslation(attacherJoint.topReferenceNode);
425
426 local x, y, z = worldDirectionToLocal(getParent(jointDesc.topArm.rotationNode), bx-ax, by-ay, bz-az);
427 local distance = Utils.vector3Length(x,y,z);
428
429 local _, upY, upZ = 0,1,0;
430 if math.abs(y) > 0.99*distance then
431 -- direction and up is parallel
432 upY = 0;
433 if y > 0 then
434 upZ = 1;
435 else
436 upZ = -1;
437 end
438 end
439
440 -- different approach I) rotate actual direction of topArm by 90degree around x-axis
441 local alpha = math.rad(-90);
442 -- check if rotationNode is at back of tractor => inverted rotation direction, could be dismissed by rotating TG in i3d
443 local px,py,pz = getWorldTranslation(jointDesc.topArm.rotationNode);
444 local _,_,lz = worldToLocal(self.components[1].node, px,py,pz);
445 if lz < 0 then
446 alpha = math.rad(90);
447 end
448
449 local dx, dy, dz = localDirectionToWorld(jointDesc.topArm.rotationNode, 0,0,1);
450 dx, dy, dz = worldDirectionToLocal(getParent(jointDesc.topArm.rotationNode), dx, dy, dz);
451 local upX = dx;
452 local upY = math.cos(alpha)*dy - math.sin(alpha)*dz;
453 local upZ = math.sin(alpha)*dy + math.cos(alpha)*dz;
454
455 setDirection(jointDesc.topArm.rotationNode, x*jointDesc.topArm.zScale, y*jointDesc.topArm.zScale, z*jointDesc.topArm.zScale, upX, upY, upZ);
456 if jointDesc.topArm.translationNode ~= nil and not implement.attachingIsInProgress then
457 local translation = (distance-jointDesc.topArm.referenceDistance)
458 setTranslation(jointDesc.topArm.translationNode, 0, 0, translation*jointDesc.topArm.zScale);
459 if jointDesc.topArm.scaleNode ~= nil then
460 setScale(jointDesc.topArm.scaleNode, 1, 1, math.max((translation+jointDesc.topArm.scaleReferenceDistance)/jointDesc.topArm.scaleReferenceDistance, 0));
461 end
462 end
463 end
464 if jointDesc.bottomArm ~= nil then
465 local ax, ay, az = getWorldTranslation(jointDesc.bottomArm.rotationNode);
466 local bx, by, bz = getWorldTranslation(attacherJoint.node);
467
468 local x, y, z = worldDirectionToLocal(getParent(jointDesc.bottomArm.rotationNode), bx-ax, by-ay, bz-az);
469 local distance = Utils.vector3Length(x,y,z);
470 local upX, upY, upZ = 0,1,0;
471 if math.abs(y) > 0.99*distance then
472 -- direction and up is parallel
473 upY = 0;
474 if y > 0 then
475 upZ = 1;
476 else
477 upZ = -1;
478 end
479 end
480 local dirX = 0
481 if not jointDesc.bottomArm.lockDirection then
482 dirX = x*jointDesc.bottomArm.zScale
483 end
484 setDirection(jointDesc.bottomArm.rotationNode, dirX, y*jointDesc.bottomArm.zScale, z*jointDesc.bottomArm.zScale, upX, upY, upZ);
485 if jointDesc.bottomArm.translationNode ~= nil and not implement.attachingIsInProgress then
486 setTranslation(jointDesc.bottomArm.translationNode, 0, 0, (distance-jointDesc.bottomArm.referenceDistance)*jointDesc.bottomArm.zScale);
487 end
488 if self.setMovingToolDirty ~= nil then
489 self:setMovingToolDirty(jointDesc.bottomArm.rotationNode);
490 end
491
492 if attacherJoint.needsToolbar and jointDesc.bottomArm.toolbar ~= nil then
493 local parent = getParent(jointDesc.bottomArm.toolbar);
494
495 local _, yDir, zDir = localDirectionToLocal(attacherJoint.node, jointDesc.rootNode, 1, 0, 0);
496 local xDir, yDir, zDir = localDirectionToLocal(jointDesc.rootNode, parent, 0, yDir, zDir);
497
498 local _, yUp, zUp = localDirectionToLocal(attacherJoint.node, jointDesc.rootNode, 0, 1, 0);
499 local xUp, yUp, zUp = localDirectionToLocal(jointDesc.rootNode, parent, 0, yUp, zUp);
500
501 setDirection(jointDesc.bottomArm.toolbar, xDir, yDir, zDir, xUp, yUp, zUp);
502 end
503 end
504 end
505
506 self:updatePowerTakeoff(implement, dt, "pto");
507 self:updatePowerTakeoff(implement, dt, "pto2");
508end

loadAttachmentFromNodes

Description
Called on loading
Definition
loadAttachmentFromNodes(table savegame)
Arguments
tablesavegamesavegame
Code
513function AttacherJoints:loadAttachmentFromNodes(superFunc, xmlFile, key, idsToVehicle)
514 local id1 = getXMLString(xmlFile, key.."#id1");
515 local jointIndex = getXMLInt(xmlFile, key.."#jointIndex");
516 local inputJointDescIndex = Utils.getNoNil(getXMLInt(xmlFile, key.."#inputJointDescIndex"), 1);
517 if id1 ~= nil and jointIndex ~= nil then
518 local vehicle1 = idsToVehicle[id1];
519
520 if vehicle1 ~= nil and vehicle1.inputAttacherJoints[inputJointDescIndex] ~= nil and self.attacherJoints[jointIndex] ~= nil and self.attacherJoints[jointIndex].jointIndex == 0 then
521 self:attachImplement(vehicle1, inputJointDescIndex, jointIndex, true, nil, nil, false);
522
523 local moveDown = getXMLBool(xmlFile, key.."#moveDown");
524 if moveDown ~= nil then
525 self:setJointMoveDown(jointIndex, moveDown, true);
526 end
527 end
528 end
529end

getAttachmentsSaveNodes

Description
Returns save nodes of attached vehicles
Definition
getAttachmentsSaveNodes(string nodeIdent, table vehiclesToId)
Arguments
stringnodeIdentnode ident
tablevehiclesToIdvehicles to id
Code
535function AttacherJoints:getAttachmentsSaveNodes(superFunc, nodeIdent, vehiclesToId)
536 local nodes = "";
537 local id = vehiclesToId[self];
538 if id ~= nil then
539 for i=1, table.getn(self.attachedImplements) do
540 local implement = self.attachedImplements[i];
541 local object = implement.object;
542 if object ~= nil and vehiclesToId[object] ~= nil then
543 local jointDescIndex = implement.jointDescIndex;
544 local jointDesc = self.attacherJoints[jointDescIndex];
545 local inputJointDescIndex = implement.object.inputAttacherJointDescIndex;
546 local moveDown = jointDesc.moveDown;
547 nodes = nodes .. nodeIdent .. '<attachment id0="'..id..'" id1="'..vehiclesToId[object]..'" inputJointDescIndex="'..inputJointDescIndex..'" jointIndex="'..jointDescIndex..'" moveDown="'..tostring(moveDown)..'"';
548
549 local subNodes = "";
550 for _,v in ipairs(self.specializations) do
551 if v.getAttachmentSaveAttributesAndNodes ~= nil then
552 local specAttributes, specNodes = v.getAttachmentSaveAttributesAndNodes(self, implement, nodeIdent, vehiclesToId);
553 if specAttributes ~= nil and specAttributes ~= "" then
554 nodes = nodes.." "..specAttributes;
555 end
556 if specNodes ~= nil and specNodes ~= "" then
557 subNodes = subNodes..specNodes.."\n";
558 end
559 end
560 end
561 if subNodes ~= "" then
562 nodes = nodes..">\n"..subNodes.."</attachment>\n";
563 else
564 nodes = nodes.."/>\n";
565 end
566 end
567 end
568 end
569 return nodes;
570end

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
576function AttacherJoints:calculateAttacherJointMoveUpperLowerAlpha(superFunc, jointDesc, object)
577 if superFunc ~= nil then
578 return superFunc(self, jointDesc, object);
579 end
580 if jointDesc.allowsLowering then
581 local upperAlpha = Utils.clamp((object.attacherJoint.upperDistanceToGround - jointDesc.upperDistanceToGround) / (jointDesc.lowerDistanceToGround - jointDesc.upperDistanceToGround), 0, 1);
582 local lowerAlpha = Utils.clamp((object.attacherJoint.lowerDistanceToGround - jointDesc.upperDistanceToGround) / (jointDesc.lowerDistanceToGround - jointDesc.upperDistanceToGround), 0, 1);
583
584 if object.attacherJoint.allowsLowering and jointDesc.allowsLowering then
585 return upperAlpha, lowerAlpha;
586 else
587 if object.attacherJoint.isDefaultLowered then
588 return lowerAlpha,lowerAlpha;
589 else
590 return upperAlpha,upperAlpha;
591 end
592 end
593 end
594
595 if object.attacherJoint.isDefaultLowered then
596 return 1,1;
597 else
598 return 0,0;
599 end
600end

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
606function AttacherJoints:updateAttacherJointRotation(superFunc, jointDesc, object)
607 if superFunc ~= nil then
608 superFunc(self, jointDesc, object);
609 end
610
611 -- rotate attacher such that
612 local targetRot = Utils.lerp(object.attacherJoint.upperRotationOffset, object.attacherJoint.lowerRotationOffset, jointDesc.moveAlpha);
613 local curRot = Utils.lerp(jointDesc.upperRotationOffset, jointDesc.lowerRotationOffset, jointDesc.moveAlpha);
614 local rotDiff = targetRot - curRot;
615
616 setRotation(jointDesc.jointTransform, unpack(jointDesc.jointOrigRot));
617 rotateAboutLocalAxis(jointDesc.jointTransform, rotDiff, 0, 0, 1);
618end

attachImplement

Description
Attach implement
Definition
attachImplement(table object, integer inputJointDescIndex, boolean noEventSend, integer index, boolean startLowered, boolean noSmoothAttach)
Arguments
tableobjectobject of vehicle to attach
integerinputJointDescIndexindex of input attacher joint to use
booleannoEventSendno event send
integerindexindex of attached implement
booleanstartLoweredstart vehicle on lower state
booleannoSmoothAttachdont use smooth attach
Return Values
booleansuccesssuccess

createAttachmentJoint

Description
Create attacher joint between vehicle and implement
Definition
createAttachmentJoint(table implement, boolean noSmoothAttach)
Arguments
tableimplementimplement to attach
booleannoSmoothAttachdont use smooth attach
Code
826function AttacherJoints:createAttachmentJoint(superFunc, implement, noSmoothAttach)
827 if superFunc ~= nil then
828 if not superFunc(self, implement) then
829 return false;
830 end
831 end
832
833 local jointDesc = self.attacherJoints[implement.jointDescIndex];
834 if self.isServer then
835 local xNew = jointDesc.jointOrigTrans[1] + jointDesc.jointPositionOffset[1];
836 local yNew = jointDesc.jointOrigTrans[2] + jointDesc.jointPositionOffset[2];
837 local zNew = jointDesc.jointOrigTrans[3] + jointDesc.jointPositionOffset[3];
838
839 -- transform offset position to world coord and to jointTransform coord to get position offset dependend on angle and position
840 local x,y,z = localToWorld(getParent(jointDesc.jointTransform), xNew, yNew, zNew);
841 local x1,y1,z1 = worldToLocal(jointDesc.jointTransform, x,y,z);
842
843 -- move jointTransform to offset pos
844 setTranslation(jointDesc.jointTransform, xNew, yNew, zNew);
845
846 -- transform it to implement position and angle
847 x,y,z = localToWorld(implement.object.attacherJoint.node,x1,y1,z1);
848 local x2,y2,z2 = worldToLocal(getParent(implement.object.attacherJoint.node), x,y,z);
849 setTranslation(implement.object.attacherJoint.node, x2,y2, z2);
850
851
852 local constr = JointConstructor:new();
853 constr:setActors(jointDesc.rootNode, implement.object.attacherJoint.rootNode);
854 constr:setJointTransforms(jointDesc.jointTransform, implement.object.attacherJoint.node);
855 --constr:setBreakable(20, 10);
856
857 implement.jointRotLimit = {};
858 implement.jointTransLimit = {};
859
860 implement.lowerRotLimit = {};
861 implement.lowerTransLimit = {};
862
863 implement.upperRotLimit = {};
864 implement.upperTransLimit = {};
865
866 if noSmoothAttach == nil or not noSmoothAttach then
867 local dx,dy,dz = localToLocal(implement.object.attacherJoint.node, jointDesc.jointTransform, 0,0,0);
868 local _,y,z = localDirectionToLocal(implement.object.attacherJoint.node, jointDesc.jointTransform, 0,1,0);
869 local rX = math.atan2(z,y);
870 local x,_,z = localDirectionToLocal(implement.object.attacherJoint.node, jointDesc.jointTransform, 0,0,1);
871 local rY = math.atan2(x,z);
872 local x,y,_ = localDirectionToLocal(implement.object.attacherJoint.node, jointDesc.jointTransform, 1,0,0);
873 local rZ = math.atan2(y,x);
874 implement.attachingTransLimit = { math.abs(dx), math.abs(dy), math.abs(dz) };
875 implement.attachingRotLimit = { math.abs(rX), math.abs(rY), math.abs(rZ) };
876 implement.attachingTransLimitSpeed = {};
877 implement.attachingRotLimitSpeed = {};
878 for i=1,3 do
879 implement.attachingTransLimitSpeed[i] = implement.attachingTransLimit[i] / 500;
880 implement.attachingRotLimitSpeed[i] = implement.attachingRotLimit[i] / 500;
881 end
882 implement.attachingIsInProgress = true;
883 else
884 implement.attachingTransLimit = { 0,0,0 };
885 implement.attachingRotLimit = { 0,0,0 };
886 end
887
888 for i=1, 3 do
889 local lowerRotLimit = jointDesc.lowerRotLimit[i]*implement.object.attacherJoint.lowerRotLimitScale[i];
890 local upperRotLimit = jointDesc.upperRotLimit[i]*implement.object.attacherJoint.upperRotLimitScale[i];
891 if implement.object.attacherJoint.fixedRotation then
892 lowerRotLimit = 0;
893 upperRotLimit = 0;
894 end
895
896 local upperTransLimit = jointDesc.lowerTransLimit[i]*implement.object.attacherJoint.lowerTransLimitScale[i];
897 local lowerTransLimit = jointDesc.upperTransLimit[i]*implement.object.attacherJoint.upperTransLimitScale[i];
898 implement.lowerRotLimit[i] = lowerRotLimit;
899 implement.upperRotLimit[i] = upperRotLimit;
900
901 implement.lowerTransLimit[i] = upperTransLimit;
902 implement.upperTransLimit[i] = lowerTransLimit;
903
904 if not jointDesc.allowsLowering then
905 implement.upperRotLimit[i] = lowerRotLimit;
906 implement.upperTransLimit[i] = upperTransLimit;
907 end
908
909 local rotLimit = lowerRotLimit;
910 local transLimit = upperTransLimit;
911 if jointDesc.allowsLowering and jointDesc.allowsJointLimitMovement then
912 if implement.object.attacherJoint.allowsJointRotLimitMovement then
913 rotLimit = Utils.lerp(upperRotLimit, lowerRotLimit, jointDesc.moveAlpha);
914 end
915 if implement.object.attacherJoint.allowsJointTransLimitMovement then
916 transLimit = Utils.lerp(lowerTransLimit, upperTransLimit, jointDesc.moveAlpha);
917 end
918 end
919
920 local limitRot = rotLimit;
921 local limitTrans = transLimit;
922 if noSmoothAttach == nil or not noSmoothAttach then
923 limitRot = math.max(rotLimit, implement.attachingRotLimit[i])
924 limitTrans = math.max(transLimit, implement.attachingTransLimit[i]);
925 end
926
927 constr:setRotationLimit(i-1, -limitRot, limitRot);
928 implement.jointRotLimit[i] = limitRot;
929 constr:setTranslationLimit(i-1, true, -limitTrans, limitTrans);
930 implement.jointTransLimit[i] = limitTrans;
931 end
932
933 if jointDesc.enableCollision then
934 constr:setEnableCollision(true);
935 else
936 for _, component in pairs(self.components) do
937 if component.node ~= jointDesc.rootNodeBackup and not component.collideWithAttachables then
938 setPairCollision(component.node, implement.object.attacherJoint.rootNode, false);
939 end
940 end
941 end
942
943 local springX = math.max(jointDesc.rotLimitSpring[1], implement.object.attacherJoint.rotLimitSpring[1]);
944 local springY = math.max(jointDesc.rotLimitSpring[2], implement.object.attacherJoint.rotLimitSpring[2]);
945 local springZ = math.max(jointDesc.rotLimitSpring[3], implement.object.attacherJoint.rotLimitSpring[3]);
946 local dampingX = math.max(jointDesc.rotLimitDamping[1], implement.object.attacherJoint.rotLimitDamping[1]);
947 local dampingY = math.max(jointDesc.rotLimitDamping[2], implement.object.attacherJoint.rotLimitDamping[2]);
948 local dampingZ = math.max(jointDesc.rotLimitDamping[3], implement.object.attacherJoint.rotLimitDamping[3]);
949 local forceLimitX = Utils.getMaxJointForceLimit(jointDesc.rotLimitForceLimit[1], implement.object.attacherJoint.rotLimitForceLimit[1]);
950 local forceLimitY = Utils.getMaxJointForceLimit(jointDesc.rotLimitForceLimit[2], implement.object.attacherJoint.rotLimitForceLimit[2]);
951 local forceLimitZ = Utils.getMaxJointForceLimit(jointDesc.rotLimitForceLimit[3], implement.object.attacherJoint.rotLimitForceLimit[3]);
952 constr:setRotationLimitSpring(springX, dampingX, springY, dampingY, springZ, dampingZ);
953 constr:setRotationLimitForceLimit(forceLimitX, forceLimitY, forceLimitZ);
954
955 local springX = math.max(jointDesc.transLimitSpring[1], implement.object.attacherJoint.transLimitSpring[1]);
956 local springY = math.max(jointDesc.transLimitSpring[2], implement.object.attacherJoint.transLimitSpring[2]);
957 local springZ = math.max(jointDesc.transLimitSpring[3], implement.object.attacherJoint.transLimitSpring[3]);
958 local dampingX = math.max(jointDesc.transLimitDamping[1], implement.object.attacherJoint.transLimitDamping[1]);
959 local dampingY = math.max(jointDesc.transLimitDamping[2], implement.object.attacherJoint.transLimitDamping[2]);
960 local dampingZ = math.max(jointDesc.transLimitDamping[3], implement.object.attacherJoint.transLimitDamping[3]);
961 local forceLimitX = Utils.getMaxJointForceLimit(jointDesc.transLimitForceLimit[1], implement.object.attacherJoint.transLimitForceLimit[1]);
962 local forceLimitY = Utils.getMaxJointForceLimit(jointDesc.transLimitForceLimit[2], implement.object.attacherJoint.transLimitForceLimit[2]);
963 local forceLimitZ = Utils.getMaxJointForceLimit(jointDesc.transLimitForceLimit[3], implement.object.attacherJoint.transLimitForceLimit[3]);
964 constr:setTranslationLimitSpring(springX, dampingX, springY, dampingY, springZ, dampingZ);
965 constr:setTranslationLimitForceLimit(forceLimitX, forceLimitY, forceLimitZ);
966
967 jointDesc.jointIndex = constr:finalize();
968
969 -- restore implement attacher joint position (to ensure correct bottom arm alignment)
970 setTranslation(implement.object.attacherJoint.node, unpack(implement.object.attacherJoint.jointOrigTrans));
971 else
972 jointDesc.jointIndex = -1;
973 end
974end

addToPhysics

Description
Add to physics
Definition
addToPhysics()
Return Values
booleansuccesssuccess
Code
979function AttacherJoints:addToPhysics(superFunc)
980 if superFunc ~= nil then
981 if not superFunc(self) then
982 return false;
983 end
984 end
985
986 for _, implement in pairs(self.attachedImplements) do
987 if not implement.object.isHardAttached then
988 self:createAttachmentJoint(implement)
989 end
990 end
991
992 return true
993end

hardAttachImplement

Description
Hard attach implement
Definition
hardAttachImplement(table implement)
Arguments
tableimplementimplement to attach
Code
998function AttacherJoints:hardAttachImplement(superFunc, implement)
999 if superFunc ~= nil then
1000 if not superFunc(self, implement) then
1001 return false;
1002 end
1003 end
1004
1005 local implements = {}
1006 for i=table.getn(implement.object.attachedImplements), 1, -1 do
1007 local impl = implement.object.attachedImplements[i]
1008 local object = impl.object
1009 local jointDescIndex = impl.jointDescIndex;
1010 local jointDesc = implement.object.attacherJoints[jointDescIndex];
1011 local inputJointDescIndex = object.inputAttacherJointDescIndex;
1012 local moveDown = jointDesc.moveDown;
1013 table.insert(implements, 1, {object=object, implementIndex=i, jointDescIndex=jointDescIndex, inputJointDescIndex=inputJointDescIndex, moveDown=moveDown})
1014 implement.object:detachImplement(1, true);
1015 end
1016
1017 local attacherJoint = self.attacherJoints[implement.jointDescIndex];
1018 local implementJoint = implement.object.attacherJoint
1019
1020 local baseVehicleComponentNode = self:getParentComponent(attacherJoint.jointTransform)
1021 local attachedVehicleComponentNode = implement.object:getParentComponent(implement.object.attacherJoint.node)
1022
1023 -- remove all components from physics
1024 local currentVehicle = self
1025 while currentVehicle ~= nil do
1026 currentVehicle:removeFromPhysics()
1027 currentVehicle = currentVehicle.attacherVehicle
1028 end
1029 implement.object:removeFromPhysics()
1030
1031 -- set valid baseVehicle compound
1032 if self.attacherVehicle == nil then
1033 setIsCompound(baseVehicleComponentNode, true)
1034 end
1035 -- set attachedVehicle to compound child
1036 setIsCompoundChild(attachedVehicleComponentNode, true)
1037
1038 -- set direction and local position
1039 local dirX, dirY, dirZ = localDirectionToLocal(attachedVehicleComponentNode, implementJoint.node, 0, 0, 1)
1040 local upX, upY, upZ = localDirectionToLocal(attachedVehicleComponentNode, implementJoint.node, 0, 1, 0)
1041 setDirection(attachedVehicleComponentNode, dirX, dirY, dirZ, upX, upY, upZ)
1042 local x,y,z = localToLocal(attachedVehicleComponentNode, implementJoint.node, 0, 0, 0)
1043 setTranslation(attachedVehicleComponentNode, x, y, z)
1044 link(attacherJoint.jointTransform, attachedVehicleComponentNode)
1045
1046 -- link visual and set to correct position
1047 if implementJoint.visualNode ~= nil and attacherJoint.jointTransformVisual ~= nil then
1048 local dirX, dirY, dirZ = localDirectionToLocal(implementJoint.visualNode, implementJoint.node, 0, 0, 1)
1049 local upX, upY, upZ = localDirectionToLocal(implementJoint.visualNode, implementJoint.node, 0, 1, 0)
1050 setDirection(implementJoint.visualNode, dirX, dirY, dirZ, upX, upY, upZ)
1051 local x,y,z = localToLocal(implementJoint.visualNode, implementJoint.node, 0, 0, 0)
1052 setTranslation(implementJoint.visualNode, x, y, z)
1053 link(attacherJoint.jointTransformVisual, implementJoint.visualNode)
1054 end
1055
1056 implement.object.isHardAttached = true
1057
1058 -- add to physics again
1059 local currentVehicle = self
1060 while currentVehicle ~= nil do
1061 currentVehicle:addToPhysics()
1062 currentVehicle = currentVehicle.attacherVehicle
1063 end
1064
1065 -- set new joint rootNodes
1066 for _, attacherJoint in pairs(implement.object.attacherJoints) do
1067 attacherJoint.rootNode = self.rootNode
1068 end
1069
1070 for _, impl in pairs(implements) do
1071 implement.object:attachImplement(impl.object, impl.inputJointDescIndex, impl.jointDescIndex, true, impl.implementIndex, impl.moveDown, true)
1072 end
1073
1074 if self.isServer then
1075 self:raiseDirtyFlags(self.vehicleDirtyFlag);
1076 end
1077
1078 return true
1079end

hardDetachImplement

Description
Hard detach implement
Definition
hardDetachImplement(table implement)
Arguments
tableimplementimplement to detach
Code
1084function AttacherJoints:hardDetachImplement(superFunc, implement)
1085 if superFunc ~= nil then
1086 if not superFunc(self, implement) then
1087 return false;
1088 end
1089 end
1090
1091 -- restore original joint rootNode
1092 for _, attacherJoint in pairs(implement.object.attacherJoints) do
1093 attacherJoint.rootNode = attacherJoint.rootNodeBackup
1094 end
1095
1096 local implementJoint = implement.object.attacherJoint
1097
1098 local attachedVehicleComponentNode = implement.object:getParentComponent(implementJoint.node)
1099
1100 local currentVehicle = self
1101 while currentVehicle ~= nil do
1102 currentVehicle:removeFromPhysics()
1103 currentVehicle = currentVehicle.attacherVehicle
1104 end
1105 --implement.object:removeFromPhysics()
1106
1107 setIsCompound(attachedVehicleComponentNode, true)
1108
1109 local x,y,z = getWorldTranslation(attachedVehicleComponentNode)
1110 setTranslation(attachedVehicleComponentNode, x,y,z)
1111 local dirX, dirY, dirZ = localDirectionToWorld(implement.object.rootNode, 0, 0, 1)
1112 local upX, upY, upZ = localDirectionToWorld(implement.object.rootNode, 0, 1, 0)
1113 setDirection(attachedVehicleComponentNode, dirX, dirY, dirZ, upX, upY, upZ)
1114 link(getRootNode(), attachedVehicleComponentNode)
1115
1116 if implementJoint.visualNode ~= nil and getParent(implementJoint.visualNode) ~= implementJoint.visualNodeData.parent then
1117 link(implementJoint.visualNodeData.parent, implementJoint.visualNode, implementJoint.visualNodeData.index)
1118 setRotation(implementJoint.visualNode, implementJoint.visualNodeData.rotation[1], implementJoint.visualNodeData.rotation[2], implementJoint.visualNodeData.rotation[3])
1119 setTranslation(implementJoint.visualNode, implementJoint.visualNodeData.translation[1], implementJoint.visualNodeData.translation[2], implementJoint.visualNodeData.translation[3])
1120 end
1121
1122 local currentVehicle = self
1123 while currentVehicle ~= nil do
1124 currentVehicle:addToPhysics()
1125 currentVehicle = currentVehicle.attacherVehicle
1126 end
1127 implement.object:addToPhysics()
1128 implement.object.isHardAttached = false
1129
1130 if self.isServer then
1131 self:raiseDirtyFlags(self.vehicleDirtyFlag);
1132 end
1133
1134 return true
1135end

detachImplement

Description
Detach implement
Definition
detachImplement(integer implementIndex, boolean noEventSend)
Arguments
integerimplementIndexindex of implement in self.attachedImplements
booleannoEventSendno event send
Return Values
booleansuccesssuccess
Code
1142function AttacherJoints:detachImplement(superFunc, implementIndex, noEventSend)
1143 if superFunc ~= nil then
1144 if not superFunc(self, implementIndex, noEventSend) then
1145 return false;
1146 end
1147 end
1148
1149 if noEventSend == nil or noEventSend == false then
1150 if g_server ~= nil then
1151 g_server:broadcastEvent(VehicleDetachEvent:new(self, self.attachedImplements[implementIndex].object), nil, nil, self);
1152 else
1153 -- Send detach request to server and return
1154 local implement = self.attachedImplements[implementIndex];
1155 if implement.object ~= nil then
1156 g_client:getServerConnection():sendEvent(VehicleDetachEvent:new(self, implement.object));
1157 end
1158 return;
1159 end
1160 end
1161
1162 self:onDetachImplement(implementIndex);
1163
1164 local implement = self.attachedImplements[implementIndex];
1165 local jointDesc;
1166 if implement.object ~= nil then
1167 jointDesc = self.attacherJoints[implement.jointDescIndex];
1168 if jointDesc.transNode ~= nil then
1169 setTranslation(jointDesc.transNode, unpack(jointDesc.transNodeOrgTrans));
1170 end
1171 if not implement.object.isHardAttached then
1172 if self.isServer then
1173 if jointDesc.jointIndex ~= 0 then
1174 removeJoint(jointDesc.jointIndex);
1175 end
1176 end
1177 end
1178 jointDesc.jointIndex = 0;
1179 end
1180
1181 ObjectChangeUtil.setObjectChanges(jointDesc.changeObjects, false, self, self.setMovingToolDirty);
1182
1183 local rootAttacherVehicle = self:getRootAttacherVehicle();
1184 local selectNewImplement = false;
1185
1186 if self.isClient then
1187 if rootAttacherVehicle.selectedImplement == implement then
1188 selectNewImplement = true;
1189 rootAttacherVehicle:setSelectedImplement(nil);
1190 end
1191 end
1192 if implement.object ~= nil then
1193 local object = implement.object;
1194 implement.object:onDetach(self, implement.jointDescIndex);
1195 if implement.object.isHardAttached then
1196 self:hardDetachImplement(implement)
1197 end
1198 implement.object = nil;
1199 if self.isClient then
1200 if jointDesc.topArm ~= nil then
1201 setRotation(jointDesc.topArm.rotationNode, jointDesc.topArm.rotX, jointDesc.topArm.rotY, jointDesc.topArm.rotZ);
1202 if jointDesc.topArm.translationNode ~= nil then
1203 setTranslation(jointDesc.topArm.translationNode, 0, 0, 0);
1204 end
1205 if jointDesc.topArm.scaleNode ~= nil then
1206 setScale(jointDesc.topArm.scaleNode, 1, 1, 1);
1207 end
1208 if jointDesc.topArm.toggleVisibility then
1209 setVisibility(jointDesc.topArm.rotationNode, false);
1210 end
1211 end
1212 if jointDesc.bottomArm ~= nil then
1213 setRotation(jointDesc.bottomArm.rotationNode, jointDesc.bottomArm.rotX, jointDesc.bottomArm.rotY, jointDesc.bottomArm.rotZ);
1214 if jointDesc.bottomArm.translationNode ~= nil then
1215 setTranslation(jointDesc.bottomArm.translationNode, 0, 0, 0);
1216 end
1217 if self.setMovingToolDirty ~= nil then
1218 self:setMovingToolDirty(jointDesc.bottomArm.rotationNode);
1219 end
1220 if jointDesc.bottomArm.toolbar ~= nil then
1221 setVisibility(jointDesc.bottomArm.toolbar, false)
1222 end
1223 end
1224 end
1225 if self.isServer then
1226 -- restore original translation
1227 setTranslation(jointDesc.jointTransform, unpack(jointDesc.jointOrigTrans));
1228 setTranslation(object.attacherJoint.node, unpack(object.attacherJoint.jointOrigTrans));
1229
1230 if jointDesc.rotationNode ~= nil then
1231 setRotation(jointDesc.rotationNode, jointDesc.rotX, jointDesc.rotY, jointDesc.rotZ);
1232 end
1233 end
1234
1235 local unlinkPto = function(object, jointDesc, name)
1236 if jointDesc[name.."Active"] then
1237 local input = object[name.."Input"]
1238 local jointDescOutput = jointDesc[name.."Output"]
1239 local objectOutput = object[name.."Output"]
1240
1241 if input.rootNode ~= nil then
1242 unlink(input.rootNode);
1243 unlink(input.attachNode);
1244
1245 if object.removeWashableNode ~= nil then
1246 object:removeWashableNode(objectOutput.rootNode);
1247 object:removeWashableNode(objectOutput.attachNode);
1248 object:removeWashableNode(objectOutput.dirAndScaleNode);
1249 end
1250 else
1251 unlink(jointDescOutput.rootNode);
1252 unlink(jointDescOutput.attachNode);
1253
1254 if object.removeWashableNode ~= nil then
1255 object:removeWashableNode(jointDescOutput.rootNode);
1256 object:removeWashableNode(jointDescOutput.attachNode);
1257 object:removeWashableNode(jointDescOutput.dirAndScaleNode);
1258 end
1259 end
1260 jointDesc[name.."Active"] = false;
1261 end
1262 end
1263
1264 unlinkPto(object, jointDesc, "pto")
1265 unlinkPto(object, jointDesc, "pto2")
1266
1267
1268 object:onDetached(self, implement.jointDescIndex);
1269 end
1270
1271 table.remove(self.attachedImplements, implementIndex);
1272
1273 if self.isClient then
1274 if selectNewImplement then
1275 local newSelectedImplement = nil;
1276 local newIndex = math.min(implementIndex, table.getn(self.attachedImplements));
1277 if newIndex == 0 then
1278 if self ~= rootAttacherVehicle then
1279 -- select self
1280 newSelectedImplement = self.attacherVehicle:getImplementByObject(self);
1281 end
1282 else
1283 -- select the implement at the new index
1284 newSelectedImplement = self.attachedImplements[newIndex];
1285 end
1286 rootAttacherVehicle:setSelectedImplement(newSelectedImplement);
1287 end
1288 end
1289
1290 self:playDetachSound(jointDesc);
1291
1292 return true;
1293end

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
1300function AttacherJoints:detachImplementByObject(superFunc, object, noEventSend)
1301 if superFunc ~= nil then
1302 if not superFunc(self, object, noEventSend) then
1303 return false;
1304 end
1305 end
1306
1307 for i,implement in ipairs(self.attachedImplements) do
1308 if implement.object == object then
1309 self:detachImplement(i, noEventSend);
1310 break;
1311 end
1312 end
1313
1314 return true;
1315end

setSelectedImplement

Description
Set implement selection
Definition
setSelectedImplement(boolean selectedImplement)
Arguments
booleanselectedImplementselected implement
Return Values
booleansuccesssuccess
Code
1321function AttacherJoints:setSelectedImplement(superFunc, selectedImplement)
1322 if superFunc ~= nil then
1323 if not superFunc(self, selectedImplement) then
1324 return false;
1325 end
1326 end
1327
1328 if selectedImplement == self then
1329 selectedImplement = nil;
1330 end
1331 if selectedImplement ~= self.selectedImplement then
1332 if self.selectedImplement ~= nil and self.selectedImplement.object ~= nil then
1333 self.selectedImplement.object:onDeselect();
1334 end
1335 self.selectedImplement = selectedImplement;
1336 if selectedImplement ~= nil and selectedImplement.object ~= nil then
1337 selectedImplement.object:onSelect();
1338 end
1339 end
1340
1341 return true;
1342end

selectNextSelectableImplement

Description
Selects the next implement in a dfs order, returns if a child or sub*-child was selected
Definition
selectNextSelectableImplement(integer selectedIndex)
Arguments
integerselectedIndexselected index
Return Values
booleansuccesssuccess
Code
1348function AttacherJoints:selectNextSelectableImplement(superFunc, selectedIndex)
1349 local rootAttacherVehicle = self:getRootAttacherVehicle();
1350 if self ~= rootAttacherVehicle and selectedIndex == nil then
1351 return rootAttacherVehicle:selectNextSelectableImplement(superFunc);
1352 end;
1353
1354 if superFunc ~= nil then
1355 if not superFunc(self, selectedIndex) then
1356 return false;
1357 end
1358 end
1359
1360 local numImplements = table.getn(self.attachedImplements);
1361 if numImplements == 0 then
1362 return false;
1363 end
1364
1365 if self == rootAttacherVehicle then
1366 if self.selectedImplement ~= nil and self.selectedImplement.object ~= nil then
1367 local curVehicle = self.selectedImplement.object;
1368 selectedIndex = 0;
1369 while curVehicle ~= nil and curVehicle ~= self do
1370 if curVehicle:selectNextSelectableImplement(selectedIndex) then
1371 return true;
1372 end
1373 if curVehicle.attacherVehicle ~= nil then
1374 selectedIndex = curVehicle.attacherVehicle:getImplementIndexByObject(curVehicle);
1375 end
1376 curVehicle = curVehicle.attacherVehicle;
1377 end
1378 end
1379 end
1380
1381 if selectedIndex == nil then
1382 selectedIndex = 0;
1383 end
1384
1385 for i=1, numImplements do
1386 local index = selectedIndex+i;
1387 if index > numImplements then
1388 if self.attacherVehicle == nil and not self:getIsSelectable() then
1389 -- loop around if this is the root vehicle and it is not selectable
1390 index = index - selectedIndex;
1391 else
1392 if self.attacherVehicle == nil then
1393 self:setSelectedImplement(nil);
1394 end
1395 return false;
1396 end
1397 end
1398 local implement = self.attachedImplements[index];
1399 if implement.object ~= nil then
1400 if implement.object:getIsSelectable() then
1401 rootAttacherVehicle:setSelectedImplement(implement);
1402 return true;
1403 elseif implement.object:selectNextSelectableImplement() then
1404 return true;
1405 end
1406 end
1407 end
1408
1409 return false;
1410end

getIsSelectable

Description
Returns true if is selectable
Definition
getIsSelectable()
Return Values
booleanisSelectablevehicle is selectable
Code
1415function AttacherJoints:getIsSelectable(superFunc)
1416 if superFunc ~= nil then
1417 if not superFunc(self) then
1418 return false;
1419 end
1420 end
1421
1422 if self.isSelectable then
1423 return true;
1424 end
1425 if self.attacherVehicle ~= nil and self.attacherJoint.allowsLowering then
1426 local implement = self.attacherVehicle.getImplementByObject(self);
1427 if implement ~= nil then
1428 local jointDesc = self.attacherVehicle.attacherJoints[implement.jointDescIndex];
1429 if jointDesc.allowsLowering then
1430 return true;
1431 end
1432 end
1433 end
1434 return false
1435end

getIsAChildSelectable

Description
Returns true if a attached vehicle is selectable
Definition
getIsAChildSelectable()
Return Values
booleanchildIsSelectablea child is selectable
Code
1440function AttacherJoints:getIsAChildSelectable(superFunc)
1441 if superFunc ~= nil then
1442 if not superFunc(self) then
1443 return false;
1444 end
1445 end
1446
1447 for _,implement in pairs(self.attachedImplements) do
1448 if implement.object ~= nil then
1449 if implement.object:getIsSelectable() or implement.object:getIsAChildSelectable() then
1450 return true;
1451 end
1452 end
1453 end
1454 return false
1455end

playAttachSound

Description
Play attach sound
Definition
playAttachSound(table jointDesc)
Arguments
tablejointDescjoint desc
Return Values
booleansuccesssuccess
Code
1461function AttacherJoints:playAttachSound(superFunc, jointDesc)
1462 if superFunc ~= nil then
1463 if not superFunc(self) then
1464 return false;
1465 end
1466 end
1467
1468 if self.isClient and self:getIsActiveForSound() then
1469 if jointDesc ~= nil and jointDesc.sampleAttach ~= nil then
1470 SoundUtil.playSample(jointDesc.sampleAttach, 1, 0, nil);
1471 else
1472 SoundUtil.playSample(self.sampleAttach, 1, 0, nil);
1473 end
1474 end
1475
1476 return true;
1477end

playDetachSound

Description
Play detach sound
Definition
playDetachSound(table jointDesc)
Arguments
tablejointDescjoint desc
Return Values
booleansuccesssuccess
Code
1483function AttacherJoints:playDetachSound(superFunc, jointDesc)
1484 if superFunc ~= nil then
1485 if not superFunc(self) then
1486 return false;
1487 end
1488 end
1489
1490 if self.isClient and self:getIsActiveForSound() then
1491 if jointDesc ~= nil and jointDesc.sampleAttach ~= nil then
1492 SoundUtil.playSample(jointDesc.sampleAttach, 1, 0, nil);
1493 else
1494 SoundUtil.playSample(self.sampleAttach, 1, 0, nil);
1495 end
1496 end
1497
1498 return true;
1499end

detachingIsPossible

Description
Returns true if it is possible to detach selected implement
Definition
detachingIsPossible()
Return Values
booleanpossibleToDetachpossible to detach selected implement
Code
1504function AttacherJoints:detachingIsPossible(superFunc)
1505 if superFunc ~= nil then
1506 if not superFunc(self) then
1507 return false;
1508 end
1509 end
1510
1511 if self.selectedImplement ~= nil then
1512 local object = self.selectedImplement.object;
1513 if object ~= nil and object.attacherVehicle ~= nil and object:isDetachAllowed() then
1514 local implementIndex = object.attacherVehicle:getImplementIndexByObject(object);
1515 if implementIndex ~= nil then
1516 return true;
1517 end
1518 end
1519 end
1520 return false;
1521end

getImplementIndexByJointDescIndex

Description
Returns implement index in 'self.attachedImplements' by jointDescIndex
Definition
getImplementIndexByJointDescIndex(integer jointDescIndex)
Arguments
integerjointDescIndexjoint desc index
Return Values
integerindexindex of implement
Code
1527function AttacherJoints:getImplementIndexByJointDescIndex(superFunc, jointDescIndex)
1528 if superFunc ~= nil then
1529 local implIndex = superFunc(self, jointDescIndex);
1530 if implIndex ~= nil then
1531 return implIndex;
1532 end
1533 end
1534
1535 for i=1, table.getn(self.attachedImplements) do
1536 if self.attachedImplements[i].jointDescIndex == jointDescIndex then
1537 return i;
1538 end
1539 end
1540 return nil;
1541end

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
1547function AttacherJoints:getImplementIndexByObject(superFunc, object)
1548 if superFunc ~= nil then
1549 local implIndex = superFunc(self, object);
1550 if implIndex ~= nil then
1551 return implIndex;
1552 end
1553 end
1554
1555 for i=1, table.getn(self.attachedImplements) do
1556 if self.attachedImplements[i].object == object then
1557 return i;
1558 end
1559 end
1560 return nil;
1561end

getImplementByObject

Description
Returns implement by object
Definition
getImplementByObject(table object)
Arguments
tableobjectobject of attached implement
Return Values
tableimplementimplement
Code
1567function AttacherJoints:getImplementByObject(superFunc, object)
1568 if superFunc ~= nil then
1569 local impl = superFunc(self, object);
1570 if impl ~= nil then
1571 return impl;
1572 end
1573 end
1574
1575 for i=1, table.getn(self.attachedImplements) do
1576 if self.attachedImplements[i].object == object then
1577 return self.attachedImplements[i];
1578 end
1579 end
1580 return nil;
1581end

onActivate

Description
Called on activate
Definition
onActivate()
Code
1585function AttacherJoints:onActivate()
1586 self:onActivateAttachments();
1587end

onDeactivate

Description
Called on deactivate
Definition
onDeactivate()
Code
1591function AttacherJoints:onDeactivate()
1592 self:onDeactivateAttachments();
1593end

onDeactivateSounds

Description
Called on deactivating sounds
Definition
onDeactivateSounds()
Code
1597function AttacherJoints:onDeactivateSounds()
1598 self:onDeactivateAttachmentsSounds();
1599end

onDeactivateLights

Description
Called on deactivating lights
Definition
onDeactivateLights()
Code
1603function AttacherJoints:onDeactivateLights()
1604 self:onDeactivateAttachmentsLights();
1605end

onDeactivateAttachments

Description
Call "onDeactivate" on all attachments
Definition
onDeactivateAttachments()
Return Values
booleansuccesssuccess
Code
1610function AttacherJoints:onDeactivateAttachments(superFunc)
1611 if superFunc ~= nil then
1612 if not superFunc(self) then
1613 return false;
1614 end
1615 end
1616
1617 for _,v in pairs(self.attachedImplements) do
1618 if v.object ~= nil then
1619 v.object:onDeactivate();
1620 end
1621 end
1622
1623 return true;
1624end

onActivateAttachments

Description
Call "onActivate" on all attachments
Definition
onActivateAttachments()
Return Values
booleansuccesssuccess
Code
1629function AttacherJoints:onActivateAttachments(superFunc)
1630 if superFunc ~= nil then
1631 if not superFunc(self) then
1632 return false;
1633 end
1634 end
1635
1636 for _,v in pairs(self.attachedImplements) do
1637 if v.object ~= nil then
1638 v.object:onActivate();
1639 end
1640 end
1641
1642 return true;
1643end

onDeactivateAttachmentsSounds

Description
Call "onDeactivateSounds" on all attachments
Definition
onDeactivateAttachmentsSounds()
Return Values
booleansuccesssuccess
Code
1648function AttacherJoints:onDeactivateAttachmentsSounds(superFunc)
1649 if superFunc ~= nil then
1650 if not superFunc(self) then
1651 return false;
1652 end
1653 end
1654
1655 for _,v in pairs(self.attachedImplements) do
1656 if v.object ~= nil then
1657 v.object:onDeactivateSounds();
1658 end
1659 end
1660
1661 return true;
1662end

onDeactivateAttachmentsLights

Description
Call "onDeactivateLights" on all attachments
Definition
onDeactivateAttachmentsLights()
Return Values
booleansuccesssuccess
Code
1667function AttacherJoints:onDeactivateAttachmentsLights(superFunc)
1668 if superFunc ~= nil then
1669 if not superFunc(self) then
1670 return false;
1671 end
1672 end
1673
1674 for _,v in pairs(self.attachedImplements) do
1675 if v.object ~= nil and v.object.onDeactivateLights ~= nil then
1676 v.object:onDeactivateLights();
1677 end
1678 end
1679
1680 return true;
1681end

isLowered

Description
Returns if vehicle is lowered
Definition
isLowered(boolean default)
Arguments
booleandefaultdefault value
Return Values
booleanisLoweredvehicle is lowered

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
1709function AttacherJoints:setJointMoveDown(superFunc, jointDescIndex, moveDown, noEventSend)
1710 if superFunc ~= nil then
1711 if not superFunc(self, jointDescIndex, moveDown, noEventSend) then
1712 return false;
1713 end
1714 end
1715
1716 VehicleLowerImplementEvent.sendEvent(self, jointDescIndex, moveDown, noEventSend)
1717 local jointDesc = self.attacherJoints[jointDescIndex]
1718 jointDesc.moveDown = moveDown;
1719
1720 local implementIndex = self:getImplementIndexByJointDescIndex(jointDescIndex);
1721 if implementIndex ~= nil then
1722 local implement = self.attachedImplements[implementIndex];
1723 if implement.object ~= nil and implement.object.onSetLowered ~= nil then
1724 implement.object:onSetLowered(moveDown)
1725 end
1726 end
1727
1728 return true;
1729end

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
1735function AttacherJoints:getIsHardAttachAllowed(superFunc, jointDescIndex)
1736 if superFunc ~= nil then
1737 if not superFunc(self, jointDescIndex) then
1738 return false;
1739 end
1740 end
1741
1742 return self.attacherJoints[jointDescIndex].supportsHardAttach
1743end

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
1751function AttacherJoints:loadAttacherJointFromXML(superFunc, attacherJoint, xmlFile, baseName, index)
1752 if superFunc ~= nil then
1753 if not superFunc(self, attacherJoint, xmlFile, baseName, index) then
1754 return false;
1755 end
1756 end
1757
1758 local node = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. "#index"));
1759 if node == nil then
1760 return false;
1761 end
1762
1763 attacherJoint.jointTransform = node;
1764 attacherJoint.jointOrigRot = { getRotation(attacherJoint.jointTransform) };
1765 attacherJoint.jointOrigTrans = { getTranslation(attacherJoint.jointTransform) };
1766
1767 attacherJoint.jointTransformVisual = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName .. "#indexVisual"));
1768 attacherJoint.supportsHardAttach = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#supportsHardAttach"), true);
1769
1770 local jointTypeStr = getXMLString(xmlFile, baseName.. "#jointType");
1771 local jointType;
1772 if jointTypeStr ~= nil then
1773 jointType = AttacherJoints.jointTypeNameToInt[jointTypeStr];
1774 if jointType == nil then
1775 print("Warning: invalid jointType " .. jointTypeStr);
1776 end
1777 end
1778 if jointType == nil then
1779 jointType = AttacherJoints.JOINTTYPE_IMPLEMENT;
1780 end
1781 attacherJoint.jointType = jointType;
1782 attacherJoint.allowsJointLimitMovement = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#allowsJointLimitMovement"), true);
1783 attacherJoint.allowsLowering = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#allowsLowering"), true);
1784 attacherJoint.isDefaultLowered = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#isDefaultLowered"), false);
1785
1786 if jointType == AttacherJoints.JOINTTYPE_TRAILER or jointType == AttacherJoints.JOINTTYPE_TRAILERLOW then
1787 attacherJoint.allowsLowering = false;
1788 end
1789
1790 self:loadPowerTakeoff(xmlFile, baseName, attacherJoint, "pto");
1791 self:loadPowerTakeoff(xmlFile, baseName, attacherJoint, "pto2");
1792
1793 attacherJoint.canTurnOnImplement = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#canTurnOnImplement"), true);
1794
1795 local rotationNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. "#rotationNode"));
1796 if rotationNode ~= nil then
1797 attacherJoint.rotationNode = rotationNode;
1798 if getXMLString(xmlFile, baseName.."#maxRot") ~= nil then
1799 print("Warning: attacherJoint attribute 'maxRot' is not supported anymore. Use 'lowerRotation' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1800 end
1801 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#lowerRotation"));
1802 attacherJoint.lowerRotation = { math.rad(Utils.getNoNil(x, 0)), math.rad(Utils.getNoNil(y, 0)), math.rad(Utils.getNoNil(z, 0)) };
1803
1804 if getXMLString(xmlFile, baseName.."#minRot") ~= nil then
1805 print("Warning: attacherJoint attribute 'minRot' is not supported anymore. Use 'upperRotation' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1806 end
1807 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#upperRotation"));
1808 local rx,ry,rz = getRotation(rotationNode);
1809 attacherJoint.upperRotation = { Utils.getNoNilRad(x, rx), Utils.getNoNilRad(y, ry), Utils.getNoNilRad(z, rz) };
1810
1811 local startRot = Utils.getRadiansFromString(getXMLString(xmlFile, baseName.."#startRotation"), 3);
1812 if startRot ~= nil then
1813 attacherJoint.rotX, attacherJoint.rotY, attacherJoint.rotZ = startRot[1],startRot[2],startRot[3];
1814 else
1815 attacherJoint.rotX, attacherJoint.rotY, attacherJoint.rotZ = getRotation(rotationNode);
1816 end
1817 end
1818 local rotationNode2 = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. "#rotationNode2"));
1819 if rotationNode2 ~= nil then
1820 if getXMLString(xmlFile, baseName.."#maxRot2") ~= nil then
1821 print("Warning: attacherJoint attribute 'maxRot2' is not supported anymore. Use 'lowerRotation2' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1822 end
1823 attacherJoint.rotationNode2 = rotationNode2;
1824 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#lowerRotation2"));
1825 if x ~= nil and y ~= nil and z ~= nil then
1826 attacherJoint.lowerRotation2 = { math.rad(Utils.getNoNil(x, 0)), math.rad(Utils.getNoNil(y, 0)), math.rad(Utils.getNoNil(z, 0)) };
1827 else
1828 attacherJoint.lowerRotation2 = { -attacherJoint.lowerRotation[1], -attacherJoint.lowerRotation[2], -attacherJoint.lowerRotation[3] };
1829 end
1830
1831 if getXMLString(xmlFile, baseName.."#minRot2") ~= nil then
1832 print("Warning: attacherJoint attribute 'minRot2' is not supported anymore. Use 'upperRotation2' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1833 end
1834 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#upperRotation2"));
1835 if x ~= nil and y ~= nil and z ~= nil then
1836 attacherJoint.upperRotation2 = { math.rad(Utils.getNoNil(x, 0)), math.rad(Utils.getNoNil(y, 0)), math.rad(Utils.getNoNil(z, 0)) };
1837 else
1838 attacherJoint.upperRotation2 = { -attacherJoint.upperRotation[1], -attacherJoint.upperRotation[2], -attacherJoint.upperRotation[3] };
1839 end
1840 end
1841
1842 if getXMLString(xmlFile, baseName.."#maxRotDistanceToGround") ~= nil then
1843 print("Warning: attacherJoint attribute 'maxRotDistanceToGround' is not supported anymore. Use 'lowerDistanceToGround' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1844 end
1845 -- lowerDistanceToGround is a mandatory attribute if a rotationNode is available
1846 if attacherJoint.rotationNode ~= nil and getXMLFloat(xmlFile, baseName.."#lowerDistanceToGround") == nil then
1847 print("Warning: Missing 'lowerDistanceToGround' for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1848 end
1849 attacherJoint.lowerDistanceToGround = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#lowerDistanceToGround"), 0.7);
1850 if getXMLString(xmlFile, baseName.."#minRotDistanceToGround") ~= nil then
1851 print("Warning: attacherJoint attribute 'minRotDistanceToGround' is not supported anymore. Use 'upperDistanceToGround' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1852 end
1853 -- upperDistanceToGround is a mandatory attribute if a rotationNode is available
1854 if attacherJoint.rotationNode ~= nil and getXMLFloat(xmlFile, baseName.."#upperDistanceToGround") == nil then
1855 print("Warning: Missing 'upperDistanceToGround' for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1856 end
1857 attacherJoint.upperDistanceToGround = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#upperDistanceToGround"), 1.0);
1858 if getXMLString(xmlFile, baseName.."#maxRotRotationOffset") ~= nil then
1859 print("Warning: attacherJoint attribute 'maxRotRotationOffset' is not supported anymore. Use 'lowerRotationOffset' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1860 end
1861 attacherJoint.lowerRotationOffset = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#lowerRotationOffset"), 0));
1862 if getXMLString(xmlFile, baseName.."#minRotRotationOffset") ~= nil then
1863 print("Warning: attacherJoint attribute 'minRotRotationOffset' is not supported anymore. Use 'upperRotationOffset' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1864 end
1865 attacherJoint.upperRotationOffset = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#upperRotationOffset"), 0));
1866
1867 attacherJoint.lockDownRotLimit = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#lockDownRotLimit"), false);
1868 attacherJoint.lockUpRotLimit = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#lockUpRotLimit"), false);
1869 -- only use translimit in +y. Set -y to 0;
1870 attacherJoint.lockDownTransLimit = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#lockDownTransLimit"), true);
1871 attacherJoint.lockUpTransLimit = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#lockUpTransLimit"), false);
1872
1873 attacherJoint.aiAllowTurnBackward = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#aiAllowTurnBackward"), false);
1874
1875 if getXMLString(xmlFile, baseName.."#maxRotLimit") ~= nil then
1876 print("Warning: attacherJoint attribute 'maxRotLimit' is not supported anymore. Use 'lowerRotLimit' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1877 end
1878 if getXMLString(xmlFile, baseName.."#minRotLimit") ~= nil then
1879 print("Warning: attacherJoint attribute 'minRotLimit' is not supported anymore. Use 'upperRotLimit' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1880 end
1881
1882 local lowerRotLimitStr = "20 20 20"
1883 if jointType ~= AttacherJoints.JOINTTYPE_IMPLEMENT then
1884 lowerRotLimitStr = "0 0 0"
1885 end
1886 local lx, ly, lz = Utils.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, baseName.."#lowerRotLimit"), lowerRotLimitStr));
1887 attacherJoint.lowerRotLimit = {};
1888 attacherJoint.lowerRotLimit[1] = math.rad(math.abs(Utils.getNoNil(lx, 20)));
1889 attacherJoint.lowerRotLimit[2] = math.rad(math.abs(Utils.getNoNil(ly, 20)));
1890 attacherJoint.lowerRotLimit[3] = math.rad(math.abs(Utils.getNoNil(lz, 20)));
1891 local ux, uy, uz = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#upperRotLimit"));
1892 attacherJoint.upperRotLimit = {};
1893 attacherJoint.upperRotLimit[1] = math.rad(math.abs(Utils.getNoNil(Utils.getNoNil(ux, lx), 20)));
1894 attacherJoint.upperRotLimit[2] = math.rad(math.abs(Utils.getNoNil(Utils.getNoNil(uy, ly), 20)));
1895 attacherJoint.upperRotLimit[3] = math.rad(math.abs(Utils.getNoNil(Utils.getNoNil(uz, lz), 20)));
1896
1897 if getXMLString(xmlFile, baseName.."#maxTransLimit") ~= nil then
1898 print("Warning: attacherJoint attribute 'maxTransLimit' is not supported anymore. Use 'lowerTransLimit' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1899 end
1900 if getXMLString(xmlFile, baseName.."#minTransLimit") ~= nil then
1901 print("Warning: attacherJoint attribute 'minTransLimit' is not supported anymore. Use 'upperTransLimit' instead for attacherJoint "..(index+1).." in "..self.configFileName.."!");
1902 end
1903
1904 local lowerTransLimitStr = "0.5 0.5 0.5"
1905 if jointType ~= AttacherJoints.JOINTTYPE_IMPLEMENT then
1906 lowerTransLimitStr = "0 0 0"
1907 end
1908 local lx, ly, lz = Utils.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, baseName.."#lowerTransLimit"), lowerTransLimitStr));
1909 attacherJoint.lowerTransLimit = {};
1910 attacherJoint.lowerTransLimit[1] = math.abs(Utils.getNoNil(lx, 0));
1911 attacherJoint.lowerTransLimit[2] = math.abs(Utils.getNoNil(ly, 0));
1912 attacherJoint.lowerTransLimit[3] = math.abs(Utils.getNoNil(lz, 0));
1913 local ux, uy, uz = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#upperTransLimit"));
1914 attacherJoint.upperTransLimit = {};
1915 attacherJoint.upperTransLimit[1] = math.abs(Utils.getNoNil(Utils.getNoNil(ux, lx), 0));
1916 attacherJoint.upperTransLimit[2] = math.abs(Utils.getNoNil(Utils.getNoNil(uy, ly), 0));
1917 attacherJoint.upperTransLimit[3] = math.abs(Utils.getNoNil(Utils.getNoNil(uz, lz), 0));
1918
1919
1920 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#jointPositionOffset"));
1921 attacherJoint.jointPositionOffset = {};
1922 attacherJoint.jointPositionOffset[1] = Utils.getNoNil(x, 0);
1923 attacherJoint.jointPositionOffset[2] = Utils.getNoNil(y, 0);
1924 attacherJoint.jointPositionOffset[3] = Utils.getNoNil(z, 0);
1925
1926 attacherJoint.transNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.."#transNode"));
1927 if attacherJoint.transNode ~= nil then
1928 attacherJoint.transNodeOrgTrans = {getTranslation(attacherJoint.transNode)};
1929 attacherJoint.transNodeMinY = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#transNodeMinY"), attacherJoint.jointOrigTrans[2]);
1930 attacherJoint.transNodeMaxY = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#transNodeMaxY"), attacherJoint.jointOrigTrans[2]);
1931 _, attacherJoint.transNodeOffsetY, _ = localToLocal(attacherJoint.jointTransform, attacherJoint.transNode, 0, 0, 0)
1932 _, attacherJoint.transNodeMinY, _ = localToLocal(getParent(attacherJoint.transNode), self.rootNode, 0, attacherJoint.transNodeMinY, 0)
1933 _, attacherJoint.transNodeMaxY, _ = localToLocal(getParent(attacherJoint.transNode), self.rootNode, 0, attacherJoint.transNodeMaxY, 0)
1934 attacherJoint.transNodeHeight = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#transNodeHeight"), 0.1)
1935 end
1936
1937 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#rotLimitSpring"));
1938 attacherJoint.rotLimitSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) };
1939 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#rotLimitDamping"));
1940 attacherJoint.rotLimitDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) };
1941 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#rotLimitForceLimit"));
1942 attacherJoint.rotLimitForceLimit = { Utils.getNoNil(x, -1), Utils.getNoNil(y, -1), Utils.getNoNil(z, -1) };
1943
1944 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#transLimitSpring"));
1945 attacherJoint.transLimitSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) };
1946 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#transLimitDamping"));
1947 attacherJoint.transLimitDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) };
1948 local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.."#transLimitForceLimit"));
1949 attacherJoint.transLimitForceLimit = { Utils.getNoNil(x, -1), Utils.getNoNil(y, -1), Utils.getNoNil(z, -1) };
1950
1951 attacherJoint.moveDefaultTime = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#moveTime"), 0.5)*1000;
1952 attacherJoint.moveTime = attacherJoint.moveDefaultTime;
1953
1954 attacherJoint.enableCollision = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#enableCollision"), false);
1955
1956 local topArmFilename = getXMLString(xmlFile, baseName.. ".topArm#filename");
1957 if topArmFilename ~= nil then
1958 local baseNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".topArm#baseNode"));
1959 if baseNode ~= nil then
1960 local i3dNode = Utils.loadSharedI3DFile(topArmFilename,self.baseDirectory, false, false, false);
1961 if i3dNode ~= 0 then
1962 local rootNode = getChildAt(i3dNode, 0);
1963 link(baseNode, rootNode);
1964 delete(i3dNode);
1965 setTranslation(rootNode, 0,0,0);
1966 local translationNode = getChildAt(rootNode, 0);
1967 local referenceNode = getChildAt(translationNode, 0);
1968
1969
1970 local topArm = {};
1971 topArm.rotationNode = rootNode;
1972 topArm.rotX, topArm.rotY, topArm.rotZ = 0,0,0;
1973 topArm.translationNode = translationNode;
1974
1975 local _,_,referenceDistance = getTranslation(referenceNode);
1976 topArm.referenceDistance = referenceDistance;
1977
1978 topArm.zScale = 1;
1979 local zScale = Utils.sign(Utils.getNoNil(getXMLFloat(xmlFile, baseName.. ".topArm#zScale"), 1));
1980 if zScale < 0 then
1981 topArm.rotY = math.pi;
1982 setRotation(rootNode, topArm.rotX, topArm.rotY, topArm.rotZ);
1983 end
1984
1985 if getNumOfChildren(rootNode) > 1 then
1986 topArm.scaleNode = getChildAt(rootNode, 1);
1987 local scaleReferenceNode = getChildAt(topArm.scaleNode, 0);
1988 local _,_,scaleReferenceDistance = getTranslation(scaleReferenceNode);
1989 topArm.scaleReferenceDistance = scaleReferenceDistance;
1990 end
1991
1992 topArm.toggleVisibility = Utils.getNoNil(getXMLBool(xmlFile, baseName.. ".topArm#toggleVisibility"), false);
1993 if topArm.toggleVisibility then
1994 setVisibility(topArm.rotationNode, false);
1995 end
1996
1997 attacherJoint.topArm = topArm;
1998 end
1999 end
2000 else
2001 local rotationNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".topArm#rotationNode"));
2002 local translationNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".topArm#translationNode"));
2003 local referenceNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".topArm#referenceNode"));
2004 if rotationNode ~= nil then
2005 local topArm = {};
2006 topArm.rotationNode = rotationNode;
2007 topArm.rotX, topArm.rotY, topArm.rotZ = getRotation(rotationNode);
2008 if translationNode ~= nil and referenceNode ~= nil then
2009 topArm.translationNode = translationNode;
2010
2011 local x,y,z = getTranslation(translationNode);
2012 if math.abs(x) >= 0.0001 or math.abs(y) >= 0.0001 or math.abs(z) >= 0.0001 then
2013 print("Warning: translation of topArm of attacherJoint "..(index+1).." is not 0/0/0 in '"..self.configFileName.."'");
2014 end
2015 local ax, ay, az = getWorldTranslation(referenceNode);
2016 local bx, by, bz = getWorldTranslation(translationNode);
2017 topArm.referenceDistance = Utils.vector3Length(ax-bx, ay-by, az-bz);
2018 end
2019 topArm.zScale = Utils.sign(Utils.getNoNil(getXMLFloat(xmlFile, baseName.. ".topArm#zScale"), 1));
2020 topArm.toggleVisibility = Utils.getNoNil(getXMLBool(xmlFile, baseName.. ".topArm#toggleVisibility"), false);
2021 if topArm.toggleVisibility then
2022 setVisibility(topArm.rotationNode, false);
2023 end
2024
2025 attacherJoint.topArm = topArm;
2026 end
2027 end
2028
2029 local rotationNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".bottomArm#rotationNode"));
2030 local translationNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".bottomArm#translationNode"));
2031 local referenceNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".bottomArm#referenceNode"));
2032 if rotationNode ~= nil then
2033 local bottomArm = {};
2034 bottomArm.rotationNode = rotationNode;
2035 local startRot = Utils.getRadiansFromString(getXMLString(xmlFile, baseName..".bottomArm#startRotation"), 3);
2036 if startRot ~= nil then
2037 bottomArm.rotX, bottomArm.rotY, bottomArm.rotZ = startRot[1],startRot[2],startRot[3];
2038 else
2039 bottomArm.rotX, bottomArm.rotY, bottomArm.rotZ = getRotation(rotationNode);
2040 end
2041 if translationNode ~= nil and referenceNode ~= nil then
2042 bottomArm.translationNode = translationNode;
2043
2044 local x,y,z = getTranslation(translationNode);
2045 if math.abs(x) >= 0.0001 or math.abs(y) >= 0.0001 or math.abs(z) >= 0.0001 then
2046 print("Warning: translation of bottomArm '"..getName(translationNode).."' of attacherJoint "..(index+1).." is "..math.abs(x) .. "/" .. math.abs(y) .. "/" .. math.abs(z) .. "! Should be 0/0/0! ("..self.configFileName..")");
2047 end
2048 local ax, ay, az = getWorldTranslation(referenceNode);
2049 local bx, by, bz = getWorldTranslation(translationNode);
2050 bottomArm.referenceDistance = Utils.vector3Length(ax-bx, ay-by, az-bz);
2051 end
2052 bottomArm.zScale = Utils.sign(Utils.getNoNil(getXMLFloat(xmlFile, baseName.. ".bottomArm#zScale"), 1));
2053 bottomArm.lockDirection = Utils.getNoNil(getXMLBool(xmlFile, baseName.. ".bottomArm#lockDirection"), true);
2054
2055 if jointType == AttacherJoints.JOINTTYPE_IMPLEMENT then
2056 local toolbarFilename = Utils.getNoNil(getXMLString(xmlFile, baseName.. ".toolbar#filename"), "$data/shared/vehicleParts/toolbar.i3d");
2057 local i3dNode = Utils.loadSharedI3DFile(toolbarFilename, self.baseDirectory, false, false, false);
2058 if i3dNode ~= 0 then
2059 local rootNode = getChildAt(i3dNode, 0);
2060 link(referenceNode, rootNode);
2061 delete(i3dNode);
2062 setTranslation(rootNode, 0,0,0);
2063 bottomArm.toolbar = rootNode
2064 setVisibility(rootNode, false)
2065 end
2066 end
2067
2068 attacherJoint.bottomArm = bottomArm;
2069 end
2070
2071 local sample = {}
2072 SoundUtil.loadSample(xmlFile, sample, baseName..".attachSound", nil, self.baseDirectory);
2073 if sample.sample ~= nil then
2074 attacherJoint.sampleAttach = sample
2075 end
2076
2077 attacherJoint.steeringBarLeftNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".steeringBars#leftNode"));
2078 attacherJoint.steeringBarRightNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. ".steeringBars#rightNode"));
2079
2080 attacherJoint.changeObjects = {};
2081 ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, baseName, attacherJoint.changeObjects, self.components, self);
2082 -- ObjectChangeUtil.setObjectChanges(attacherJoint.changeObjects, false, self, self.setMovingToolDirty)
2083
2084 attacherJoint.rootNode = Utils.getNoNil(Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.."#rootNode")), self.components[1].node);
2085 attacherJoint.rootNodeBackup = attacherJoint.rootNode
2086 attacherJoint.jointIndex = 0;
2087
2088 local schemaKey = baseName.. ".schema"
2089 if hasXMLProperty(xmlFile, schemaKey) then
2090 local x, y = Utils.getVectorFromString(getXMLString(xmlFile, schemaKey.."#position"));
2091 local rotation = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, schemaKey.."#rotation"), 0));
2092 local invertX = Utils.getNoNil(getXMLBool(xmlFile, schemaKey.."#invertX"), false);
2093 local liftedOffsetX, liftedOffsetY = Utils.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, schemaKey.."#liftedOffset"), "0 5"));
2094 table.insert(self.schemaOverlay.attacherJoints, {x=x,y=y, rotation=rotation,invertX=invertX,liftedOffsetX=liftedOffsetX,liftedOffsetY=liftedOffsetY});
2095 else
2096 print("Warning: Missing schema overlay for '".. baseName .."' in '"..self.configFileName.."'")
2097 end
2098
2099 return true;
2100end

validateAttacherJoint

Description
Currently not used
Definition
validateAttacherJoint()

loadPowerTakeoff

Description
Load pto from xml
Definition
loadPowerTakeoff(integer fileId, string baseName, table entry, string name)
Arguments
integerfileIdxml file id
stringbaseNamebaseName
tableentryentry to add the pto
stringnamename of pto
Code
2117function AttacherJoints:loadPowerTakeoff(superFunc, xmlFile, baseName, entry, name)
2118 if superFunc ~= nil then
2119 superFunc(self, xmlFile, baseName, entry, name)
2120 end
2121 local ptoOutputNode = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.. "#"..name.."OutputNode"));
2122 if ptoOutputNode ~= nil then
2123 local filename = Utils.getNoNil(getXMLString(xmlFile, baseName.. "#"..name.."Filename"), "$data/shared/vehicleParts/powerTakeoff.i3d");
2124 if filename ~= "" then
2125 local i3dNode = Utils.loadSharedI3DFile(filename, self.baseDirectory, false, false, false);
2126 if i3dNode ~= 0 then
2127 local rootNode = getChildAt(i3dNode, 0);
2128 unlink(rootNode);
2129 delete(i3dNode);
2130 setTranslation(rootNode, 0,0,0);
2131 local dirAndScaleNode = getChildAt(rootNode, 0);
2132 local attachNode = getChildAt(dirAndScaleNode, 0);
2133 local attachRefNode = getChildAt(attachNode, 0);
2134
2135 local _,_,baseDistance = getTranslation(attachNode);
2136 unlink(attachNode);
2137 local ax, ay, az = getTranslation(attachRefNode);
2138 setTranslation(attachNode, -ax, -ay, -az);
2139
2140 setTranslation(rootNode, ax, ay, az);
2141
2142 if entry[name.."Output"] == nil then
2143 entry[name.."Output"] = {}
2144 end
2145
2146 entry[name.."Output"].node = ptoOutputNode
2147 entry[name.."Output"].rootNode = rootNode
2148 entry[name.."Output"].dirAndScaleNode = dirAndScaleNode
2149 entry[name.."Output"].attachNode = attachNode
2150 entry[name.."Output"].baseDistance = baseDistance
2151 entry[name.."Output"].rotation = 0
2152 end
2153 end
2154 end
2155 entry[name.."Active"] = false;
2156end

updatePowerTakeoff

Description
Update pto direction, scale and rotation
Definition
updatePowerTakeoff(table implement, float dt, string name)
Arguments
tableimplementattached implement
floatdttime since last call in ms
stringnamename
Code
2163function AttacherJoints:updatePowerTakeoff(superFunc, implement, dt, name)
2164 if superFunc ~= nil then
2165 superFunc(self, implement, dt, name)
2166 end
2167 local jointDesc = self.attacherJoints[implement.jointDescIndex];
2168 if jointDesc[name.."Active"] then
2169 local object = implement.object;
2170
2171 local output = jointDesc[name.."Output"]
2172 local input = object[name.."Input"]
2173
2174 local ptoRootNode = output.rootNode;
2175 local ptoAttachNode = output.attachNode;
2176 local ptoDirAndScaleNode = output.dirAndScaleNode;
2177 local ptoBaseDistance = output.baseDistance;
2178 if input.rootNode ~= nil then
2179 ptoRootNode = input.rootNode;
2180 ptoAttachNode = input.attachNode
2181 ptoDirAndScaleNode = input.dirAndScaleNode
2182 ptoBaseDistance = input.baseDistance
2183 end
2184
2185 if input.rotSpeed ~= 0 and object:getIsPowerTakeoffActive() then
2186 output.rotation = (output.rotation + dt*input.rotSpeed) % (2*math.pi);
2187 setRotation(ptoRootNode, 0, 0, output.rotation);
2188 setRotation(ptoAttachNode, 0, 0, output.rotation);
2189 end
2190
2191 local x,y,z = getWorldTranslation(ptoAttachNode);
2192 local dx,dy,dz = worldToLocal(ptoRootNode, x,y,z);
2193 setDirection(ptoDirAndScaleNode, dx, dy, dz, 0, 1, 0);
2194
2195 local _,_,dist = worldToLocal(ptoDirAndScaleNode, x,y,z);
2196 setScale(ptoDirAndScaleNode, 1, 1, dist/ptoBaseDistance);
2197 end
2198end