469 | function Pipe:loadPipeNodes(pipeNodes, xmlFile, baseKey) |
470 | local spec = self.spec_pipe |
471 | |
472 | local maxPriority = 0 |
473 | local i = 0 |
474 | while true do |
475 | local key = string.format("%s(%d)", baseKey, i) |
476 | if not xmlFile:hasProperty(key) then |
477 | break |
478 | end |
479 | local node = xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings) |
480 | if node ~= nil then |
481 | local entry = {} |
482 | entry.node = node |
483 | entry.autoAimXRotation = xmlFile:getValue(key.."#autoAimXRotation", false) |
484 | entry.autoAimYRotation = xmlFile:getValue(key.."#autoAimYRotation", false) |
485 | entry.autoAimInvertZ = xmlFile:getValue(key.."#autoAimInvertZ", false) |
486 | entry.states = {} |
487 | |
488 | entry.subPipeNode = xmlFile:getValue(key.."#subPipeNode", nil, self.components, self.i3dMappings) |
489 | if entry.subPipeNode ~= nil then |
490 | local x1, _, _ = getRotation(entry.node) |
491 | local x2, _, _ = localRotationToLocal(entry.subPipeNode, getParent(entry.node), 0, 0, 0) |
492 | entry.subPipeNodeRatio = xmlFile:getValue(key.."#subPipeNodeRatio", math.abs(x1 / x2)) |
493 | end |
494 | |
495 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, key .. ".state1", key .. ".state") -- FS19 to FS21 |
496 | |
497 | for state=1, spec.numStates do |
498 | local stateKey = key..string.format(".state(%d)", state - 1) |
499 | entry.states[state] = {} |
500 | |
501 | local x, y, z = xmlFile:getValue(stateKey.."#translation", {getTranslation(node)}) |
502 | if state == 1 then |
503 | setTranslation(node, x, y, z) |
504 | end |
505 | entry.states[state].translation = {x, y, z} |
506 | |
507 | x, y, z = xmlFile:getValue(stateKey.."#rotation", {getRotation(node)}) |
508 | if state == 1 then |
509 | setRotation(node, x, y, z) |
510 | end |
511 | entry.states[state].rotation = {x, y, z} |
512 | end |
513 | |
514 | local x, y, z = xmlFile:getValue(key.."#translationSpeeds") |
515 | if x ~= nil and y ~= nil and z ~= nil then |
516 | x, y, z = x * 0.001, y * 0.001, z * 0.001 |
517 | if x ~= 0 or y ~= 0 or z ~= 0 then |
518 | entry.translationSpeeds = {x, y, z} |
519 | end |
520 | end |
521 | x, y, z = xmlFile:getValue(key.."#rotationSpeeds") |
522 | if x ~= nil and y ~= nil and z ~= nil then |
523 | x, y, z = x * 0.001, y * 0.001, z *0.001 |
524 | if x ~= 0 or y ~= 0 or z ~= 0 then |
525 | entry.rotationSpeeds = {x, y, z} |
526 | end |
527 | end |
528 | |
529 | x, y, z = string.getVector(xmlFile:getValue(key.."#minRotationLimits")) |
530 | if x ~= nil or y ~= nil or z ~= nil then |
531 | x = (x ~= nil and math.rad(x)) or nil |
532 | y = (y ~= nil and math.rad(y)) or nil |
533 | z = (z ~= nil and math.rad(z)) or nil |
534 | entry.minRotationLimits = {x, y, z} |
535 | end |
536 | x, y, z = string.getVector(xmlFile:getValue(key.."#maxRotationLimits")) |
537 | if x ~= nil or y ~= nil or z ~= nil then |
538 | x = (x ~= nil and math.rad(x)) or nil |
539 | y = (y ~= nil and math.rad(y)) or nil |
540 | z = (z ~= nil and math.rad(z)) or nil |
541 | entry.maxRotationLimits = {x, y, z} |
542 | end |
543 | |
544 | entry.foldPriority = xmlFile:getValue(key.."#foldPriority", 0) |
545 | maxPriority = math.max(entry.foldPriority, maxPriority) |
546 | |
547 | x, y, z = getTranslation(node) |
548 | entry.curTranslation = {x, y, z} |
549 | |
550 | x, y, z = getRotation(node) |
551 | entry.curRotation = {x, y, z} |
552 | entry.lastTargetRotation = {x, y, z} |
553 | |
554 | entry.bendingRegulation = xmlFile:getValue(key.."#bendingRegulation", 0) |
555 | |
556 | entry.regulationNodes = {} |
557 | |
558 | local j = 0 |
559 | while true do |
560 | local regKey = string.format("%s.bendingRegulationNode(%d)", key, j) |
561 | if not xmlFile:hasProperty(regKey) then |
562 | break |
563 | end |
564 | |
565 | local regulationNode = {} |
566 | regulationNode.node = xmlFile:getValue(regKey.."#node", nil, self.components, self.i3dMappings) |
567 | if regulationNode.node ~= nil then |
568 | regulationNode.startRotation = {getRotation(regulationNode.node)} |
569 | |
570 | local axis = xmlFile:getValue(regKey.."#axis", 1) |
571 | local direction = xmlFile:getValue(regKey.."#direction", 1) |
572 | |
573 | regulationNode.weights = {0, 0, 0} |
574 | regulationNode.weights[MathUtil.clamp(axis, 1, 3)] = direction |
575 | |
576 | table.insert(entry.regulationNodes, regulationNode) |
577 | else |
578 | Logging.xmlWarning(self.xmlFile, "Failed to load bendingRegulationNode '%s'", regKey) |
579 | end |
580 | |
581 | j = j + 1 |
582 | end |
583 | |
584 | table.insert(pipeNodes, entry) |
585 | end |
586 | i = i + 1 |
587 | end |
588 | |
589 | for _, pipeNode in ipairs(pipeNodes) do |
590 | pipeNode.inverseFoldPriority = maxPriority-pipeNode.foldPriority |
591 | end |
592 | end |
158 | function Pipe:onLoad(savegame) |
159 | local spec = self.spec_pipe |
160 | |
161 | local pipeConfigurationId = Utils.getNoNil(self.configurations["pipe"], 1) |
162 | local baseKey = string.format("vehicle.pipe.pipeConfigurations.pipeConfiguration(%d)", pipeConfigurationId -1) |
163 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.pipe.pipeConfigurations.pipeConfiguration", pipeConfigurationId , self.components, self) |
164 | |
165 | -- fallback key |
166 | if not self.xmlFile:hasProperty(baseKey) then |
167 | baseKey = "vehicle.pipe" |
168 | end |
169 | |
170 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipeEffect.effectNode", baseKey..".pipeEffect.effectNode") --FS17 to FS19 |
171 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.overloading.trailerTriggers.trailerTrigger(0)#index", baseKey..".unloadingTriggers.unloadingTrigger(0)#node") --FS17 to FS19 |
172 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe#raycastNodeIndex", baseKey..".raycast#node") --FS17 to FS19 |
173 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe#raycastDistance", baseKey..".raycast#maxDistance") --FS17 to FS19 |
174 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe#effectExtraDistanceOnTrailer", baseKey..".raycast#extraDistance") --FS17 to FS19 |
175 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe#animName", baseKey..".animation#name") --FS17 to FS19 |
176 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe#animSpeedScale", baseKey..".animation#speedScale") --FS17 to FS19 |
177 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe#animSpeedScale", baseKey..".animation#speedScale") --FS17 to FS19 |
178 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe.node#node", baseKey..".node#node") --FS17 to FS19 |
179 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe#numStates", baseKey..".states#num") --FS17 to FS19 |
180 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe#unloadingStates", baseKey..".states#unloading") --FS17 to FS19 |
181 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe#autoAimingStates", baseKey..".states#autoAiming") --FS17 to FS19 |
182 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipe#turnOnAllowed", baseKey..".states#turnOnAllowed") --FS17 to FS19 |
183 | |
184 | spec.turnOnStateWarning = string.format(self.xmlFile:getValue(baseKey.."#turnOnStateWarning", "warning_firstSetPipeState", self.customEnvironment), self.typeDesc) |
185 | |
186 | spec.dischargeNodeIndex = self.xmlFile:getValue(baseKey .. "#dischargeNodeIndex", 1) |
187 | spec.forceDischargeNodeIndex = self.xmlFile:getValue(baseKey .. "#forceDischargeNodeIndex", true) |
188 | if spec.forceDischargeNodeIndex then |
189 | self:setCurrentDischargeNodeIndex(spec.dischargeNodeIndex) |
190 | end |
191 | |
192 | spec.automaticDischarge = self.xmlFile:getValue(baseKey .. "#automaticDischarge", true) |
193 | spec.toggleableDischargeToGround = self.xmlFile:getValue(baseKey .. "#toggleableDischargeToGround", false) |
194 | spec.dischargeToGroundState = false |
195 | |
196 | spec.unloadingTriggers = {} |
197 | spec.objectsInTriggers = {} |
198 | spec.unloadTriggersInTriggers = {} |
199 | spec.numObjectsInTriggers = 0 |
200 | spec.numUnloadTriggersInTriggers = 0 |
201 | spec.nearestObjectInTriggers = {objectId=nil, fillUnitIndex=0} |
202 | spec.nearestObjectInTriggersSent = {objectId=nil, fillUnitIndex=0} |
203 | |
204 | self:loadUnloadingTriggers(spec.unloadingTriggers, self.xmlFile, baseKey .. ".unloadingTriggers.unloadingTrigger") |
205 | if #spec.unloadingTriggers == 0 then |
206 | Logging.xmlWarning(self.xmlFile, "No 'unloadingTriggers' defined for pipe 'vehicle.pipe'!") |
207 | else |
208 | for _,trigger in pairs(spec.unloadingTriggers) do |
209 | addTrigger(trigger.node, "unloadingTriggerCallback", self) |
210 | end |
211 | end |
212 | |
213 | spec.animation = {} |
214 | spec.animation.name = self.xmlFile:getValue(baseKey .. ".animation#name") |
215 | spec.animation.speedScale = self.xmlFile:getValue(baseKey .. ".animation#speedScale", 1) |
216 | |
217 | spec.currentState = 1 |
218 | spec.targetState = 1 |
219 | spec.numStates = self.xmlFile:getValue(baseKey .. ".states#num", 0) |
220 | |
221 | spec.nodes = {} |
222 | self:loadPipeNodes(spec.nodes, self.xmlFile, baseKey .. ".pipeNodes.pipeNode") |
223 | spec.hasMovablePipe = #spec.nodes > 0 or spec.animation.name ~= nil |
224 | |
225 | local function loadState(target, xmlFile, key) |
226 | local i = 0 |
227 | local states = xmlFile:getValue(key, nil, true) |
228 | if states ~= nil then |
229 | for _, state in ipairs(states) do |
230 | target[state] = true |
231 | i = i + 1 |
232 | end |
233 | end |
234 | |
235 | return i |
236 | end |
237 | |
238 | spec.unloadingStates = {} |
239 | spec.autoAimingStates = {} |
240 | spec.turnOnAllowedStates = {} |
241 | spec.numUnloadingStates = loadState(spec.unloadingStates, self.xmlFile, baseKey .. ".states#unloading") |
242 | spec.numAutoAimingStates = loadState(spec.autoAimingStates, self.xmlFile, baseKey .. ".states#autoAiming") |
243 | spec.numTurnOnAllowedStates = loadState(spec.turnOnAllowedStates, self.xmlFile, baseKey .. ".states#turnOnAllowed") |
244 | |
245 | spec.dischargeNodeMapping = {} |
246 | local i = 0 |
247 | while true do |
248 | local stateKey = string.format("%s.states.state(%d)", baseKey, i) |
249 | if not self.xmlFile:hasProperty(stateKey) then |
250 | break |
251 | end |
252 | |
253 | local stateIndex = self.xmlFile:getValue(stateKey .. "#stateIndex") |
254 | local dischargeNodeIndex = self.xmlFile:getValue(stateKey .. "#dischargeNodeIndex") |
255 | |
256 | if stateIndex ~= nil and dischargeNodeIndex ~= nil then |
257 | spec.dischargeNodeMapping[stateIndex] = dischargeNodeIndex |
258 | end |
259 | |
260 | i = i + 1 |
261 | end |
262 | |
263 | if self.isClient then |
264 | spec.animationNodes = g_animationManager:loadAnimations(self.xmlFile, "vehicle.pipe.animationNodes", self.components, self, self.i3dMappings) |
265 | end |
266 | |
267 | spec.foldMinTime = self.xmlFile:getValue(baseKey .. "#foldMinLimit", 0.0) |
268 | spec.foldMaxTime = self.xmlFile:getValue(baseKey .. "#foldMaxLimit", 1.0) |
269 | spec.foldMinState = self.xmlFile:getValue(baseKey .. "#foldMinState", 1) |
270 | spec.foldMaxState = self.xmlFile:getValue(baseKey .. "#foldMaxState", spec.numStates) |
271 | |
272 | spec.aiFoldedPipeUsesTrailerSpace = self.xmlFile:getValue(baseKey .. "#aiFoldedPipeUsesTrailerSpace", false) |
273 | |
274 | spec.texts = {} |
275 | spec.texts.warningFoldingPipe = g_i18n:getText("warning_foldingNotWhilePipeExtended") |
276 | spec.texts.pipeIn = g_i18n:getText("action_pipeIn") |
277 | spec.texts.pipeOut = g_i18n:getText("action_pipeOut") |
278 | spec.texts.startTipToGround = g_i18n:getText("action_startTipToGround") |
279 | spec.texts.stopTipToGround = g_i18n:getText("action_stopTipToGround") |
280 | |
281 | spec.dirtyFlag = self:getNextDirtyFlag() |
282 | |
283 | spec.lastFillTime = -1000 |
284 | spec.lastEmptyTime = -1000 |
285 | |
286 | if not self.isServer then |
287 | SpecializationUtil.removeEventListener(self, "onUpdateTick", Pipe) |
288 | end |
289 | end |
119 | function Pipe.registerOverwrittenFunctions(vehicleType) |
120 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsDischargeNodeActive", Pipe.getIsDischargeNodeActive) |
121 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanBeTurnedOn", Pipe.getCanBeTurnedOn) |
122 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getTurnedOnNotAllowedWarning", Pipe.getTurnedOnNotAllowedWarning) |
123 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsFoldAllowed", Pipe.getIsFoldAllowed) |
124 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "handleDischarge", Pipe.handleDischarge) |
125 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "handleDischargeRaycast", Pipe.handleDischargeRaycast) |
126 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "dischargeToGround", Pipe.dischargeToGround) |
127 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanToggleDischargeToObject", Pipe.getCanToggleDischargeToObject) |
128 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanToggleDischargeToGround", Pipe.getCanToggleDischargeToGround) |
129 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadMovingToolFromXML", Pipe.loadMovingToolFromXML) |
130 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsMovingToolActive", Pipe.getIsMovingToolActive) |
131 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadCoverFromXML", Pipe.loadCoverFromXML) |
132 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsNextCoverStateAllowed", Pipe.getIsNextCoverStateAllowed) |
133 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanBeSelected", Pipe.getCanBeSelected) |
134 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsAIReadyToDrive", Pipe.getIsAIReadyToDrive) |
135 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsAIPreparingToDrive", Pipe.getIsAIPreparingToDrive) |
136 | end |
49 | function Pipe.registers(schema, basePath) |
50 | schema:register(XMLValueType.L10N_STRING, basePath .. "#turnOnStateWarning", "Turn on warning", "warning_firstSetPipeState") |
51 | schema:register(XMLValueType.INT, basePath .. "#dischargeNodeIndex", "Discharge node index", 1) |
52 | schema:register(XMLValueType.BOOL, basePath .. "#forceDischargeNodeIndex", "Force discharge node selection while changing pipe state. Can be deactivated e.g. if the selection is done by trailer spec etc.", true) |
53 | schema:register(XMLValueType.BOOL, basePath .. "#automaticDischarge", "Pipe is automatically starting to discharge as soon as it hits the trailer", true) |
54 | schema:register(XMLValueType.BOOL, basePath .. "#toggleableDischargeToGround", "Defines if the discharge to ground can be enabled separatly", false) |
55 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".unloadingTriggers.unloadingTrigger(?)#node", "Unload trigger node") |
56 | |
57 | schema:register(XMLValueType.STRING, basePath .. ".animation#name", "Pipe animation name") |
58 | schema:register(XMLValueType.FLOAT, basePath .. ".animation#speedScale", "Pipe animation speed scale", 1) |
59 | schema:register(XMLValueType.INT, basePath .. ".states#num", "Number of pipe states", 0) |
60 | |
61 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".pipeNodes.pipeNode(?)#node", "Pipe node") |
62 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".pipeNodes.pipeNode(?)#subPipeNode", "Sub pipe node (Target rotation is devided between these two nodes depending on the X rotation ratio between #node and #node parent and #subPipeNode and #node parent)") |
63 | schema:register(XMLValueType.FLOAT, basePath .. ".pipeNodes.pipeNode(?)#subPipeNodeRatio", "Ratio between usage of this pipe node and sub node [0-1]", "Calculated based on rotation in i3d file") |
64 | schema:register(XMLValueType.BOOL, basePath .. ".pipeNodes.pipeNode(?)#autoAimXRotation", "Auto aim X rotation", false) |
65 | schema:register(XMLValueType.BOOL, basePath .. ".pipeNodes.pipeNode(?)#autoAimYRotation", "Auto aim Y rotation", false) |
66 | schema:register(XMLValueType.BOOL, basePath .. ".pipeNodes.pipeNode(?)#autoAimInvertZ", "Auto aim invert Z axis", false) |
67 | |
68 | schema:register(XMLValueType.VECTOR_TRANS, basePath .. ".pipeNodes.pipeNode(?).state(?)#translation", "State translation") |
69 | schema:register(XMLValueType.VECTOR_ROT, basePath .. ".pipeNodes.pipeNode(?).state(?)#rotation", "State translation") |
70 | |
71 | schema:register(XMLValueType.VECTOR_TRANS, basePath .. ".pipeNodes.pipeNode(?)#translationSpeeds", "Translation speeds") |
72 | schema:register(XMLValueType.VECTOR_ROT, basePath .. ".pipeNodes.pipeNode(?)#rotationSpeeds", "Rotation speeds") |
73 | |
74 | schema:register(XMLValueType.STRING, basePath .. ".pipeNodes.pipeNode(?)#minRotationLimits", "Min. rotation limit") |
75 | schema:register(XMLValueType.STRING, basePath .. ".pipeNodes.pipeNode(?)#maxRotationLimits", "Max. rotation limit") |
76 | |
77 | schema:register(XMLValueType.INT, basePath .. ".pipeNodes.pipeNode(?)#foldPriority", "Fold priority", 0) |
78 | schema:register(XMLValueType.FLOAT, basePath .. ".pipeNodes.pipeNode(?)#bendingRegulation", "Bending angle regulation", 0) |
79 | |
80 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".pipeNodes.pipeNode(?).bendingRegulationNode(?)#node", "Bending regulation node", 0) |
81 | schema:register(XMLValueType.INT, basePath .. ".pipeNodes.pipeNode(?).bendingRegulationNode(?)#axis", "Bending regulation axis", 0) |
82 | schema:register(XMLValueType.INT, basePath .. ".pipeNodes.pipeNode(?).bendingRegulationNode(?)#direction", "Bending regulation direction", 0) |
83 | |
84 | schema:register(XMLValueType.VECTOR_N, basePath .. ".states#unloading", "Unloading states") |
85 | schema:register(XMLValueType.VECTOR_N, basePath .. ".states#autoAiming", "Auto aim states") |
86 | schema:register(XMLValueType.VECTOR_N, basePath .. ".states#turnOnAllowed", "Turn on allowed states") |
87 | |
88 | schema:register(XMLValueType.INT, basePath .. ".states.state(?)#stateIndex", "State index") |
89 | schema:register(XMLValueType.INT, basePath .. ".states.state(?)#dischargeNodeIndex", "Discharge node index") |
90 | |
91 | schema:register(XMLValueType.FLOAT, basePath .. "#foldMinLimit", "Fold min. limit", 0) |
92 | schema:register(XMLValueType.FLOAT, basePath .. "#foldMaxLimit", "Fold max. limit", 1) |
93 | |
94 | schema:register(XMLValueType.INT, basePath .. "#foldMinState", "Fold min. state", 1) |
95 | schema:register(XMLValueType.INT, basePath .. "#foldMaxState", "Fold max. state", "Num. of states") |
96 | |
97 | schema:register(XMLValueType.BOOL, basePath .. "#aiFoldedPipeUsesTrailerSpace", "Defines if the folded pipe uses the space of the trailer to discharge", false) |
98 | end |
847 | function Pipe:unloadingTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId) |
848 | if onEnter or onLeave then |
849 | local object = g_currentMission:getNodeObject(otherId) |
850 | |
851 | if object ~= nil and object ~= self and object:isa(Vehicle) then |
852 | if object.getFillUnitIndexFromNode ~= nil then |
853 | local fillUnitIndex = object:getFillUnitIndexFromNode(otherId) |
854 | if fillUnitIndex ~= nil then |
855 | local spec = self.spec_pipe |
856 | |
857 | local dischargeNode = self:getDischargeNodeByIndex(self:getPipeDischargeNodeIndex()) |
858 | if dischargeNode ~= nil then |
859 | local fillTypes = self:getFillUnitSupportedFillTypes(dischargeNode.fillUnitIndex) |
860 | |
861 | -- objects is only valid if it supports at least one of the harvesters fill types |
862 | local objectSupportsFillType = false |
863 | for fillType, _ in pairs(fillTypes) do |
864 | if object:getFillUnitSupportsFillType(fillUnitIndex, fillType) then |
865 | objectSupportsFillType = true |
866 | break |
867 | end |
868 | end |
869 | |
870 | if objectSupportsFillType then |
871 | if onEnter then |
872 | if spec.objectsInTriggers[object] == nil then |
873 | spec.objectsInTriggers[object] = 0 |
874 | spec.numObjectsInTriggers = spec.numObjectsInTriggers + 1 |
875 | if object.addDeleteListener ~= nil then |
876 | object:addDeleteListener(self, "onDeletePipeObject") |
877 | end |
878 | end |
879 | |
880 | spec.objectsInTriggers[object] = spec.objectsInTriggers[object] + 1 |
881 | else |
882 | spec.objectsInTriggers[object] = spec.objectsInTriggers[object] - 1 |
883 | if spec.objectsInTriggers[object] == 0 then |
884 | spec.objectsInTriggers[object] = nil |
885 | spec.numObjectsInTriggers = spec.numObjectsInTriggers - 1 |
886 | if object.removeDeleteListener ~= nil then |
887 | object:removeDeleteListener(self) |
888 | end |
889 | end |
890 | end |
891 | end |
892 | end |
893 | end |
894 | end |
895 | elseif object ~= nil and object ~= self and object:isa(UnloadTrigger) then |
896 | local spec = self.spec_pipe |
897 | if onEnter then |
898 | if spec.unloadTriggersInTriggers[object] == nil then |
899 | spec.unloadTriggersInTriggers[object] = 0 |
900 | spec.numUnloadTriggersInTriggers = spec.numUnloadTriggersInTriggers + 1 |
901 | if object.addDeleteListener ~= nil then |
902 | object:addDeleteListener(self, "onDeletePipeObject") |
903 | end |
904 | end |
905 | |
906 | spec.unloadTriggersInTriggers[object] = spec.unloadTriggersInTriggers[object] + 1 |
907 | else |
908 | spec.unloadTriggersInTriggers[object] = spec.unloadTriggersInTriggers[object] - 1 |
909 | if spec.unloadTriggersInTriggers[object] == 0 then |
910 | spec.unloadTriggersInTriggers[object] = nil |
911 | spec.numUnloadTriggersInTriggers = spec.numUnloadTriggersInTriggers - 1 |
912 | if object.removeDeleteListener ~= nil then |
913 | object:removeDeleteListener(self) |
914 | end |
915 | end |
916 | end |
917 | end |
918 | end |
919 | end |
824 | function Pipe:updateBendingRegulationNodes(pipeNode, distance) |
825 | local _, dirY, _ = localDirectionToWorld(pipeNode.node, 0, 1, 0) |
826 | |
827 | for _, regulationNode in ipairs(pipeNode.regulationNodes) do |
828 | local regulationAngle = dirY*pipeNode.bendingRegulation |
829 | local weights = regulationNode.weights |
830 | local startRotation = regulationNode.startRotation |
831 | setRotation(regulationNode.node, startRotation[1]+weights[1]*regulationAngle, |
832 | startRotation[2]+weights[2]*regulationAngle, |
833 | startRotation[3]+weights[3]*regulationAngle) |
834 | |
835 | if VehicleDebug.state == VehicleDebug.DEBUG then |
836 | local x1, y1, z1 = getWorldTranslation(regulationNode.node) |
837 | local x2, y2, z2 = localToWorld(regulationNode.node, 0, -10, 0) |
838 | drawDebugLine(x1, y1, z1, 0, 0, 1, x2, y2, z2, 0, 0, 1) |
839 | end |
840 | end |
841 | |
842 | return math.sin(dirY*pipeNode.bendingRegulation) * distance |
843 | end |
923 | function Pipe:updateNearestObjectInTriggers() |
924 | local spec = self.spec_pipe |
925 | |
926 | spec.nearestObjectInTriggers.objectId = nil |
927 | spec.nearestObjectInTriggers.fillUnitIndex = 0 |
928 | |
929 | local minDistance = math.huge |
930 | local dischargeNode = self:getDischargeNodeByIndex(self:getPipeDischargeNodeIndex()) |
931 | if dischargeNode ~= nil then |
932 | local checkNode = Utils.getNoNil(dischargeNode.node, self.components[1].node) |
933 | |
934 | for object, _ in pairs(spec.objectsInTriggers) do |
935 | local outputFillType = self:getFillUnitLastValidFillType(dischargeNode.fillUnitIndex) |
936 | |
937 | for fillUnitIndex, _ in ipairs(object.spec_fillUnit.fillUnits) do |
938 | local allowedToFillByPipe = object:getFillUnitSupportsToolType(fillUnitIndex, ToolType.DISCHARGEABLE) |
939 | local supportsFillType = object:getFillUnitSupportsFillType(fillUnitIndex, outputFillType) or outputFillType == FillType.UNKNOWN |
940 | local fillLevel = object:getFillUnitFreeCapacity(fillUnitIndex, outputFillType, self:getOwnerFarmId()) |
941 | |
942 | if allowedToFillByPipe and supportsFillType and fillLevel > 0 then |
943 | local targetPoint = object:getFillUnitAutoAimTargetNode(fillUnitIndex) |
944 | local exactFillRootNode = object:getFillUnitExactFillRootNode(fillUnitIndex) |
945 | |
946 | if targetPoint == nil then |
947 | targetPoint = exactFillRootNode |
948 | end |
949 | |
950 | if targetPoint ~= nil then |
951 | local distance = calcDistanceFrom(checkNode, targetPoint) |
952 | if distance < minDistance then |
953 | minDistance = distance |
954 | spec.nearestObjectInTriggers.objectId = NetworkUtil.getObjectId(object) |
955 | spec.nearestObjectInTriggers.fillUnitIndex = fillUnitIndex |
956 | break |
957 | end |
958 | end |
959 | end |
960 | end |
961 | end |
962 | else |
963 | Logging.xmlWarning(self.xmlFile, "Unable to find discharge node index '%d' for pipe", self:getPipeDischargeNodeIndex()) |
964 | end |
965 | end |
644 | function Pipe:updatePipeNodes(dt, nearestObjectInTrigger) |
645 | local spec = self.spec_pipe |
646 | local object = nil |
647 | if nearestObjectInTrigger ~= nil then |
648 | object = NetworkUtil.getObject(nearestObjectInTrigger.objectId) |
649 | end |
650 | |
651 | local doAutoAiming = object ~= nil and entityExists(object.components[1].node) and spec.autoAimingStates[spec.currentState] |
652 | |
653 | if spec.currentState ~= spec.targetState or doAutoAiming then |
654 | local priority = spec.targetState ~= spec.numStates and "foldPriority" or "inverseFoldPriority" |
655 | |
656 | local fillAutoAimTargetNode |
657 | local autoAimX, autoAimY, autoAimZ |
658 | if doAutoAiming then |
659 | fillAutoAimTargetNode = object:getFillUnitAutoAimTargetNode(nearestObjectInTrigger.fillUnitIndex) |
660 | if fillAutoAimTargetNode == nil then |
661 | fillAutoAimTargetNode = object:getFillUnitExactFillRootNode(nearestObjectInTrigger.fillUnitIndex) |
662 | end |
663 | |
664 | autoAimX, autoAimY, autoAimZ = getWorldTranslation(fillAutoAimTargetNode) |
665 | |
666 | if VehicleDebug.state == VehicleDebug.DEBUG then |
667 | DebugUtil.drawDebugGizmoAtWorldPos(autoAimX, autoAimY, autoAimZ, 0, 0, 1, 0, 1, 0, getName(fillAutoAimTargetNode), false) |
668 | end |
669 | end |
670 | |
671 | local moved = false |
672 | for i=1, #spec.nodes do |
673 | local nodeMoved = false |
674 | local pipeNode = spec.nodes[i] |
675 | |
676 | local nodeAutoAimY = autoAimY |
677 | if pipeNode.bendingRegulation > 0 then |
678 | if fillAutoAimTargetNode ~= nil then |
679 | local distance = calcDistanceFrom(pipeNode.node, fillAutoAimTargetNode) |
680 | local regulation = self:updateBendingRegulationNodes(pipeNode, distance) |
681 | nodeAutoAimY = autoAimY - regulation |
682 | end |
683 | end |
684 | |
685 | local state = pipeNode.states[spec.targetState] |
686 | if pipeNode.translationSpeeds ~= nil then |
687 | for axis=1, 3 do |
688 | if math.abs(pipeNode.curTranslation[axis] - state.translation[axis]) > 0.000001 then |
689 | nodeMoved = true |
690 | if pipeNode.curTranslation[axis] < state.translation[axis] then |
691 | pipeNode.curTranslation[axis] = math.min(pipeNode.curTranslation[axis] + dt*pipeNode.translationSpeeds[axis], state.translation[axis]) |
692 | else |
693 | pipeNode.curTranslation[axis] = math.max(pipeNode.curTranslation[axis] - dt*pipeNode.translationSpeeds[axis], state.translation[axis]) |
694 | end |
695 | end |
696 | end |
697 | setTranslation(pipeNode.node, pipeNode.curTranslation[1],pipeNode.curTranslation[2],pipeNode.curTranslation[3]) |
698 | end |
699 | if pipeNode.rotationSpeeds ~= nil then |
700 | local changed = false |
701 | for axis=1, 3 do |
702 | local targetRotation = state.rotation[axis] |
703 | if doAutoAiming then |
704 | if pipeNode.autoAimXRotation and axis == 1 then |
705 | local x, y, z = getWorldTranslation(pipeNode.node) |
706 | |
707 | if VehicleDebug.state == VehicleDebug.DEBUG then |
708 | if pipeNode.subPipeNode == nil then |
709 | drawDebugLine(x, y, z, 1, 0, 0, autoAimX, nodeAutoAimY, autoAimZ, 1, 0, 0) |
710 | else |
711 | local x1, y1, z1 = getWorldTranslation(pipeNode.node) |
712 | local x2, y2, z2 = localToWorld(pipeNode.node, 0, 0, 3) |
713 | drawDebugLine(x1, y1, z1, 1, 1, 0, x2, y2, z2, 1, 1, 0) |
714 | |
715 | DebugUtil.drawDebugGizmoAtWorldPos(x2, y2, z2, 0, 0, 1, 0, 1, 0, string.format("ratio: %.2f", pipeNode.subPipeNodeRatio), false) |
716 | end |
717 | end |
718 | |
719 | local _, lDirY, lDirZ = worldDirectionToLocal(getParent(pipeNode.node), autoAimX-x, nodeAutoAimY-y, autoAimZ-z) |
720 | targetRotation = -math.atan2(lDirY, lDirZ) |
721 | |
722 | if pipeNode.subPipeNode ~= nil then |
723 | targetRotation = targetRotation * pipeNode.subPipeNodeRatio |
724 | end |
725 | |
726 | if pipeNode.autoAimInvertZ then |
727 | targetRotation = targetRotation+math.pi |
728 | end |
729 | targetRotation = MathUtil.normalizeRotationForShortestPath(targetRotation, pipeNode.curRotation[axis]) |
730 | elseif pipeNode.autoAimYRotation and axis == 2 then |
731 | local x, y, z = getWorldTranslation(pipeNode.node) |
732 | local lDirX, _, lDirZ = worldDirectionToLocal(getParent(pipeNode.node), autoAimX-x, nodeAutoAimY-y, autoAimZ-z) |
733 | targetRotation = math.atan2(lDirX, lDirZ) |
734 | if pipeNode.autoAimInvertZ then |
735 | targetRotation = targetRotation+math.pi |
736 | end |
737 | targetRotation = MathUtil.normalizeRotationForShortestPath(targetRotation, pipeNode.curRotation[axis]) |
738 | end |
739 | end |
740 | if pipeNode.minRotationLimits ~= nil and pipeNode.maxRotationLimits ~= nil then |
741 | if math.abs(targetRotation) > (2 * math.pi) then |
742 | targetRotation = targetRotation % (2 * math.pi) |
743 | end |
744 | |
745 | if pipeNode.minRotationLimits[axis] ~= nil then |
746 | targetRotation = math.max(targetRotation, pipeNode.minRotationLimits[axis]) |
747 | end |
748 | if pipeNode.maxRotationLimits[axis] ~= nil then |
749 | targetRotation = math.min(targetRotation, pipeNode.maxRotationLimits[axis]) |
750 | end |
751 | end |
752 | if math.abs(pipeNode.curRotation[axis] - targetRotation) > 0.00001 then |
753 | changed = true |
754 | local rotationAllowed = true |
755 | -- priorities only while folding the pipe, not while auto aiming |
756 | if not doAutoAiming then |
757 | for j=1, #spec.nodes do |
758 | local pipeNodeToCheck = spec.nodes[j] |
759 | if pipeNodeToCheck[priority] > pipeNode[priority] then |
760 | for l=1, 3 do |
761 | if pipeNodeToCheck.curRotation[l] ~= pipeNodeToCheck.lastTargetRotation[l] then |
762 | rotationAllowed = false |
763 | break |
764 | end |
765 | end |
766 | end |
767 | end |
768 | end |
769 | |
770 | if rotationAllowed then |
771 | nodeMoved = true |
772 | if pipeNode.curRotation[axis] < targetRotation then |
773 | pipeNode.curRotation[axis] = math.min(pipeNode.curRotation[axis] + dt*pipeNode.rotationSpeeds[axis], targetRotation) |
774 | else |
775 | pipeNode.curRotation[axis] = math.max(pipeNode.curRotation[axis] - dt*pipeNode.rotationSpeeds[axis], targetRotation) |
776 | end |
777 | if pipeNode.curRotation[axis] > 2*math.pi then |
778 | pipeNode.curRotation[axis] = pipeNode.curRotation[axis] - 2*math.pi |
779 | elseif pipeNode.curRotation[axis] < -2*math.pi then |
780 | pipeNode.curRotation[axis] = pipeNode.curRotation[axis] + 2*math.pi |
781 | end |
782 | |
783 | pipeNode.lastTargetRotation[axis] = targetRotation |
784 | end |
785 | end |
786 | end |
787 | |
788 | if changed then |
789 | setRotation(pipeNode.node, pipeNode.curRotation[1], pipeNode.curRotation[2], pipeNode.curRotation[3]) |
790 | end |
791 | end |
792 | moved = moved or nodeMoved |
793 | |
794 | if nodeMoved and self.setMovingToolDirty ~= nil then |
795 | self:setMovingToolDirty(pipeNode.node) |
796 | end |
797 | end |
798 | |
799 | if #spec.nodes == 0 and spec.animation.name ~= nil then |
800 | if self:getIsAnimationPlaying(spec.animation.name) then |
801 | moved = true |
802 | end |
803 | end |
804 | |
805 | if not moved then |
806 | spec.currentState = spec.targetState |
807 | end |
808 | else |
809 | if self:getDischargeState() == Dischargeable.DISCHARGE_STATE_GROUND then |
810 | local dischargeNode = self:getDischargeNodeByIndex(self:getPipeDischargeNodeIndex()) |
811 | if dischargeNode ~= nil then |
812 | for _,pipeNode in ipairs(spec.nodes) do |
813 | if pipeNode.bendingRegulation > 0 then |
814 | self:updateBendingRegulationNodes(pipeNode, dischargeNode.dischargeDistance) |
815 | end |
816 | end |
817 | end |
818 | end |
819 | end |
820 | end |