665 | function PowerTakeOffs:detachPowerTakeOff(detachingVehicle, implement, jointDescIndex) |
666 | local spec = self.spec_powerTakeOffs |
667 | -- clear delayed mountings |
668 | spec.delayedPowerTakeOffsMountings = {} |
669 | |
670 | local outputs = detachingVehicle:getOutputPowerTakeOffsByJointDescIndex(jointDescIndex or implement.jointDescIndex) |
671 | |
672 | for _, output in ipairs(outputs) do |
673 | if output.connectedInput ~= nil then |
674 | local input = output.connectedInput |
675 | |
676 | if input.detachFunc ~= nil then |
677 | input.detachFunc(self, input, output) |
678 | end |
679 | |
680 | input.connectedVehicle = nil |
681 | input.connectedOutput = nil |
682 | output.connectedVehicle = nil |
683 | output.connectedInput = nil |
684 | |
685 | ObjectChangeUtil.setObjectChanges(input.objectChanges, false) |
686 | ObjectChangeUtil.setObjectChanges(output.objectChanges, false) |
687 | end |
688 | end |
689 | |
690 | return true |
691 | end |
25 | function PowerTakeOffs.initSpecialization() |
26 | local schema = Vehicle.xmlSchema |
27 | schema:setXMLSpecializationType("PowerTakeOffs") |
28 | |
29 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.powerTakeOffs.powerTakeOffConfigurations.powerTakeOffConfiguration(?)") |
30 | PowerTakeOffs.registerXMLPaths(schema, "vehicle.powerTakeOffs.powerTakeOffConfigurations.powerTakeOffConfiguration(?)") |
31 | PowerTakeOffs.registerXMLPaths(schema, "vehicle.powerTakeOffs") |
32 | |
33 | schema:register(XMLValueType.VECTOR_N, Cylindered.MOVING_TOOL_XML_KEY .. ".powerTakeOffs#indices", "PTOs to update") |
34 | schema:register(XMLValueType.VECTOR_N, Cylindered.MOVING_TOOL_XML_KEY .. ".powerTakeOffs#localIndices", "Local PTOs to update") |
35 | schema:register(XMLValueType.VECTOR_N, Cylindered.MOVING_PART_XML_KEY .. ".powerTakeOffs#indices", "PTOs to update") |
36 | schema:register(XMLValueType.VECTOR_N, Cylindered.MOVING_PART_XML_KEY .. ".powerTakeOffs#localIndices", "Local PTOs to update") |
37 | |
38 | schema:register(XMLValueType.BOOL, "vehicle.powerTakeOffs#ignoreInvalidJointIndices", "Do not display warning if attacher joint index could not be found. Can be useful if attacher joints change due to configurations", false) |
39 | schema:register(XMLValueType.FLOAT, "vehicle.powerTakeOffs#maxUpdateDistance", "Max. distance to vehicle root to update power take offs", PowerTakeOffs.DEFAULT_MAX_UPDATE_DISTANCE) |
40 | |
41 | schema:setXMLSpecializationType() |
42 | |
43 | local powerTakeOffXMLSchema = XMLSchema.new("powerTakeOff") |
44 | PowerTakeOffs.xmlSchema = powerTakeOffXMLSchema |
45 | |
46 | powerTakeOffXMLSchema:register(XMLValueType.STRING, "powerTakeOff#filename", "Path to i3d file") |
47 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.startNode#node", "Start node") |
48 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.linkNode#node", "Link node") |
49 | powerTakeOffXMLSchema:register(XMLValueType.FLOAT, "powerTakeOff#size", "Height of pto", 0.19) |
50 | powerTakeOffXMLSchema:register(XMLValueType.FLOAT, "powerTakeOff#minLength", "Minimum length of pto", 0.6) |
51 | powerTakeOffXMLSchema:register(XMLValueType.ANGLE, "powerTakeOff#maxAngle", "Max. angle between start and end", 45) |
52 | powerTakeOffXMLSchema:register(XMLValueType.FLOAT, "powerTakeOff#zOffset", "Z axis offset of end node", 0) |
53 | powerTakeOffXMLSchema:register(XMLValueType.STRING, "powerTakeOff#colorShaderParameter", "Color shader parameter") |
54 | powerTakeOffXMLSchema:register(XMLValueType.STRING, "powerTakeOff#decalColorShaderParameter", "Decal color shader parameter") |
55 | |
56 | AnimationManager.registerAnimationNodesXMLPaths(powerTakeOffXMLSchema, "powerTakeOff.animationNodes") |
57 | |
58 | powerTakeOffXMLSchema:register(XMLValueType.BOOL, "powerTakeOff#isSingleJoint", "Is single joint PTO", false) |
59 | powerTakeOffXMLSchema:register(XMLValueType.BOOL, "powerTakeOff#isDoubleJoint", "Is double joint PTO", false) |
60 | |
61 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.startJoint#node", "(Single Joint) Start joint node") |
62 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.endJoint#node", "(Single Joint) End joint node") |
63 | |
64 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.scalePart#node", "(Single|Double Joint) Scale part node") |
65 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.scalePart#referenceNode", "(Single|Double Joint) Scale part reference node") |
66 | |
67 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.translationPart#node", "(Single|Double Joint) translation part node") |
68 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.translationPart#referenceNode", "(Single|Double Joint) translation part reference node") |
69 | powerTakeOffXMLSchema:register(XMLValueType.FLOAT, "powerTakeOff.translationPart#length", "(Single|Double Joint) translation part length", 0.4) |
70 | |
71 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.translationPart.decal#node", "(Single|Double Joint) translation part decal node") |
72 | powerTakeOffXMLSchema:register(XMLValueType.FLOAT, "powerTakeOff.translationPart.decal#size", "(Single|Double Joint) translation part decal size", 0.1) |
73 | powerTakeOffXMLSchema:register(XMLValueType.FLOAT, "powerTakeOff.translationPart.decal#offset", "(Single|Double Joint) translation part decal offset", 0.05) |
74 | powerTakeOffXMLSchema:register(XMLValueType.FLOAT, "powerTakeOff.translationPart.decal#minOffset", "(Single|Double Joint) translation part decal minOffset", 0.01) |
75 | |
76 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.startJoint1#node", "(Double Joint) Start joint 1") |
77 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.startJoint2#node", "(Double Joint) Start joint 2") |
78 | |
79 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.endJoint1#node", "(Double Joint) End joint 1") |
80 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.endJoint1#referenceNode", "(Double Joint) End joint 1 reference node") |
81 | powerTakeOffXMLSchema:register(XMLValueType.NODE_INDEX, "powerTakeOff.endJoint2#node", "(Double Joint) End joint 2") |
82 | end |
905 | function PowerTakeOffs:loadDoubleJointPowerTakeOff(powerTakeOff, xmlFile, rootNode) |
906 | powerTakeOff.startJoint1 = xmlFile:getValue("powerTakeOff.startJoint1#node", nil, rootNode) |
907 | powerTakeOff.startJoint2 = xmlFile:getValue("powerTakeOff.startJoint2#node", nil, rootNode) |
908 | |
909 | powerTakeOff.scalePart = xmlFile:getValue("powerTakeOff.scalePart#node", nil, rootNode) |
910 | powerTakeOff.scalePartRef = xmlFile:getValue("powerTakeOff.scalePart#referenceNode", nil, rootNode) |
911 | local _, _, dis = localToLocal(powerTakeOff.scalePartRef, powerTakeOff.scalePart, 0, 0, 0) |
912 | powerTakeOff.scalePartBaseDistance = dis |
913 | |
914 | powerTakeOff.translationPart = xmlFile:getValue("powerTakeOff.translationPart#node", nil, rootNode) |
915 | powerTakeOff.translationPartRef = xmlFile:getValue("powerTakeOff.translationPart#referenceNode", nil, rootNode) |
916 | powerTakeOff.translationPartLength = xmlFile:getValue("powerTakeOff.translationPart#length", 0.4) |
917 | |
918 | powerTakeOff.decal = xmlFile:getValue("powerTakeOff.translationPart.decal#node", nil, rootNode) |
919 | powerTakeOff.decalSize = xmlFile:getValue("powerTakeOff.translationPart.decal#size", 0.1) |
920 | powerTakeOff.decalOffset = xmlFile:getValue("powerTakeOff.translationPart.decal#offset", 0.05) |
921 | powerTakeOff.decalMinOffset = xmlFile:getValue("powerTakeOff.translationPart.decal#minOffset", 0.01) |
922 | |
923 | powerTakeOff.endJoint1 = xmlFile:getValue("powerTakeOff.endJoint1#node", nil, rootNode) |
924 | powerTakeOff.endJoint1Ref = xmlFile:getValue("powerTakeOff.endJoint1#referenceNode", nil, rootNode) |
925 | |
926 | powerTakeOff.endJoint2 = xmlFile:getValue("powerTakeOff.endJoint2#node", nil, rootNode) |
927 | powerTakeOff.linkNode = xmlFile:getValue("powerTakeOff.linkNode#node", nil, rootNode) |
928 | |
929 | local _, _, betweenLength = localToLocal(powerTakeOff.translationPart, powerTakeOff.translationPartRef, 0, 0, 0) |
930 | local _, _, ptoLength = localToLocal(powerTakeOff.startNode, powerTakeOff.linkNode, 0, 0, 0) |
931 | powerTakeOff.betweenLength = math.abs(betweenLength) |
932 | powerTakeOff.connectorLength = math.abs(ptoLength) - math.abs(betweenLength) |
933 | |
934 | setTranslation(powerTakeOff.linkNode, 0, 0, 0) |
935 | setRotation(powerTakeOff.linkNode, 0, 0, 0) |
936 | |
937 | powerTakeOff.updateFunc = PowerTakeOffs.updateDoubleJointPowerTakeOff |
938 | powerTakeOff.updateDistanceFunc = PowerTakeOffs.updateDistanceOfTypedPowerTakeOff |
939 | powerTakeOff.attachFunc = PowerTakeOffs.attachTypedPowerTakeOff |
940 | powerTakeOff.detachFunc = PowerTakeOffs.detachTypedPowerTakeOff |
941 | end |
441 | function PowerTakeOffs:loadInputPowerTakeOff(xmlFile, baseName, powerTakeOffInput) |
442 | local inputNode = xmlFile:getValue(baseName.."#inputNode", nil, self.components, self.i3dMappings) |
443 | if inputNode == nil then |
444 | Logging.xmlWarning(xmlFile, "Pto input needs to have a valid 'inputNode' in '%s'", baseName) |
445 | return false |
446 | end |
447 | |
448 | local inputAttacherJointIndices = {} |
449 | local inputAttacherJointIndicesRaw = xmlFile:getValue(baseName .. "#inputAttacherJointIndices", nil, true) |
450 | |
451 | if inputAttacherJointIndicesRaw == nil then |
452 | Logging.xmlWarning(xmlFile, "Pto output needs to have valid 'inputAttacherJointIndices' in '%s'", baseName) |
453 | return false |
454 | else |
455 | for _, index in ipairs(inputAttacherJointIndicesRaw) do |
456 | inputAttacherJointIndices[index] = true |
457 | end |
458 | end |
459 | |
460 | powerTakeOffInput.inputNode = inputNode |
461 | powerTakeOffInput.detachNode = xmlFile:getValue(baseName.."#detachNode", nil, self.components, self.i3dMappings) |
462 | powerTakeOffInput.inputAttacherJointIndices = inputAttacherJointIndices |
463 | |
464 | powerTakeOffInput.aboveAttacher = xmlFile:getValue(baseName.."#aboveAttacher", true) |
465 | |
466 | powerTakeOffInput.color = xmlFile:getValue(baseName .. "#color", nil, true) |
467 | powerTakeOffInput.decalColor = xmlFile:getValue(baseName .. "#decalColor", nil, true) |
468 | |
469 | powerTakeOffInput.ptoName = xmlFile:getValue(baseName .. "#ptoName", "DEFAULT_PTO") |
470 | |
471 | powerTakeOffInput.objectChanges = {} |
472 | ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, baseName, powerTakeOffInput.objectChanges, self.components, self) |
473 | ObjectChangeUtil.setObjectChanges(powerTakeOffInput.objectChanges, false) |
474 | |
475 | local filename = xmlFile:getValue(baseName.. "#filename", "$data/shared/assets/powerTakeOffs/walterscheidW.xml") |
476 | if filename ~= nil then |
477 | self:loadPowerTakeOffFromConfigFile(powerTakeOffInput, filename) |
478 | end |
479 | |
480 | return true |
481 | end |
485 | function PowerTakeOffs:loadLocalPowerTakeOff(xmlFile, baseName, powerTakeOffLocal) |
486 | powerTakeOffLocal.isLocal = true |
487 | |
488 | powerTakeOffLocal.inputNode = xmlFile:getValue(baseName.."#startNode", nil, self.components, self.i3dMappings) |
489 | if powerTakeOffLocal.inputNode == nil then |
490 | Logging.xmlWarning(xmlFile, "Missing startNode for local power take off '%s'", baseName) |
491 | return false |
492 | end |
493 | |
494 | powerTakeOffLocal.endNode = xmlFile:getValue(baseName.."#endNode", nil, self.components, self.i3dMappings) |
495 | if powerTakeOffLocal.endNode == nil then |
496 | Logging.xmlWarning(xmlFile, "Missing endNode for local power take off '%s'", baseName) |
497 | return false |
498 | end |
499 | |
500 | powerTakeOffLocal.color = xmlFile:getValue(baseName .. "#color", nil, true) |
501 | powerTakeOffLocal.decalColor = xmlFile:getValue(baseName .. "#decalColor", nil, true) |
502 | |
503 | local filename = xmlFile:getValue(baseName.. "#filename", "$data/shared/assets/powerTakeOffs/walterscheidW.xml") |
504 | if filename ~= nil then |
505 | self:loadPowerTakeOffFromConfigFile(powerTakeOffLocal, filename) |
506 | end |
507 | |
508 | return true |
509 | end |
402 | function PowerTakeOffs:loadOutputPowerTakeOff(xmlFile, baseName, powerTakeOffOutput) |
403 | XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#linkNode", baseName .."#outputNode") -- FS19 to FS19 |
404 | XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName .. "#filename", "pto file is now defined in the pto input node") -- FS19 to FS19 |
405 | |
406 | powerTakeOffOutput.skipToInputAttacherIndex = xmlFile:getValue(baseName.. "#skipToInputAttacherIndex") |
407 | |
408 | local outputNode = xmlFile:getValue(baseName.. "#outputNode", nil, self.components, self.i3dMappings) |
409 | if outputNode == nil and powerTakeOffOutput.skipToInputAttacherIndex == nil then |
410 | Logging.xmlWarning(xmlFile, "Pto output needs to have either a valid 'outputNode' or a 'skipToInputAttacherIndex' in '%s'", baseName) |
411 | return false |
412 | end |
413 | |
414 | local attacherJointIndices = {} |
415 | local attacherJointIndicesRaw = xmlFile:getValue(baseName .. "#attacherJointIndices", nil, true) |
416 | |
417 | if attacherJointIndicesRaw == nil then |
418 | Logging.xmlWarning(xmlFile, "Pto output needs to have valid 'attacherJointIndices' in '%s'", baseName) |
419 | return false |
420 | else |
421 | for _, index in ipairs(attacherJointIndicesRaw) do |
422 | attacherJointIndices[index] = true |
423 | end |
424 | end |
425 | |
426 | powerTakeOffOutput.outputNode = outputNode |
427 | powerTakeOffOutput.attacherJointIndices = attacherJointIndices |
428 | powerTakeOffOutput.connectedInput = nil |
429 | |
430 | powerTakeOffOutput.ptoName = xmlFile:getValue(baseName .. "#ptoName", "DEFAULT_PTO") |
431 | |
432 | powerTakeOffOutput.objectChanges = {} |
433 | ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, baseName, powerTakeOffOutput.objectChanges, self.components, self) |
434 | ObjectChangeUtil.setObjectChanges(powerTakeOffOutput.objectChanges, false) |
435 | |
436 | return true |
437 | end |
371 | function PowerTakeOffs:loadPowerTakeOffsFromXML(xmlFile, key) |
372 | local spec = self.spec_powerTakeOffs |
373 | |
374 | if SpecializationUtil.hasSpecialization(AttacherJoints, self.specializations) then |
375 | xmlFile:iterate(key..".output", function (_, outputKey) |
376 | local powerTakeOffOutput = {} |
377 | if self:loadOutputPowerTakeOff(xmlFile, outputKey, powerTakeOffOutput) then |
378 | table.insert(spec.outputPowerTakeOffs, powerTakeOffOutput) |
379 | end |
380 | end) |
381 | end |
382 | |
383 | if SpecializationUtil.hasSpecialization(Attachable, self.specializations) then |
384 | xmlFile:iterate(key..".input", function (_, inputKey) |
385 | local powerTakeOffInput = {} |
386 | if self:loadInputPowerTakeOff(xmlFile, inputKey, powerTakeOffInput) then |
387 | table.insert(spec.inputPowerTakeOffs, powerTakeOffInput) |
388 | end |
389 | end) |
390 | end |
391 | |
392 | xmlFile:iterate(key..".local", function (_, localKey) |
393 | local powerTakeOffLocal = {} |
394 | if self:loadLocalPowerTakeOff(xmlFile, localKey, powerTakeOffLocal) then |
395 | table.insert(spec.localPowerTakeOffs, powerTakeOffLocal) |
396 | end |
397 | end) |
398 | end |
854 | function PowerTakeOffs:loadSingleJointPowerTakeOff(powerTakeOff, xmlFile, rootNode) |
855 | powerTakeOff.startJoint = xmlFile:getValue("powerTakeOff.startJoint#node", nil, rootNode) |
856 | |
857 | powerTakeOff.scalePart = xmlFile:getValue("powerTakeOff.scalePart#node", nil, rootNode) |
858 | powerTakeOff.scalePartRef = xmlFile:getValue("powerTakeOff.scalePart#referenceNode", nil, rootNode) |
859 | local _, _, dis = localToLocal(powerTakeOff.scalePartRef, powerTakeOff.scalePart, 0, 0, 0) |
860 | powerTakeOff.scalePartBaseDistance = dis |
861 | |
862 | powerTakeOff.translationPart = xmlFile:getValue("powerTakeOff.translationPart#node", nil, rootNode) |
863 | powerTakeOff.translationPartRef = xmlFile:getValue("powerTakeOff.translationPart#referenceNode", nil, rootNode) |
864 | powerTakeOff.translationPartLength = xmlFile:getValue("powerTakeOff.translationPart#length", 0.4) |
865 | |
866 | powerTakeOff.decal = xmlFile:getValue("powerTakeOff.translationPart.decal#node", nil, rootNode) |
867 | powerTakeOff.decalSize = xmlFile:getValue("powerTakeOff.translationPart.decal#size", 0.1) |
868 | powerTakeOff.decalOffset = xmlFile:getValue("powerTakeOff.translationPart.decal#offset", 0.05) |
869 | powerTakeOff.decalMinOffset = xmlFile:getValue("powerTakeOff.translationPart.decal#minOffset", 0.01) |
870 | |
871 | powerTakeOff.endJoint = xmlFile:getValue("powerTakeOff.endJoint#node", nil, rootNode) |
872 | powerTakeOff.linkNode = xmlFile:getValue("powerTakeOff.linkNode#node", nil, rootNode) |
873 | |
874 | local _, _, betweenLength = localToLocal(powerTakeOff.translationPart, powerTakeOff.translationPartRef, 0, 0, 0) |
875 | local _, _, ptoLength = localToLocal(powerTakeOff.startNode, powerTakeOff.linkNode, 0, 0, 0) |
876 | powerTakeOff.betweenLength = math.abs(betweenLength) |
877 | powerTakeOff.connectorLength = math.abs(ptoLength) - math.abs(betweenLength) |
878 | |
879 | setTranslation(powerTakeOff.linkNode, 0, 0, 0) |
880 | setRotation(powerTakeOff.linkNode, 0, 0, 0) |
881 | |
882 | powerTakeOff.updateFunc = PowerTakeOffs.updateSingleJointPowerTakeOff |
883 | powerTakeOff.updateDistanceFunc = PowerTakeOffs.updateDistanceOfTypedPowerTakeOff |
884 | powerTakeOff.attachFunc = PowerTakeOffs.attachTypedPowerTakeOff |
885 | powerTakeOff.detachFunc = PowerTakeOffs.detachTypedPowerTakeOff |
886 | end |
258 | function PowerTakeOffs:onDelete() |
259 | local spec = self.spec_powerTakeOffs |
260 | if spec.outputPowerTakeOffs ~= nil then |
261 | for _,output in pairs(spec.outputPowerTakeOffs) do |
262 | if output.xmlFile ~= nil then |
263 | output.xmlFile:delete() |
264 | output.xmlFile = nil |
265 | end |
266 | if output.sharedLoadRequestId ~= nil then |
267 | g_i3DManager:releaseSharedI3DFile(output.sharedLoadRequestId) |
268 | output.sharedLoadRequestId = nil |
269 | end |
270 | |
271 | if output.rootNode ~= nil then |
272 | delete(output.rootNode) |
273 | delete(output.attachNode) |
274 | end |
275 | end |
276 | end |
277 | |
278 | if spec.inputPowerTakeOffs ~= nil then |
279 | for _,input in pairs(spec.inputPowerTakeOffs) do |
280 | if input.xmlFile ~= nil then |
281 | input.xmlFile:delete() |
282 | input.xmlFile = nil |
283 | end |
284 | if input.sharedLoadRequestId ~= nil then |
285 | g_i3DManager:releaseSharedI3DFile(input.sharedLoadRequestId) |
286 | input.sharedLoadRequestId = nil |
287 | end |
288 | if input.rootNode ~= nil then |
289 | delete(input.rootNode) |
290 | delete(input.attachNode) |
291 | end |
292 | g_animationManager:deleteAnimations(input.animationNodes) |
293 | end |
294 | end |
295 | |
296 | if spec.localPowerTakeOffs ~= nil then |
297 | for _,localPto in pairs(spec.localPowerTakeOffs) do |
298 | if localPto.xmlFile ~= nil then |
299 | localPto.xmlFile:delete() |
300 | localPto.xmlFile = nil |
301 | end |
302 | if localPto.sharedLoadRequestId ~= nil then |
303 | g_i3DManager:releaseSharedI3DFile(localPto.sharedLoadRequestId) |
304 | localPto.sharedLoadRequestId = nil |
305 | end |
306 | g_animationManager:deleteAnimations(localPto.animationNodes) |
307 | end |
308 | end |
309 | end |
197 | function PowerTakeOffs:onLoad(savegame) |
198 | local spec = self.spec_powerTakeOffs |
199 | |
200 | local configKey = string.format("vehicle.powerTakeOffs.powerTakeOffConfigurations.powerTakeOffConfiguration(%d)", spec.configIndex - 1) |
201 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.powerTakeOffs.powerTakeOffConfigurations.powerTakeOffConfiguration", spec.configIndex, self.components, self) |
202 | |
203 | spec.outputPowerTakeOffs = {} |
204 | spec.inputPowerTakeOffs = {} |
205 | spec.localPowerTakeOffs = {} |
206 | |
207 | self:loadPowerTakeOffsFromXML(self.xmlFile, "vehicle.powerTakeOffs") |
208 | |
209 | if self.xmlFile:hasProperty(configKey) then |
210 | self:loadPowerTakeOffsFromXML(self.xmlFile, configKey) |
211 | end |
212 | |
213 | spec.ignoreInvalidJointIndices = self.xmlFile:getValue("vehicle.powerTakeOffs#ignoreInvalidJointIndices", false) |
214 | spec.maxUpdateDistance = self.xmlFile:getValue("vehicle.powerTakeOffs#maxUpdateDistance", PowerTakeOffs.DEFAULT_MAX_UPDATE_DISTANCE) |
215 | spec.delayedPowerTakeOffsMountings = {} |
216 | |
217 | if not self.isClient then |
218 | SpecializationUtil.removeEventListener(self, "onPostUpdate", PowerTakeOffs) |
219 | end |
220 | end |
224 | function PowerTakeOffs:onPostLoad(savegame) |
225 | local spec = self.spec_powerTakeOffs |
226 | |
227 | for i=1, #spec.outputPowerTakeOffs do |
228 | local powerTakeOffOutput = spec.outputPowerTakeOffs[i] |
229 | |
230 | for index, _ in pairs(powerTakeOffOutput.attacherJointIndices) do |
231 | if self:getAttacherJointByJointDescIndex(index) == nil then |
232 | if not spec.ignoreInvalidJointIndices then |
233 | Logging.xmlWarning(self.xmlFile, "The given attacherJointIndex '%d' for powerTakeOff output '%s' can't be resolved into a valid attacherJoint", index, i) |
234 | end |
235 | |
236 | powerTakeOffOutput.attacherJointIndices[index] = false |
237 | end |
238 | end |
239 | end |
240 | |
241 | for i=1, #spec.inputPowerTakeOffs do |
242 | local inputPowerTakeOff = spec.inputPowerTakeOffs[i] |
243 | |
244 | for index, _ in pairs(inputPowerTakeOff.inputAttacherJointIndices) do |
245 | if self:getInputAttacherJointByJointDescIndex(index) == nil then |
246 | if not spec.ignoreInvalidJointIndices then |
247 | Logging.xmlWarning(self.xmlFile, "The given inputAttacherJointIndex '%d' for powerTakeOff input '%s' can't be resolved into a valid attacherJoint", index, i) |
248 | end |
249 | |
250 | inputPowerTakeOff.inputAttacherJointIndices[index] = false |
251 | end |
252 | end |
253 | end |
254 | end |
313 | function PowerTakeOffs:onPostUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
314 | if self.isClient then |
315 | local spec = self.spec_powerTakeOffs |
316 | if self.currentUpdateDistance < spec.maxUpdateDistance then |
317 | for i=1, #spec.inputPowerTakeOffs do |
318 | local input = spec.inputPowerTakeOffs[i] |
319 | if input.connectedVehicle ~= nil then |
320 | if self.updateLoopIndex == input.connectedVehicle.updateLoopIndex then |
321 | self:updatePowerTakeOff(input, dt) |
322 | end |
323 | end |
324 | end |
325 | |
326 | if self.getAttachedImplements ~= nil then |
327 | local impements = self:getAttachedImplements() |
328 | for i=1, #impements do |
329 | local object = impements[i].object |
330 | if object.updateAttachedPowerTakeOffs ~= nil then |
331 | object:updateAttachedPowerTakeOffs(dt, self) |
332 | end |
333 | end |
334 | end |
335 | |
336 | local isPowerTakeOffActive = self:getIsPowerTakeOffActive() |
337 | if spec.lastIsPowerTakeOffActive ~= isPowerTakeOffActive then |
338 | for i=1, #spec.inputPowerTakeOffs do |
339 | local input = spec.inputPowerTakeOffs[i] |
340 | if isPowerTakeOffActive and input.connectedVehicle ~= nil then |
341 | g_animationManager:startAnimations(input.animationNodes) |
342 | else |
343 | g_animationManager:stopAnimations(input.animationNodes) |
344 | end |
345 | end |
346 | |
347 | for i=1, #spec.localPowerTakeOffs do |
348 | local localPto = spec.localPowerTakeOffs[i] |
349 | if isPowerTakeOffActive then |
350 | g_animationManager:startAnimations(localPto.animationNodes) |
351 | else |
352 | g_animationManager:stopAnimations(localPto.animationNodes) |
353 | end |
354 | end |
355 | |
356 | spec.lastIsPowerTakeOffActive = isPowerTakeOffActive |
357 | end |
358 | end |
359 | end |
360 | end |
794 | function PowerTakeOffs:onPowerTakeOffI3DLoaded(i3dNode, failedReason, args) |
795 | local xmlFile = args.xmlFile |
796 | local powerTakeOff = args.powerTakeOff |
797 | |
798 | if i3dNode ~= 0 then |
799 | powerTakeOff.startNode = xmlFile:getValue("powerTakeOff.startNode#node", nil, i3dNode) |
800 | powerTakeOff.size = xmlFile:getValue("powerTakeOff#size", 0.19) |
801 | powerTakeOff.minLength = xmlFile:getValue("powerTakeOff#minLength", 0.6) |
802 | powerTakeOff.maxAngle = xmlFile:getValue("powerTakeOff#maxAngle", 45) |
803 | powerTakeOff.zOffset = xmlFile:getValue("powerTakeOff#zOffset", 0) |
804 | |
805 | powerTakeOff.animationNodes = g_animationManager:loadAnimations(xmlFile, "powerTakeOff.animationNodes", i3dNode, self) |
806 | |
807 | if xmlFile:getValue("powerTakeOff#isSingleJoint") then |
808 | self:loadSingleJointPowerTakeOff(powerTakeOff, xmlFile, i3dNode) |
809 | elseif xmlFile:getValue("powerTakeOff#isDoubleJoint") then |
810 | self:loadDoubleJointPowerTakeOff(powerTakeOff, xmlFile, i3dNode) |
811 | else |
812 | self:loadBasicPowerTakeOff(powerTakeOff, xmlFile, i3dNode) |
813 | end |
814 | |
815 | if powerTakeOff.color ~= nil and #powerTakeOff.color >= 3 then |
816 | local colorShaderParameter = xmlFile:getValue("powerTakeOff#colorShaderParameter") |
817 | if colorShaderParameter ~= nil then |
818 | I3DUtil.setShaderParameterRec(powerTakeOff.startNode, colorShaderParameter, powerTakeOff.color[1], powerTakeOff.color[2], powerTakeOff.color[3]) |
819 | end |
820 | end |
821 | |
822 | if powerTakeOff.decalColor ~= nil and #powerTakeOff.decalColor >= 3 then |
823 | local decalColorShaderParameter = xmlFile:getValue("powerTakeOff#decalColorShaderParameter") |
824 | if decalColorShaderParameter ~= nil then |
825 | I3DUtil.setShaderParameterRec(powerTakeOff.startNode, decalColorShaderParameter, powerTakeOff.decalColor[1], powerTakeOff.decalColor[2], powerTakeOff.decalColor[3]) |
826 | end |
827 | end |
828 | |
829 | link(powerTakeOff.inputNode, powerTakeOff.startNode) |
830 | |
831 | powerTakeOff.i3dLoaded = true |
832 | |
833 | if powerTakeOff.isLocal then |
834 | self:placeLocalPowerTakeOff(powerTakeOff) |
835 | else |
836 | self:parkPowerTakeOff(powerTakeOff) |
837 | end |
838 | |
839 | self:updatePowerTakeOff(powerTakeOff, 0) |
840 | |
841 | delete(i3dNode) |
842 | else |
843 | if not (self.isDeleted or self.isDeleting) then |
844 | Logging.xmlWarning(self.xmlFile, "Failed to find powerTakeOff in i3d file '%s'", powerTakeOff.filename) |
845 | end |
846 | end |
847 | |
848 | xmlFile:delete() |
849 | powerTakeOff.xmlFile = nil |
850 | end |
714 | function PowerTakeOffs:parkPowerTakeOff(input) |
715 | if input.detachNode ~= nil then |
716 | link(input.detachNode, input.linkNode) |
717 | link(input.inputNode, input.startNode) |
718 | self:updatePowerTakeOff(input, 0) |
719 | self:updatePowerTakeOffLength(input) |
720 | else |
721 | -- keep both inside of the vehicle |
722 | -- so other specializations can still keep track of them (e.g. washable) |
723 | link(input.inputNode, input.linkNode) |
724 | link(input.inputNode, input.startNode) |
725 | |
726 | setVisibility(input.linkNode, false) |
727 | setVisibility(input.startNode, false) |
728 | end |
729 | |
730 | setTranslation(input.linkNode, 0, 0, input.zOffset) |
731 | setTranslation(input.startNode, 0, 0, -input.zOffset) |
732 | end |
131 | function PowerTakeOffs.registerFunctions(vehicleType) |
132 | SpecializationUtil.registerFunction(vehicleType, "getPowerTakeOffConfigIndex", PowerTakeOffs.getPowerTakeOffConfigIndex) |
133 | SpecializationUtil.registerFunction(vehicleType, "loadPowerTakeOffsFromXML", PowerTakeOffs.loadPowerTakeOffsFromXML) |
134 | SpecializationUtil.registerFunction(vehicleType, "loadOutputPowerTakeOff", PowerTakeOffs.loadOutputPowerTakeOff) |
135 | SpecializationUtil.registerFunction(vehicleType, "loadInputPowerTakeOff", PowerTakeOffs.loadInputPowerTakeOff) |
136 | SpecializationUtil.registerFunction(vehicleType, "loadLocalPowerTakeOff", PowerTakeOffs.loadLocalPowerTakeOff) |
137 | SpecializationUtil.registerFunction(vehicleType, "placeLocalPowerTakeOff", PowerTakeOffs.placeLocalPowerTakeOff) |
138 | SpecializationUtil.registerFunction(vehicleType, "updatePowerTakeOff", PowerTakeOffs.updatePowerTakeOff) |
139 | SpecializationUtil.registerFunction(vehicleType, "updateAttachedPowerTakeOffs", PowerTakeOffs.updateAttachedPowerTakeOffs) |
140 | SpecializationUtil.registerFunction(vehicleType, "updatePowerTakeOffLength", PowerTakeOffs.updatePowerTakeOffLength) |
141 | SpecializationUtil.registerFunction(vehicleType, "getOutputPowerTakeOffsByJointDescIndex", PowerTakeOffs.getOutputPowerTakeOffsByJointDescIndex) |
142 | SpecializationUtil.registerFunction(vehicleType, "getOutputPowerTakeOffs", PowerTakeOffs.getOutputPowerTakeOffs) |
143 | SpecializationUtil.registerFunction(vehicleType, "getInputPowerTakeOffs", PowerTakeOffs.getInputPowerTakeOffs) |
144 | SpecializationUtil.registerFunction(vehicleType, "getInputPowerTakeOffsByJointDescIndexAndName", PowerTakeOffs.getInputPowerTakeOffsByJointDescIndexAndName) |
145 | SpecializationUtil.registerFunction(vehicleType, "getIsPowerTakeOffActive", PowerTakeOffs.getIsPowerTakeOffActive) |
146 | SpecializationUtil.registerFunction(vehicleType, "attachPowerTakeOff", PowerTakeOffs.attachPowerTakeOff) |
147 | SpecializationUtil.registerFunction(vehicleType, "detachPowerTakeOff", PowerTakeOffs.detachPowerTakeOff) |
148 | SpecializationUtil.registerFunction(vehicleType, "checkPowerTakeOffCollision", PowerTakeOffs.checkPowerTakeOffCollision) |
149 | SpecializationUtil.registerFunction(vehicleType, "parkPowerTakeOff", PowerTakeOffs.parkPowerTakeOff) |
150 | |
151 | SpecializationUtil.registerFunction(vehicleType, "loadPowerTakeOffFromConfigFile", PowerTakeOffs.loadPowerTakeOffFromConfigFile) |
152 | SpecializationUtil.registerFunction(vehicleType, "onPowerTakeOffI3DLoaded", PowerTakeOffs.onPowerTakeOffI3DLoaded) |
153 | |
154 | SpecializationUtil.registerFunction(vehicleType, "loadSingleJointPowerTakeOff", PowerTakeOffs.loadSingleJointPowerTakeOff) |
155 | SpecializationUtil.registerFunction(vehicleType, "updateSingleJointPowerTakeOff", PowerTakeOffs.updateSingleJointPowerTakeOff) |
156 | |
157 | SpecializationUtil.registerFunction(vehicleType, "loadDoubleJointPowerTakeOff", PowerTakeOffs.loadDoubleJointPowerTakeOff) |
158 | SpecializationUtil.registerFunction(vehicleType, "updateDoubleJointPowerTakeOff", PowerTakeOffs.updateDoubleJointPowerTakeOff) |
159 | |
160 | SpecializationUtil.registerFunction(vehicleType, "loadBasicPowerTakeOff", PowerTakeOffs.loadBasicPowerTakeOff) |
161 | |
162 | SpecializationUtil.registerFunction(vehicleType, "attachTypedPowerTakeOff", PowerTakeOffs.attachTypedPowerTakeOff) |
163 | SpecializationUtil.registerFunction(vehicleType, "detachTypedPowerTakeOff", PowerTakeOffs.detachTypedPowerTakeOff) |
164 | |
165 | SpecializationUtil.registerFunction(vehicleType, "validatePowerTakeOffAttachment", PowerTakeOffs.validatePowerTakeOffAttachment) |
166 | end |
105 | function PowerTakeOffs.registerInputXMLPaths(schema, basePath) |
106 | schema:register(XMLValueType.NODE_INDEX, basePath .. "#inputNode", "Input node") |
107 | schema:register(XMLValueType.VECTOR_N, basePath .. "#inputAttacherJointIndices", "Input attacher joint indices") |
108 | schema:register(XMLValueType.NODE_INDEX, basePath .. "#detachNode", "Detach node") |
109 | schema:register(XMLValueType.BOOL, basePath .. "#aboveAttacher", "Above attacher", true) |
110 | schema:register(XMLValueType.COLOR, basePath .. "#color", "Color") |
111 | schema:register(XMLValueType.COLOR, basePath .. "#decalColor", "Color of decals") |
112 | schema:register(XMLValueType.STRING, basePath .. "#filename", "Path to pto xml file", "$data/shared/assets/powerTakeOffs/walterscheidW.xml") |
113 | schema:register(XMLValueType.STRING, basePath .. "#ptoName", "Pto name", "DEFAULT_PTO") |
114 | |
115 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, basePath) |
116 | end |
977 | function PowerTakeOffs:updateDistanceOfTypedPowerTakeOff(powerTakeOff) |
978 | local attachLength = calcDistanceFrom(powerTakeOff.linkNode, powerTakeOff.startNode) |
979 | local transPartScale = math.max(attachLength - powerTakeOff.connectorLength, 0) / powerTakeOff.betweenLength |
980 | setScale(powerTakeOff.translationPart, 1, 1, transPartScale) |
981 | |
982 | if powerTakeOff.decal ~= nil then |
983 | local transPartLength = transPartScale * powerTakeOff.translationPartLength |
984 | |
985 | if transPartLength > powerTakeOff.decalMinOffset * 2 + powerTakeOff.decalSize then |
986 | local offset = math.min((transPartLength - powerTakeOff.decalSize) / 2, powerTakeOff.decalOffset) |
987 | local decalTranslation = offset + powerTakeOff.decalSize * 0.5 |
988 | local x, y, _ = getTranslation(powerTakeOff.decal) |
989 | setTranslation(powerTakeOff.decal, x, y, -decalTranslation/transPartScale) |
990 | setScale(powerTakeOff.decal, 1, 1, 1/transPartScale) |
991 | else |
992 | setVisibility(powerTakeOff.decal, false) |
993 | end |
994 | end |
995 | end |
945 | function PowerTakeOffs:updateDoubleJointPowerTakeOff(powerTakeOff, dt) |
946 | local x, y, z = getWorldTranslation(powerTakeOff.startNode) |
947 | local dx, dy, dz = worldToLocal(getParent(powerTakeOff.endJoint2), x, y, z) |
948 | I3DUtil.setDirection(powerTakeOff.endJoint2, dx*0.5, dy*0.5, dz, 0, 1, 0) |
949 | |
950 | x, y, z = getWorldTranslation(powerTakeOff.endJoint1Ref) |
951 | dx, dy, dz = worldToLocal(getParent(powerTakeOff.startJoint1), x, y, z) |
952 | I3DUtil.setDirection(powerTakeOff.startJoint1, dx*0.5, dy*0.5, dz, 0, 1, 0) |
953 | |
954 | x, y, z = getWorldTranslation(powerTakeOff.endJoint1Ref) |
955 | dx, dy, dz = worldToLocal(getParent(powerTakeOff.startJoint2), x, y, z) |
956 | I3DUtil.setDirection(powerTakeOff.startJoint2, dx, dy, dz, 0, 1, 0) |
957 | |
958 | dx, dy, dz = worldToLocal(getParent(powerTakeOff.endJoint1), x, y, z) |
959 | setTranslation(powerTakeOff.endJoint1, 0, 0, MathUtil.vector3Length(dx, dy, dz)) |
960 | |
961 | local dist = calcDistanceFrom(powerTakeOff.scalePart, powerTakeOff.scalePartRef) |
962 | setScale(powerTakeOff.scalePart, 1, 1, dist / powerTakeOff.scalePartBaseDistance) |
963 | end |
1075 | function PowerTakeOffs:updateExtraDependentParts(superFunc, part, dt) |
1076 | superFunc(self, part, dt) |
1077 | |
1078 | if part.powerTakeOffs ~= nil then |
1079 | local spec = self.spec_powerTakeOffs |
1080 | for i, index in ipairs(part.powerTakeOffs) do |
1081 | if spec.inputPowerTakeOffs[index] == nil then |
1082 | part.powerTakeOffs[i] = nil |
1083 | Logging.xmlWarning(self.xmlFile, "Unable to find powerTakeOff index '%d' for movingPart/movingTool '%s'", index, getName(part.node)) |
1084 | else |
1085 | self:updatePowerTakeOff(spec.inputPowerTakeOffs[index], dt) |
1086 | end |
1087 | end |
1088 | end |
1089 | |
1090 | if part.localPowerTakeOffs ~= nil then |
1091 | local spec = self.spec_powerTakeOffs |
1092 | for i, index in ipairs(part.localPowerTakeOffs) do |
1093 | if spec.localPowerTakeOffs[index] == nil then |
1094 | part.localPowerTakeOffs[i] = nil |
1095 | Logging.xmlWarning(self.xmlFile, "Unable to find local powerTakeOff index '%d' for movingPart/movingTool '%s'", index, getName(part.node)) |
1096 | else |
1097 | self:placeLocalPowerTakeOff(spec.localPowerTakeOffs[index], dt) |
1098 | end |
1099 | end |
1100 | end |
1101 | end |
890 | function PowerTakeOffs:updateSingleJointPowerTakeOff(powerTakeOff, dt) |
891 | local x, y, z = getWorldTranslation(powerTakeOff.linkNode) |
892 | |
893 | local dx, dy, dz = worldToLocal(powerTakeOff.startNode, x, y, z) |
894 | I3DUtil.setDirection(powerTakeOff.startJoint, dx, dy, dz, 0, 1, 0) |
895 | |
896 | dx, dy, dz = worldToLocal(getParent(powerTakeOff.endJoint), x, y, z) |
897 | setTranslation(powerTakeOff.endJoint, 0, 0, MathUtil.vector3Length(dx, dy, dz)) |
898 | |
899 | local dist = calcDistanceFrom(powerTakeOff.scalePart, powerTakeOff.scalePartRef) |
900 | setScale(powerTakeOff.scalePart, 1, 1, dist / powerTakeOff.scalePartBaseDistance) |
901 | end |