483 | function PowerTakeOffs:attachPowerTakeOff(attachableObject, inputJointDescIndex, jointDescIndex) |
484 | local spec = self.spec_powerTakeOffs |
485 | local outputs = self:getOutputPowerTakeOffsByJointDescIndex(jointDescIndex) |
486 | |
487 | for _, output in ipairs(outputs) do |
488 | if attachableObject.getInputPowerTakeOffsByJointDescIndexAndName ~= nil then |
489 | local inputs = attachableObject:getInputPowerTakeOffsByJointDescIndexAndName(inputJointDescIndex, output.ptoName) |
490 | for _, input in ipairs(inputs) do |
491 | output.connectedInput = input |
492 | output.connectedVehicle = attachableObject |
493 | input.connectedVehicle = self |
494 | input.connectedOutput = output |
495 | |
496 | table.insert(spec.delayedPowerTakeOffsMountings, {jointDescIndex=jointDescIndex, input=input, output=output}) |
497 | end |
498 | end |
499 | end |
500 | |
501 | return true |
502 | end |
506 | function PowerTakeOffs:detachPowerTakeOff(detachingVehicle, implement) |
507 | local spec = self.spec_powerTakeOffs |
508 | -- clear delayed mountings |
509 | spec.delayedPowerTakeOffsMountings = {} |
510 | |
511 | local outputs = detachingVehicle:getOutputPowerTakeOffsByJointDescIndex(implement.jointDescIndex) |
512 | |
513 | for _, output in ipairs(outputs) do |
514 | if output.connectedInput ~= nil then |
515 | local input = output.connectedInput |
516 | |
517 | if input.detachFunc ~= nil then |
518 | input.detachFunc(self, input, output) |
519 | end |
520 | |
521 | input.connectedVehicle = nil |
522 | input.connectedOutput = nil |
523 | output.connectedVehicle = nil |
524 | output.connectedInput = nil |
525 | |
526 | ObjectChangeUtil.setObjectChanges(input.objectChanges, false) |
527 | ObjectChangeUtil.setObjectChanges(output.objectChanges, false) |
528 | end |
529 | end |
530 | |
531 | return true |
532 | end |
441 | function PowerTakeOffs:getInputPowerTakeOffsByJointDescIndexAndName(jointDescIndex, ptoName) |
442 | local retInputs = {} |
443 | |
444 | local spec = self.spec_powerTakeOffs |
445 | for _, input in pairs(spec.inputPowerTakeOffs) do |
446 | if input.inputAttacherJointIndices[jointDescIndex] ~= nil then |
447 | if input.ptoName == ptoName then |
448 | table.insert(retInputs, input) |
449 | end |
450 | end |
451 | end |
452 | |
453 | if table.getn(retInputs) == 0 then |
454 | for _, output in pairs(spec.outputPowerTakeOffs) do |
455 | if output.skipToInputAttacherIndex == jointDescIndex then |
456 | for index, _ in pairs(output.attacherJointIndices) do |
457 | local implement = self:getImplementFromAttacherJointIndex(index) |
458 | if implement ~= nil then |
459 | retInputs = implement.object:getInputPowerTakeOffsByJointDescIndexAndName(implement.inputJointDescIndex, ptoName) |
460 | end |
461 | end |
462 | end |
463 | end |
464 | end |
465 | |
466 | return retInputs |
467 | end |
407 | function PowerTakeOffs:getOutputPowerTakeOffsByJointDescIndex(jointDescIndex) |
408 | local retOutputs = {} |
409 | |
410 | local spec = self.spec_powerTakeOffs |
411 | for _, output in pairs(spec.outputPowerTakeOffs) do |
412 | if output.attacherJointIndices[jointDescIndex] ~= nil then |
413 | table.insert(retOutputs, output) |
414 | end |
415 | end |
416 | |
417 | if table.getn(retOutputs) > 0 then |
418 | for _, output in ipairs(retOutputs) do |
419 | if output.skipToInputAttacherIndex ~= nil then |
420 | local secondAttacherVehicle = self:getAttacherVehicle() |
421 | if secondAttacherVehicle ~= nil then |
422 | local ownImplement = secondAttacherVehicle:getImplementByObject(self) |
423 | retOutputs = secondAttacherVehicle:getOutputPowerTakeOffsByJointDescIndex(ownImplement.jointDescIndex) |
424 | break |
425 | end |
426 | end |
427 | end |
428 | end |
429 | |
430 | return retOutputs |
431 | end |
693 | function PowerTakeOffs:loadDoubleJointPowerTakeOff(powerTakeOff, xmlFile, rootNode) |
694 | powerTakeOff.startJoint1 = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.startJoint1#node")) |
695 | powerTakeOff.startJoint2 = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.startJoint2#node")) |
696 | |
697 | powerTakeOff.scalePart = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.scalePart#node")) |
698 | powerTakeOff.scalePartRef = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.scalePart#referenceNode")) |
699 | local _, _, dis = localToLocal(powerTakeOff.scalePartRef, powerTakeOff.scalePart, 0, 0, 0) |
700 | powerTakeOff.scalePartBaseDistance = dis |
701 | |
702 | powerTakeOff.translationPart = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.translationPart#node")) |
703 | powerTakeOff.translationPartRef = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.translationPart#referenceNode")) |
704 | powerTakeOff.translationPartLength = getXMLFloat(xmlFile, "powerTakeOff.translationPart#length") or 0.4 |
705 | |
706 | powerTakeOff.decal = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.translationPart.decal#node")) |
707 | powerTakeOff.decalSize = getXMLFloat(xmlFile, "powerTakeOff.translationPart.decal#size") or 0.1 |
708 | powerTakeOff.decalOffset = getXMLFloat(xmlFile, "powerTakeOff.translationPart.decal#offset") or 0.05 |
709 | powerTakeOff.decalMinOffset = getXMLFloat(xmlFile, "powerTakeOff.translationPart.decal#minOffset") or 0.01 |
710 | |
711 | powerTakeOff.endJoint1 = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.endJoint1#node")) |
712 | powerTakeOff.endJoint1Ref = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.endJoint1#referenceNode")) |
713 | |
714 | powerTakeOff.endJoint2 = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.endJoint2#node")) |
715 | powerTakeOff.linkNode = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.linkNode#node")) |
716 | |
717 | local _, _, betweenLength = localToLocal(powerTakeOff.translationPart, powerTakeOff.translationPartRef, 0, 0, 0) |
718 | local _, _, ptoLength = localToLocal(powerTakeOff.startNode, powerTakeOff.linkNode, 0, 0, 0) |
719 | powerTakeOff.betweenLength = math.abs(betweenLength) |
720 | powerTakeOff.connectorLength = math.abs(ptoLength) - math.abs(betweenLength) |
721 | |
722 | setTranslation(powerTakeOff.linkNode, 0, 0, 0) |
723 | setRotation(powerTakeOff.linkNode, 0, 0, 0) |
724 | |
725 | powerTakeOff.updateFunc = PowerTakeOffs.updateDoubleJointPowerTakeOff |
726 | powerTakeOff.updateDistanceFunc = PowerTakeOffs.updateDistanceOfTypedPowerTakeOff |
727 | powerTakeOff.attachFunc = PowerTakeOffs.attachTypedPowerTakeOff |
728 | powerTakeOff.detachFunc = PowerTakeOffs.detachTypedPowerTakeOff |
729 | end |
832 | function PowerTakeOffs:loadExtraDependentParts(superFunc, xmlFile, baseName, entry) |
833 | if not superFunc(self, xmlFile, baseName, entry) then |
834 | return false |
835 | end |
836 | |
837 | local indices = StringUtil.getVectorNFromString(getXMLString(xmlFile, baseName.. ".powerTakeOffs#indices")) |
838 | if indices ~= nil then |
839 | entry.powerTakeOffs = {} |
840 | |
841 | for i=1, table.getn(indices) do |
842 | table.insert(entry.powerTakeOffs, indices[i]) |
843 | end |
844 | end |
845 | |
846 | local localIndices = StringUtil.getVectorNFromString(getXMLString(xmlFile, baseName.. ".powerTakeOffs#localIndices")) |
847 | if localIndices ~= nil then |
848 | entry.localPowerTakeOffs = {} |
849 | |
850 | for i=1, table.getn(localIndices) do |
851 | table.insert(entry.localPowerTakeOffs, localIndices[i]) |
852 | end |
853 | end |
854 | |
855 | return true |
856 | end |
278 | function PowerTakeOffs:loadInputPowerTakeOff(xmlFile, baseName, entry) |
279 | local inputNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.."#inputNode"), self.i3dMappings) |
280 | if inputNode == nil then |
281 | g_logManager:xmlWarning(self.configFileName, "Pto input needs to have a valid 'inputNode' in '%s'", baseName) |
282 | return false |
283 | end |
284 | |
285 | local inputAttacherJointIndices = {} |
286 | local inputAttacherJointIndicesStr = getXMLString(xmlFile, baseName .. "#inputAttacherJointIndices") |
287 | |
288 | if inputAttacherJointIndicesStr == nil then |
289 | g_logManager:xmlWarning(self.configFileName, "Pto output needs to have valid 'inputAttacherJointIndices' in '%s'", baseName) |
290 | return false |
291 | else |
292 | local indices = {StringUtil.getVectorFromString(inputAttacherJointIndicesStr)} |
293 | for _, index in ipairs(indices) do |
294 | if self:getInputAttacherJointByJointDescIndex(index) == nil then |
295 | g_logManager:xmlWarning(self.configFileName, "The given inputAttacherJointIndex '%d' for '%s' can't be resolved into a valid inputAttacherJoint", index, baseName) |
296 | return false |
297 | else |
298 | inputAttacherJointIndices[index] = true |
299 | end |
300 | end |
301 | end |
302 | |
303 | entry.inputNode = inputNode |
304 | entry.detachNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.."#detachNode"), self.i3dMappings) |
305 | entry.inputAttacherJointIndices = inputAttacherJointIndices |
306 | |
307 | entry.aboveAttacher = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#aboveAttacher"), true) |
308 | |
309 | entry.color = ConfigurationUtil.getColorFromString(getXMLString(xmlFile, baseName .. "#color")) |
310 | |
311 | local filename = Utils.getNoNil(getXMLString(xmlFile, baseName.. "#filename"), "$data/shared/assets/powerTakeOffs/walterscheidW.xml") |
312 | if filename ~= nil then |
313 | self:loadPowerTakeOffFromConfigFile(entry, filename) |
314 | |
315 | if self.addAllSubWashableNodes ~= nil and entry.startNode ~= nil then |
316 | self:addAllSubWashableNodes(entry.startNode) |
317 | end |
318 | end |
319 | |
320 | entry.ptoName = getXMLString(self.xmlFile, baseName .. "#ptoName") or "DEFAULT_PTO" |
321 | |
322 | entry.objectChanges = {} |
323 | ObjectChangeUtil.loadObjectChangeFromXML(self.xmlFile, baseName, entry.objectChanges, self.components, self) |
324 | ObjectChangeUtil.setObjectChanges(entry.objectChanges, false) |
325 | |
326 | return true |
327 | end |
331 | function PowerTakeOffs:loadLocalPowerTakeOff(xmlFile, baseName, entry) |
332 | entry.inputNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.."#startNode"), self.i3dMappings) |
333 | if entry.inputNode == nil then |
334 | g_logManager:xmlWarning(self.configFileName, "Missing startNode for local power take off '%s'", baseName) |
335 | return false |
336 | end |
337 | |
338 | entry.endNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.."#endNode"), self.i3dMappings) |
339 | if entry.endNode == nil then |
340 | g_logManager:xmlWarning(self.configFileName, "Missing endNode for local power take off '%s'", baseName) |
341 | return false |
342 | end |
343 | |
344 | entry.color = ConfigurationUtil.getColorFromString(getXMLString(xmlFile, baseName .. "#color")) |
345 | |
346 | local filename = Utils.getNoNil(getXMLString(xmlFile, baseName.. "#filename"), "$data/shared/assets/powerTakeOffs/walterscheidW.xml") |
347 | if filename ~= nil then |
348 | self:loadPowerTakeOffFromConfigFile(entry, filename) |
349 | |
350 | if self.addAllSubWashableNodes ~= nil and entry.startNode ~= nil then |
351 | self:addAllSubWashableNodes(entry.startNode) |
352 | end |
353 | end |
354 | |
355 | return true |
356 | end |
233 | function PowerTakeOffs:loadOutputPowerTakeOff(xmlFile, baseName, entry) |
234 | XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#linkNode", baseName .."#outputNode") -- FS19 to FS19 |
235 | XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, baseName .. "#filename", "pto file is now defined in the pto input node") -- FS19 to FS19 |
236 | |
237 | entry.skipToInputAttacherIndex = getXMLInt(xmlFile, baseName.. "#skipToInputAttacherIndex") |
238 | |
239 | local outputNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseName.. "#outputNode"), self.i3dMappings) |
240 | if outputNode == nil and entry.skipToInputAttacherIndex == nil then |
241 | g_logManager:xmlWarning(self.configFileName, "Pto output needs to have either a valid 'outputNode' or a 'skipToInputAttacherIndex' in '%s'", baseName) |
242 | return false |
243 | end |
244 | |
245 | local attacherJointIndices = {} |
246 | local attacherJointIndicesStr = getXMLString(xmlFile, baseName .. "#attacherJointIndices") |
247 | |
248 | if attacherJointIndicesStr == nil then |
249 | g_logManager:xmlWarning(self.configFileName, "Pto output needs to have valid 'attacherJointIndices' in '%s'", baseName) |
250 | return false |
251 | else |
252 | local indices = {StringUtil.getVectorFromString(attacherJointIndicesStr)} |
253 | for _, index in ipairs(indices) do |
254 | if self:getAttacherJointByJointDescIndex(index) == nil then |
255 | g_logManager:xmlWarning(self.configFileName, "The given attacherJointIndex '%d' for '%s' can't be resolved into a valid attacherJoint", index, baseName) |
256 | return false |
257 | else |
258 | attacherJointIndices[index] = true |
259 | end |
260 | end |
261 | end |
262 | |
263 | entry.outputNode = outputNode |
264 | entry.attacherJointIndices = attacherJointIndices |
265 | entry.connectedInput = nil |
266 | |
267 | entry.ptoName = getXMLString(self.xmlFile, baseName .. "#ptoName") or "DEFAULT_PTO" |
268 | |
269 | entry.objectChanges = {} |
270 | ObjectChangeUtil.loadObjectChangeFromXML(self.xmlFile, baseName, entry.objectChanges, self.components, self) |
271 | ObjectChangeUtil.setObjectChanges(entry.objectChanges, false) |
272 | |
273 | return true |
274 | end |
584 | function PowerTakeOffs:loadPowerTakeOffFromConfigFile(powerTakeOff, xmlFilename) |
585 | xmlFilename = Utils.getFilename(xmlFilename, self.baseDirectory) |
586 | local xmlFile = loadXMLFile("TempConfig", xmlFilename) |
587 | if xmlFile ~= nil then |
588 | local filename = getXMLString(xmlFile, "powerTakeOff#filename") |
589 | if filename ~= nil then |
590 | powerTakeOff.filename = filename |
591 | local i3dNode = g_i3DManager:loadSharedI3DFile(filename, self.baseDirectory, false, false, false) |
592 | if i3dNode ~= 0 then |
593 | powerTakeOff.startNode = I3DUtil.indexToObject(i3dNode, getXMLString(xmlFile, "powerTakeOff.startNode#node")) |
594 | powerTakeOff.size = Utils.getNoNil(getXMLFloat(xmlFile, "powerTakeOff#size"), 0.19) |
595 | powerTakeOff.minLength = Utils.getNoNil(getXMLFloat(xmlFile, "powerTakeOff#minLength"), 0.6) |
596 | powerTakeOff.maxAngle = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, "powerTakeOff#maxAngle"), 45)) |
597 | powerTakeOff.zOffset = getXMLFloat(xmlFile, "powerTakeOff#zOffset") or 0 |
598 | |
599 | powerTakeOff.animationNodes = g_animationManager:loadAnimations(xmlFile, "powerTakeOff.animationNodes", i3dNode, self) |
600 | |
601 | if getXMLBool(xmlFile, "powerTakeOff#isSingleJoint") then |
602 | self:loadSingleJointPowerTakeOff(powerTakeOff, xmlFile, i3dNode) |
603 | elseif getXMLBool(xmlFile, "powerTakeOff#isDoubleJoint") then |
604 | self:loadDoubleJointPowerTakeOff(powerTakeOff, xmlFile, i3dNode) |
605 | else |
606 | self:loadBasicPowerTakeOff(powerTakeOff, xmlFile, i3dNode) |
607 | end |
608 | |
609 | if powerTakeOff.color ~= nil and #powerTakeOff.color >= 3 then |
610 | local colorShaderParameter = getXMLString(xmlFile, "powerTakeOff#colorShaderParameter") |
611 | if colorShaderParameter ~= nil then |
612 | local nodes = {} |
613 | I3DUtil.getNodesByShaderParam(powerTakeOff.startNode, colorShaderParameter, nodes) |
614 | for _, node in pairs(nodes) do |
615 | local _, _, _, mat = getShaderParameter(node, colorShaderParameter) |
616 | setShaderParameter(node, colorShaderParameter, powerTakeOff.color[1], powerTakeOff.color[2], powerTakeOff.color[3], mat, false) |
617 | end |
618 | end |
619 | end |
620 | |
621 | link(powerTakeOff.inputNode, powerTakeOff.startNode) |
622 | delete(i3dNode) |
623 | else |
624 | g_logManager:xmlWarning(self.configFileName, "Failed to find powerTakeOff in i3d file '%s'", filename, xmlFilename) |
625 | return false |
626 | end |
627 | else |
628 | g_logManager:xmlWarning(self.configFileName, "Failed to open powerTakeOff i3d file '%s' in '%s'", filename, xmlFilename) |
629 | return false |
630 | end |
631 | |
632 | delete(xmlFile) |
633 | return true |
634 | end |
635 | |
636 | g_logManager:xmlWarning(self.configFileName, "Failed to open powerTakeOff config file '%s'", xmlFilename) |
637 | return false |
638 | end |
642 | function PowerTakeOffs:loadSingleJointPowerTakeOff(powerTakeOff, xmlFile, rootNode) |
643 | powerTakeOff.startJoint = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.startJoint#node")) |
644 | |
645 | powerTakeOff.scalePart = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.scalePart#node")) |
646 | powerTakeOff.scalePartRef = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.scalePart#referenceNode")) |
647 | local _, _, dis = localToLocal(powerTakeOff.scalePartRef, powerTakeOff.scalePart, 0, 0, 0) |
648 | powerTakeOff.scalePartBaseDistance = dis |
649 | |
650 | powerTakeOff.translationPart = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.translationPart#node")) |
651 | powerTakeOff.translationPartRef = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.translationPart#referenceNode")) |
652 | powerTakeOff.translationPartLength = getXMLFloat(xmlFile, "powerTakeOff.translationPart#length") or 0.4 |
653 | |
654 | powerTakeOff.decal = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.translationPart.decal#node")) |
655 | powerTakeOff.decalSize = getXMLFloat(xmlFile, "powerTakeOff.translationPart.decal#size") or 0.1 |
656 | powerTakeOff.decalOffset = getXMLFloat(xmlFile, "powerTakeOff.translationPart.decal#offset") or 0.05 |
657 | powerTakeOff.decalMinOffset = getXMLFloat(xmlFile, "powerTakeOff.translationPart.decal#minOffset") or 0.01 |
658 | |
659 | powerTakeOff.endJoint = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.endJoint#node")) |
660 | powerTakeOff.linkNode = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, "powerTakeOff.linkNode#node")) |
661 | |
662 | local _, _, betweenLength = localToLocal(powerTakeOff.translationPart, powerTakeOff.translationPartRef, 0, 0, 0) |
663 | local _, _, ptoLength = localToLocal(powerTakeOff.startNode, powerTakeOff.linkNode, 0, 0, 0) |
664 | powerTakeOff.betweenLength = math.abs(betweenLength) |
665 | powerTakeOff.connectorLength = math.abs(ptoLength) - math.abs(betweenLength) |
666 | |
667 | setTranslation(powerTakeOff.linkNode, 0, 0, 0) |
668 | setRotation(powerTakeOff.linkNode, 0, 0, 0) |
669 | |
670 | powerTakeOff.updateFunc = PowerTakeOffs.updateSingleJointPowerTakeOff |
671 | powerTakeOff.updateDistanceFunc = PowerTakeOffs.updateDistanceOfTypedPowerTakeOff |
672 | powerTakeOff.attachFunc = PowerTakeOffs.attachTypedPowerTakeOff |
673 | powerTakeOff.detachFunc = PowerTakeOffs.detachTypedPowerTakeOff |
674 | end |
167 | function PowerTakeOffs:onDelete() |
168 | local spec = self.spec_powerTakeOffs |
169 | for _,output in pairs(spec.outputPowerTakeOffs) do |
170 | if output.rootNode ~= nil then |
171 | delete(output.rootNode) |
172 | delete(output.attachNode) |
173 | end |
174 | end |
175 | for _,input in pairs(spec.inputPowerTakeOffs) do |
176 | if input.rootNode ~= nil then |
177 | delete(input.rootNode) |
178 | delete(input.attachNode) |
179 | end |
180 | g_animationManager:deleteAnimations(input.animationNodes) |
181 | end |
182 | for _,localPto in pairs(spec.localPowerTakeOffs) do |
183 | g_animationManager:deleteAnimations(localPto.animationNodes) |
184 | end |
185 | end |
556 | function PowerTakeOffs:onPostAttachImplement(attachableObject, inputJointDescIndex, jointDescIndex) |
557 | local spec = self.spec_powerTakeOffs |
558 | for i=#spec.delayedPowerTakeOffsMountings, 1, -1 do |
559 | local delayedMounting = spec.delayedPowerTakeOffsMountings[i] |
560 | if delayedMounting.jointDescIndex == jointDescIndex then |
561 | local input = delayedMounting.input |
562 | local output = delayedMounting.output |
563 | |
564 | if input.attachFunc ~= nil then |
565 | input.attachFunc(self, input, output) |
566 | end |
567 | |
568 | ObjectChangeUtil.setObjectChanges(input.objectChanges, true) |
569 | ObjectChangeUtil.setObjectChanges(output.objectChanges, true) |
570 | |
571 | table.remove(spec.delayedPowerTakeOffsMountings, i) |
572 | end |
573 | end |
574 | end |
94 | function PowerTakeOffs:onPostLoad(savegame) |
95 | local spec = self.spec_powerTakeOffs |
96 | |
97 | local ptoConfigurationId = Utils.getNoNil(self.configurations["powerTakeOff"], 1) |
98 | local configKey = string.format("vehicle.powerTakeOffs.powerTakeOffConfigurations.powerTakeOffConfiguration(%d)", ptoConfigurationId -1) |
99 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.powerTakeOffs.powerTakeOffConfigurations.powerTakeOffConfiguration", ptoConfigurationId , self.components, self) |
100 | |
101 | -- fallback key |
102 | if not hasXMLProperty(self.xmlFile, configKey) then |
103 | configKey = "vehicle.powerTakeOffs" |
104 | end |
105 | |
106 | if SpecializationUtil.hasSpecialization(AttacherJoints, self.specializations) then |
107 | local i = 0 |
108 | while true do |
109 | local baseName = string.format("%s.output(%d)", configKey, i) |
110 | if not hasXMLProperty(self.xmlFile, baseName) then |
111 | break |
112 | end |
113 | |
114 | local entry = {} |
115 | if self:loadOutputPowerTakeOff(self.xmlFile, baseName, entry) then |
116 | table.insert(spec.outputPowerTakeOffs, entry) |
117 | end |
118 | i = i + 1 |
119 | end |
120 | end |
121 | |
122 | if SpecializationUtil.hasSpecialization(Attachable, self.specializations) then |
123 | local i = 0 |
124 | while true do |
125 | local baseName = string.format("%s.input(%d)", configKey, i) |
126 | if not hasXMLProperty(self.xmlFile, baseName) then |
127 | break |
128 | end |
129 | |
130 | local entry = {} |
131 | if self:loadInputPowerTakeOff(self.xmlFile, baseName, entry) then |
132 | table.insert(spec.inputPowerTakeOffs, entry) |
133 | |
134 | self:parkPowerTakeOff(entry) |
135 | end |
136 | i = i + 1 |
137 | end |
138 | end |
139 | |
140 | local i = 0 |
141 | while true do |
142 | local baseName = string.format("%s.local(%d)", configKey, i) |
143 | if not hasXMLProperty(self.xmlFile, baseName) then |
144 | break |
145 | end |
146 | |
147 | local entry = {} |
148 | if self:loadLocalPowerTakeOff(self.xmlFile, baseName, entry) then |
149 | table.insert(spec.localPowerTakeOffs, entry) |
150 | end |
151 | i = i + 1 |
152 | end |
153 | end |
189 | function PowerTakeOffs:onUpdateInterpolation(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
190 | if self.isClient then |
191 | local spec = self.spec_powerTakeOffs |
192 | for _, input in pairs(spec.inputPowerTakeOffs) do |
193 | if input.connectedVehicle ~= nil then |
194 | if self.updateLoopIndex == input.connectedVehicle.updateLoopIndex then |
195 | self:updatePowerTakeOff(input, dt) |
196 | end |
197 | end |
198 | end |
199 | |
200 | if self.getAttachedImplements ~= nil then |
201 | for _, implement in ipairs(self:getAttachedImplements()) do |
202 | if implement.object.updateAttachedPowerTakeOffs ~= nil then |
203 | implement.object:updateAttachedPowerTakeOffs(dt, self) |
204 | end |
205 | end |
206 | end |
207 | |
208 | local isPowerTakeOffActive = self:getIsPowerTakeOffActive() |
209 | if spec.lastIsPowerTakeOffActive ~= isPowerTakeOffActive then |
210 | for _, input in pairs(spec.inputPowerTakeOffs) do |
211 | if isPowerTakeOffActive then |
212 | g_animationManager:startAnimations(input.animationNodes) |
213 | else |
214 | g_animationManager:stopAnimations(input.animationNodes) |
215 | end |
216 | end |
217 | |
218 | for _, localPto in pairs(spec.localPowerTakeOffs) do |
219 | if isPowerTakeOffActive then |
220 | g_animationManager:startAnimations(localPto.animationNodes) |
221 | else |
222 | g_animationManager:stopAnimations(localPto.animationNodes) |
223 | end |
224 | end |
225 | |
226 | spec.lastIsPowerTakeOffActive = isPowerTakeOffActive |
227 | end |
228 | end |
229 | end |
64 | function PowerTakeOffs.registerEventListeners(vehicleType) |
65 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", PowerTakeOffs) |
66 | SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", PowerTakeOffs) |
67 | SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", PowerTakeOffs) |
68 | SpecializationUtil.registerEventListener(vehicleType, "onDelete", PowerTakeOffs) |
69 | SpecializationUtil.registerEventListener(vehicleType, "onUpdateInterpolation", PowerTakeOffs) |
70 | SpecializationUtil.registerEventListener(vehicleType, "onPreAttachImplement", PowerTakeOffs) |
71 | SpecializationUtil.registerEventListener(vehicleType, "onPostAttachImplement", PowerTakeOffs) |
72 | SpecializationUtil.registerEventListener(vehicleType, "onPreDetachImplement", PowerTakeOffs) |
73 | end |
22 | function PowerTakeOffs.registerFunctions(vehicleType) |
23 | SpecializationUtil.registerFunction(vehicleType, "loadOutputPowerTakeOff", PowerTakeOffs.loadOutputPowerTakeOff) |
24 | SpecializationUtil.registerFunction(vehicleType, "loadInputPowerTakeOff", PowerTakeOffs.loadInputPowerTakeOff) |
25 | SpecializationUtil.registerFunction(vehicleType, "loadLocalPowerTakeOff", PowerTakeOffs.loadLocalPowerTakeOff) |
26 | SpecializationUtil.registerFunction(vehicleType, "placeLocalPowerTakeOff", PowerTakeOffs.placeLocalPowerTakeOff) |
27 | SpecializationUtil.registerFunction(vehicleType, "updatePowerTakeOff", PowerTakeOffs.updatePowerTakeOff) |
28 | SpecializationUtil.registerFunction(vehicleType, "updateAttachedPowerTakeOffs", PowerTakeOffs.updateAttachedPowerTakeOffs) |
29 | SpecializationUtil.registerFunction(vehicleType, "updatePowerTakeOffLength", PowerTakeOffs.updatePowerTakeOffLength) |
30 | SpecializationUtil.registerFunction(vehicleType, "getOutputPowerTakeOffsByJointDescIndex", PowerTakeOffs.getOutputPowerTakeOffsByJointDescIndex) |
31 | SpecializationUtil.registerFunction(vehicleType, "getOutputPowerTakeOffs", PowerTakeOffs.getOutputPowerTakeOffs) |
32 | SpecializationUtil.registerFunction(vehicleType, "getInputPowerTakeOffs", PowerTakeOffs.getInputPowerTakeOffs) |
33 | SpecializationUtil.registerFunction(vehicleType, "getInputPowerTakeOffsByJointDescIndexAndName", PowerTakeOffs.getInputPowerTakeOffsByJointDescIndexAndName) |
34 | SpecializationUtil.registerFunction(vehicleType, "getIsPowerTakeOffActive", PowerTakeOffs.getIsPowerTakeOffActive) |
35 | SpecializationUtil.registerFunction(vehicleType, "attachPowerTakeOff", PowerTakeOffs.attachPowerTakeOff) |
36 | SpecializationUtil.registerFunction(vehicleType, "detachPowerTakeOff", PowerTakeOffs.detachPowerTakeOff) |
37 | SpecializationUtil.registerFunction(vehicleType, "parkPowerTakeOff", PowerTakeOffs.parkPowerTakeOff) |
38 | |
39 | SpecializationUtil.registerFunction(vehicleType, "loadPowerTakeOffFromConfigFile", PowerTakeOffs.loadPowerTakeOffFromConfigFile) |
40 | |
41 | SpecializationUtil.registerFunction(vehicleType, "loadSingleJointPowerTakeOff", PowerTakeOffs.loadSingleJointPowerTakeOff) |
42 | SpecializationUtil.registerFunction(vehicleType, "updateSingleJointPowerTakeOff", PowerTakeOffs.updateSingleJointPowerTakeOff) |
43 | |
44 | SpecializationUtil.registerFunction(vehicleType, "loadDoubleJointPowerTakeOff", PowerTakeOffs.loadDoubleJointPowerTakeOff) |
45 | SpecializationUtil.registerFunction(vehicleType, "updateDoubleJointPowerTakeOff", PowerTakeOffs.updateDoubleJointPowerTakeOff) |
46 | |
47 | SpecializationUtil.registerFunction(vehicleType, "loadBasicPowerTakeOff", PowerTakeOffs.loadBasicPowerTakeOff) |
48 | |
49 | SpecializationUtil.registerFunction(vehicleType, "attachTypedPowerTakeOff", PowerTakeOffs.attachTypedPowerTakeOff) |
50 | SpecializationUtil.registerFunction(vehicleType, "detachTypedPowerTakeOff", PowerTakeOffs.detachTypedPowerTakeOff) |
51 | |
52 | SpecializationUtil.registerFunction(vehicleType, "validatePowerTakeOffAttachment", PowerTakeOffs.validatePowerTakeOffAttachment) |
53 | end |
765 | function PowerTakeOffs:updateDistanceOfTypedPowerTakeOff(powerTakeOff) |
766 | local attachLength = calcDistanceFrom(powerTakeOff.linkNode, powerTakeOff.startNode) |
767 | local transPartScale = math.max(attachLength - powerTakeOff.connectorLength, 0) / powerTakeOff.betweenLength |
768 | setScale(powerTakeOff.translationPart, 1, 1, transPartScale) |
769 | |
770 | if powerTakeOff.decal ~= nil then |
771 | local transPartLength = transPartScale * powerTakeOff.translationPartLength |
772 | |
773 | if transPartLength > powerTakeOff.decalMinOffset * 2 + powerTakeOff.decalSize then |
774 | local offset = math.min((transPartLength - powerTakeOff.decalSize) / 2, powerTakeOff.decalOffset) |
775 | local decalTranslation = offset + powerTakeOff.decalSize * 0.5 |
776 | local x, y, _ = getTranslation(powerTakeOff.decal) |
777 | setTranslation(powerTakeOff.decal, x, y, -decalTranslation/transPartScale) |
778 | setScale(powerTakeOff.decal, 1, 1, 1/transPartScale) |
779 | else |
780 | setVisibility(powerTakeOff.decal, false) |
781 | end |
782 | end |
783 | end |
733 | function PowerTakeOffs:updateDoubleJointPowerTakeOff(powerTakeOff, dt) |
734 | local x, y, z = getWorldTranslation(powerTakeOff.startNode) |
735 | local dx, dy, dz = worldToLocal(getParent(powerTakeOff.endJoint2), x, y, z) |
736 | I3DUtil.setDirection(powerTakeOff.endJoint2, dx*0.5, dy*0.5, dz, 0, 1, 0) |
737 | |
738 | x, y, z = getWorldTranslation(powerTakeOff.endJoint1Ref) |
739 | dx, dy, dz = worldToLocal(getParent(powerTakeOff.startJoint1), x, y, z) |
740 | I3DUtil.setDirection(powerTakeOff.startJoint1, dx*0.5, dy*0.5, dz, 0, 1, 0) |
741 | |
742 | x, y, z = getWorldTranslation(powerTakeOff.endJoint1Ref) |
743 | dx, dy, dz = worldToLocal(getParent(powerTakeOff.startJoint2), x, y, z) |
744 | I3DUtil.setDirection(powerTakeOff.startJoint2, dx, dy, dz, 0, 1, 0) |
745 | |
746 | dx, dy, dz = worldToLocal(getParent(powerTakeOff.endJoint1), x, y, z) |
747 | setTranslation(powerTakeOff.endJoint1, 0, 0, MathUtil.vector3Length(dx, dy, dz)) |
748 | |
749 | local dist = calcDistanceFrom(powerTakeOff.scalePart, powerTakeOff.scalePartRef) |
750 | setScale(powerTakeOff.scalePart, 1, 1, dist / powerTakeOff.scalePartBaseDistance) |
751 | end |
860 | function PowerTakeOffs:updateExtraDependentParts(superFunc, part, dt) |
861 | superFunc(self, part, dt) |
862 | |
863 | if part.powerTakeOffs ~= nil then |
864 | local spec = self.spec_powerTakeOffs |
865 | for i, index in ipairs(part.powerTakeOffs) do |
866 | if spec.inputPowerTakeOffs[index] == nil then |
867 | part.powerTakeOffs[i] = nil |
868 | g_logManager:xmlWarning(self.configFileName, "Unable to find powerTakeOff index '%d' for movingPart/movingTool '%s'", index, getName(part.node)) |
869 | else |
870 | self:updatePowerTakeOff(spec.inputPowerTakeOffs[index], dt) |
871 | end |
872 | end |
873 | end |
874 | |
875 | if part.localPowerTakeOffs ~= nil then |
876 | local spec = self.spec_powerTakeOffs |
877 | for i, index in ipairs(part.localPowerTakeOffs) do |
878 | if spec.localPowerTakeOffs[index] == nil then |
879 | part.localPowerTakeOffs[i] = nil |
880 | g_logManager:xmlWarning(self.configFileName, "Unable to find local powerTakeOff index '%d' for movingPart/movingTool '%s'", index, getName(part.node)) |
881 | else |
882 | self:placeLocalPowerTakeOff(spec.localPowerTakeOffs[index], dt) |
883 | end |
884 | end |
885 | end |
886 | end |
678 | function PowerTakeOffs:updateSingleJointPowerTakeOff(powerTakeOff, dt) |
679 | local x, y, z = getWorldTranslation(powerTakeOff.linkNode) |
680 | |
681 | local dx, dy, dz = worldToLocal(powerTakeOff.startNode, x, y, z) |
682 | I3DUtil.setDirection(powerTakeOff.startJoint, dx, dy, dz, 0, 1, 0) |
683 | |
684 | dx, dy, dz = worldToLocal(getParent(powerTakeOff.endJoint), x, y, z) |
685 | setTranslation(powerTakeOff.endJoint, 0, 0, MathUtil.vector3Length(dx, dy, dz)) |
686 | |
687 | local dist = calcDistanceFrom(powerTakeOff.scalePart, powerTakeOff.scalePartRef) |
688 | setScale(powerTakeOff.scalePart, 1, 1, dist / powerTakeOff.scalePartBaseDistance) |
689 | end |
808 | function PowerTakeOffs:validatePowerTakeOffAttachment(powerTakeOff, output) |
809 | if output.outputNode == nil or powerTakeOff.inputNode == nil then |
810 | return false |
811 | end |
812 | |
813 | local x1, y1, z1 = getWorldTranslation(output.outputNode) |
814 | local x2, y2, z2 = getWorldTranslation(powerTakeOff.inputNode) |
815 | |
816 | local length = MathUtil.vector3Length(x1-x2, y1-y2, z1-z2) |
817 | if length < powerTakeOff.minLength then |
818 | return false |
819 | end |
820 | |
821 | local length2D = MathUtil.vector2Length(x1-x2, z1-z2) |
822 | local angle = math.acos(length2D / length) |
823 | if angle > powerTakeOff.maxAngle then |
824 | return false |
825 | end |
826 | |
827 | return true |
828 | end |