LUADOC - Farming Simulator 19

Script v1.7.1.0

Engine v1.7.1.0

Foundation Reference

AIDriveStrategyStraight

Description
Class for a drive strategy just driving straight lines Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.
Parent
AIDriveStrategy
Functions

delete

Description
Definition
delete()
Code
23function AIDriveStrategyStraight:delete()
24 AIDriveStrategyStraight:superClass().delete(self)
25
26 for _, implement in ipairs(self.vehicle:getAttachedAIImplements()) do
27 implement.object:aiImplementEndLine()
28
29 local rootVehicle = implement.object:getRootVehicle()
30 rootVehicle:raiseStateChange(Vehicle.STATE_CHANGE_AI_END_LINE)
31 end
32end

getDriveData

Description
Definition
getDriveData()
Code
152function AIDriveStrategyStraight:getDriveData(dt, vX, vY, vZ)
153
154 -- during turn
155 if self.activeTurnStrategy ~= nil then
156 self.fieldEndGabDetected = false
157 self.fieldEndGabLastPos = {}
158
159 self.lastValidTurnLeftPosition = {0,0,0}
160 self.lastValidTurnLeftValue = 0
161 self.lastValidTurnRightPosition = {0,0,0}
162 self.lastValidTurnRightValue = 0
163
164 self.gabAllowTurnLeft = true
165 self.gabAllowTurnRight = true
166 self.resetGabDetection = true
167
168 self.rowStartTranslation = nil
169
170 local tX, tZ, moveForwards, maxSpeed, distanceToStop = self.activeTurnStrategy:getDriveData(dt, vX,vY,vZ, self.turnData)
171 if tX ~= nil then
172 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
173 self.vehicle:addAIDebugText("===> distanceToStop = "..distanceToStop)
174 end
175 return tX, tZ, moveForwards, maxSpeed, distanceToStop
176 else
177 for _,turnStrategy in pairs(self.turnStrategies) do
178 turnStrategy:onEndTurn(self.activeTurnStrategy.turnLeft)
179 end
180 self.turnLeft = self.activeTurnStrategy.turnLeft
181
182 self.activeTurnStrategy = nil
183 self.idealTurnStrategy = nil
184
185 -- ToDo: decide wether to measure again ?! (yes for plows, no for all other tools?)
186 self.turnDataIsStable = false
187 self.turnDataIsStableCounter = 0
188
189 self.lastLookAheadDistance = 5 -- 30
190 self.foundField = false
191 self.foundNoBetterTurnStrategy = false
192
193 self.lastHasNoField = false
194 end
195 end
196
197 if self.rowStartTranslation == nil then
198 local x, y, z = getWorldTranslation(self.vehicle:getAIVehicleDirectionNode())
199 self.rowStartTranslation = {x, y, z}
200 end
201
202 --
203 local distanceToEndOfField, hasField, ownedField = self:getDistanceToEndOfField(dt, vX,vY,vZ)
204 local attachedAIImplements = self.vehicle:getAttachedAIImplements()
205
206 if hasField and distanceToEndOfField > 0 then
207 self.foundField = true
208 end
209
210 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
211 self.vehicle:addAIDebugText(string.format("==(I)=> distanceToEndOfField: %.1f", distanceToEndOfField))
212 end
213
214 -- this is the case when turn is finished and tool is too far away from field
215 if self.foundField == false and distanceToEndOfField <= 0 and self.turnLeft ~= nil then
216 local _,_,lz = worldToLocal(self.vehicle:getAIVehicleDirectionNode(), self.vehicle.aiDriveTarget[1], 0, self.vehicle.aiDriveTarget[2])
217 if lz > 0 then
218 distanceToEndOfField = self.lookAheadDistanceField
219 self.lastHasNoField = false
220 end
221 end
222
223 if not hasField and self.foundField ~= true and self.turnLeft == nil then
224 if ownedField then
225 self.vehicle:stopAIVehicle(AIVehicle.STOP_REASON_REGULAR)
226 self:debugPrint("Stopping AIVehicle - unable to find field")
227 else
228 self.vehicle:stopAIVehicle(AIVehicle.STOP_REASON_FIELD_NOT_OWNED)
229 self:debugPrint("Stopping AIVehicle - field not owned")
230 end
231
232 return nil
233 end
234
235 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
236 self.vehicle:addAIDebugText(string.format("==(II)=> distanceToEndOfField: %.1f", distanceToEndOfField))
237 self.vehicle:addAIDebugText(string.format("===> foundField: %s", tostring(self.foundField)))
238 self.vehicle:addAIDebugText(string.format("===> lastHasNoField: %s", tostring(self.lastHasNoField)))
239 if self.turnData ~= nil then
240 self.vehicle:addAIDebugText(string.format("===> useExtraStraight: %s %s", tostring(self.turnData.useExtraStraightLeft), tostring(self.turnData.useExtraStraightRight)))
241 end
242 end
243
244 -- if the field border is found in front of us we continue to drive straight until we reach the field border on the side we turn (or on both sides if we have not decided the direction yet)
245 -- this helps us to complete fields with a 45 degress field border to 100%
246 if distanceToEndOfField <= 0 then
247 for _, implement in ipairs(attachedAIImplements) do
248 local leftMarker, rightMarker, _ = implement.object:getAIMarkers()
249 local hasNoFullCoverageArea, _ = implement.object:getAIHasNoFullCoverageArea()
250
251 local allowCheck = self.turnLeft == nil or (self.turnLeft and self.gabAllowTurnRight) or (self.turnLeft == false and self.gabAllowTurnLeft)
252 allowCheck = allowCheck and not hasNoFullCoverageArea
253
254 if allowCheck then
255 local dir = self.turnLeft and -1 or 1
256 local width = calcDistanceFrom(leftMarker, rightMarker)
257 local sX, sZ, wX, wZ, hX, hZ = AIVehicleUtil.getAreaDimensions(self.vehicle.aiDriveDirection[1], self.vehicle.aiDriveDirection[2], leftMarker, rightMarker, dir*width, 0, 1, false)
258 local area, _ = AIVehicleUtil.getAIAreaOfVehicle(implement.object, sX,sZ, wX,wZ, hX,hZ, false)
259 if area > 0 then
260 if not AIVehicleUtil.getIsAreaOwned(self.vehicle, sX, sZ, wX, wZ, hX, hZ) then
261 area = 0
262 end
263 end
264
265 if self.turnLeft == nil then
266 if not self.gabAllowTurnLeft then
267 area = 0
268 end
269
270 if area <= 0 then
271 if self.gabAllowTurnRight then
272 dir = -dir
273 sX, sZ, wX, wZ, hX, hZ = AIVehicleUtil.getAreaDimensions(self.vehicle.aiDriveDirection[1], self.vehicle.aiDriveDirection[2], leftMarker, rightMarker, dir*width, 0, 1, false)
274 area, _ = AIVehicleUtil.getAIAreaOfVehicle(implement.object, sX,sZ, wX,wZ, hX,hZ, false)
275
276 if area > 0 then
277 if not AIVehicleUtil.getIsAreaOwned(self.vehicle, sX, sZ, wX, wZ, hX, hZ) then
278 area = 0
279 end
280 end
281 end
282 end
283 end
284
285 if area > 0 then
286 distanceToEndOfField = 5
287 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
288 self.vehicle:addAIDebugText(string.format("===> continue until field border on left/right (area: %d)", area))
289 end
290 self.driveExtraDistanceToFieldBorder = true
291 end
292 end
293 end
294 else
295 self.driveExtraDistanceToFieldBorder = false
296 end
297
298 local lookAheadDistance = self.lastLookAheadDistance
299
300 local distanceToCollision = 0
301
302 if distanceToEndOfField > 0 and not self.useCorridor then
303 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
304 self.vehicle:addAIDebugText(string.format(" turnDataIsStable: %s | %d", tostring(self.turnDataIsStable), self.turnDataIsStableCounter))
305 end
306 if not self.turnDataIsStable then
307 self:updateTurnData()
308 end
309
310 local searchForTurnStrategy = self.idealTurnStrategy == nil
311
312 if self.idealTurnStrategy ~= nil then
313 distanceToCollision = self.idealTurnStrategy:getDistanceToCollision(dt, vX,vY,vZ, self.turnData, lookAheadDistance)
314 if distanceToCollision < lookAheadDistance then
315 searchForTurnStrategy = true
316 else
317 self.foundNoBetterTurnStrategy = false
318 end
319 end
320
321 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
322 self.vehicle:addAIDebugText(string.format(" searchForTurnStrategy: %s", tostring(searchForTurnStrategy)))
323 end
324
325 if searchForTurnStrategy and self.foundNoBetterTurnStrategy ~= true then
326 for i,turnStrategy in pairs(self.turnStrategies) do
327 if turnStrategy ~= self.idealTurnStrategy then
328 local colDist = turnStrategy:getDistanceToCollision(dt, vX,vY,vZ, self.turnData, lookAheadDistance)
329
330 if colDist >= lookAheadDistance and not turnStrategy.collisionDetected then
331 self.idealTurnStrategy = turnStrategy
332 distanceToCollision = colDist
333 break
334 end
335 end
336 end
337 end
338
339 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
340 self.vehicle:addAIDebugText(string.format("===> distanceToCollision: %.1f", distanceToCollision))
341 end
342
343 if self.idealTurnStrategy ~= nil then
344 -- reset valid position of opposite turn side since there is a reason why not to turn into this direction
345 if self.idealTurnStrategy.turnLeft ~= self.turnLeft then
346 if self.idealTurnStrategy.turnLeft then
347 self.lastValidTurnRightPosition[1] = 0
348 self.lastValidTurnRightPosition[2] = 0
349 self.lastValidTurnRightPosition[3] = 0
350 self.lastValidTurnRightValue = 0
351 else
352 self.lastValidTurnLeftPosition[1] = 0
353 self.lastValidTurnLeftPosition[2] = 0
354 self.lastValidTurnLeftPosition[3] = 0
355 self.lastValidTurnLeftValue = 0
356 end
357 end
358
359 self.turnLeft = self.idealTurnStrategy.turnLeft
360 end
361 end
362
363 local distanceToTurn = math.min(distanceToEndOfField, distanceToCollision)
364 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
365 self.vehicle:addAIDebugText(string.format("===> Distance to turn: %.1f", distanceToTurn))
366 self.vehicle:addAIDebugText(string.format("===> turnLeft: %s", tostring(self.turnLeft)))
367 end
368
369 if distanceToCollision < lookAheadDistance and distanceToCollision < distanceToEndOfField then
370 if self.turnLeft ~= nil or self.idealTurnStrategy == nil then
371 AIVehicleUtil.updateInvertLeftRightMarkers(self.vehicle, self.vehicle)
372 for _,implement in pairs(attachedAIImplements) do
373 AIVehicleUtil.updateInvertLeftRightMarkers(self.vehicle, implement.object)
374 end
375
376 local leftAreaPercentage, rightAreaPercentage = AIVehicleUtil.getValidityOfTurnDirections(self.vehicle, self.turnData)
377
378 if (self.turnLeft and rightAreaPercentage < AIVehicleUtil.VALID_AREA_THRESHOLD) or (not self.turnLeft and leftAreaPercentage < AIVehicleUtil.VALID_AREA_THRESHOLD) or self.idealTurnStrategy == nil then
379 self.foundNoBetterTurnStrategy = false
380
381 local collision = self.turnStrategies[1]:checkCollisionInFront(self.turnData)
382 if collision or distanceToEndOfField <= 0 then
383 self.foundNoBetterTurnStrategy = false
384 self.idealTurnStrategy = nil
385
386 if self.turnLeft == nil then
387 self.vehicle:stopAIVehicle(AIVehicle.STOP_REASON_REGULAR)
388 return nil
389 end
390
391 else
392 distanceToTurn = lookAheadDistance
393 end
394 end
395
396 end
397 end
398
399 -- if we collide to the left or to the right and we still got some area to work in front of us
400 -- we check if there is no collision and still valid ground in the front and continue to work
401 -- as soon as we reach the field end or a collision we will start to reverse and turn in front of the collision
402 if self.allowTurnBackward or self.aiToolReverserDirectionNode ~= nil then
403 if distanceToTurn <= 0 and distanceToEndOfField > 0 then
404 local collision = self.turnStrategies[1]:checkCollisionInFront(self.turnData, 0)
405
406 -- if the vehicle is somehow blocked and can not move (slopes etc.) we cancel the corridor and start to reverse
407 if not collision then
408 if self.vehicle:getLastSpeed() < 1.5 then
409 self.useCorridorTimeOut = self.useCorridorTimeOut + dt
410 else
411 self.useCorridorTimeOut = 0
412 end
413
414 if self.useCorridorTimeOut > 3000 then
415 collision = true
416 end
417 end
418
419 if not collision then
420 distanceToTurn = distanceToEndOfField
421 self.useCorridor = true
422 if self.useCorridorStart == nil then
423 self.useCorridorStart = {vX, vY, vZ}
424 else
425 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
426 local distance = MathUtil.vector3Length(self.useCorridorStart[1]-vX, self.useCorridorStart[2]-vY,self.useCorridorStart[3]-vZ)
427 self.vehicle:addAIDebugText(string.format("===> is using a corridor (%.1fm)", distance))
428 end
429 end
430 else
431 self.useCorridor = false
432 end
433 else
434 self.useCorridor = false
435 end
436 end
437
438 if distanceToTurn <= 0 and not self.useCorridor then
439 -- call end line for all tools that were still 'on field'
440 for _, implement in ipairs(attachedAIImplements) do
441 if implement.aiEndLineCalled == nil or not implement.aiEndLineCalled then
442 implement.aiEndLineCalled = true
443 implement.aiStartLineCalled = nil
444
445 implement.object:aiImplementEndLine()
446
447 local rootVehicle = implement.object:getRootVehicle()
448 rootVehicle:raiseStateChange(Vehicle.STATE_CHANGE_AI_END_LINE)
449 end
450 end
451
452 self.lastHasNoField = false -- reset state for end of field check
453
454 self.activeTurnStrategy = self.idealTurnStrategy
455 if self.turnData ~= nil and self.activeTurnStrategy ~= nil then
456 if self.useCorridorStart ~= nil then
457 local distance = MathUtil.vector3Length(self.useCorridorStart[1]-vX, self.useCorridorStart[2]-vY,self.useCorridorStart[3]-vZ)
458 self.corridorDistance = distance
459 self.useCorridorStart = nil
460 self:debugPrint(string.format("start turn with corridor offset: %.2f", distance))
461 end
462 local canTurn = self.activeTurnStrategy:startTurn(self)
463 self.activeTurnStrategy.lastValidTurnPositionOffset = 0
464 self.corridorDistance = 0
465 if not canTurn then
466 self.vehicle:stopAIVehicle(AIVehicle.STOP_REASON_REGULAR)
467 self:debugPrint("Stopping AIVehicle - could not start to turn")
468
469 return nil
470 end
471 return self.activeTurnStrategy:getDriveData(dt, vX,vY,vZ, self.turnData)
472 else
473 self.vehicle:stopAIVehicle(AIVehicle.STOP_REASON_REGULAR)
474 end
475 return nil
476 else
477 self.vehicle:addAIDebugText("===> Drive straight")
478 return self:getDriveStraightData(dt, vX,vY,vZ, distanceToTurn, distanceToEndOfField)
479 end
480
481end

getDriveStraightData

Description
Definition
getDriveStraightData()
Code
485function AIDriveStrategyStraight:getDriveStraightData(dt, vX, vY, vZ, distanceToTurn, distanceToEndOfField)
486 if self.vehicle.aiDriveDirection == nil then
487 return nil, nil, true, 0, 0
488 end
489
490 -- drive along the desired direction
491 local pX, pZ = MathUtil.projectOnLine(vX, vZ, self.vehicle.aiDriveTarget[1], self.vehicle.aiDriveTarget[2], self.vehicle.aiDriveDirection[1], self.vehicle.aiDriveDirection[2])
492 local tX = pX + self.vehicle.aiDriveDirection[1] * self.vehicle.maxTurningRadius
493 local tZ = pZ + self.vehicle.aiDriveDirection[2] * self.vehicle.maxTurningRadius
494
495 local maxSpeed = self.vehicle:getSpeedLimit()
496 local attachedAIImplements = self.vehicle:getAttachedAIImplements()
497
498
499 -- 1: raise, nil: stay, -1: lower
500 for i=#self.toolLineStates, 1, -1 do
501 self.toolLineStates[i] = nil
502 end
503
504
505 local nrOfImplements = #attachedAIImplements
506 for i=nrOfImplements,1,-1 do
507 local implement = attachedAIImplements[i]
508
509 if self.toolLineStates[i+1] == -1 and attachedAIImplements[i+1].object:getAttacherVehicle() == implement.object then
510 self.toolLineStates[i] = -1
511 else
512 local leftMarker, rightMarker, backMarker = implement.object:getAIMarkers()
513 -- we apply a savety offset here cause on same implements the markers are moved on x axis while lifting the tool
514 -- with this offset we don't check the next row
515 local safetyOffset = 0.2
516
517 local markerZOffset = 0
518 local _, _, areaLength = localToLocal(backMarker, leftMarker, 0,0,0)
519 local size = 1
520 local doAdditionalFieldEndChecks = false
521 local hasNoFullCoverageArea, hasNoFullCoverageAreaOffset = implement.object:getAIHasNoFullCoverageArea()
522 if hasNoFullCoverageArea then
523 markerZOffset = areaLength
524 size = math.abs(markerZOffset) + hasNoFullCoverageAreaOffset
525 doAdditionalFieldEndChecks = true
526 end
527
528 local getAreaDimensions = function(leftNode, rightNode, xOffset, zOffset, areaSize, invertXOffset)
529 local xOffsetLeft, xOffsetRight = xOffset, xOffset
530 if invertXOffset == nil or invertXOffset then
531 xOffsetLeft = -xOffsetLeft
532 end
533 local lX, _, lZ = localToWorld(leftNode, xOffsetLeft, 0, zOffset)
534 local rX, _, rZ = localToWorld(rightNode, xOffsetRight, 0, zOffset)
535
536 local sX = lX - (0.5 * self.vehicle.aiDriveDirection[1])
537 local sZ = lZ - (0.5 * self.vehicle.aiDriveDirection[2])
538 local wX = rX - (0.5 * self.vehicle.aiDriveDirection[1])
539 local wZ = rZ - (0.5 * self.vehicle.aiDriveDirection[2])
540 local hX = lX + (areaSize * self.vehicle.aiDriveDirection[1])
541 local hZ = lZ + (areaSize * self.vehicle.aiDriveDirection[2])
542
543 return sX, sZ, wX, wZ, hX, hZ
544 end
545
546 local sX, sZ, wX, wZ, hX, hZ = getAreaDimensions(leftMarker, rightMarker, safetyOffset, markerZOffset, size)
547
548 local area, totalArea = AIVehicleUtil.getAIAreaOfVehicle(implement.object, sX,sZ, wX,wZ, hX,hZ, false)
549 if area / totalArea > 0.025 then
550 if not self.fieldEndGabDetected then
551 self.toolLineStates[i] = -1
552 end
553
554 -- detect gab at the end of the field to another field
555 -- needed since the plow check area is as long as the plow is and this length can be longer than the distance between fields
556 -- so if the back of the plow is on a field, the center not and the front also on a field we reached the headland
557 if doAdditionalFieldEndChecks then
558 local distance1 = 0
559 local distance2 = 0
560
561 local sX1, sZ1, wX1, wZ1, hX1, hZ1 = getAreaDimensions(leftMarker, rightMarker, safetyOffset, 1, 0.75)
562 local area2, _ = AIVehicleUtil.getAIAreaOfVehicle(implement.object, sX1,sZ1, wX1,wZ1, hX1,hZ1, false)
563 if #self.fieldEndGabLastPos > 0 then
564 distance1 = math.abs(MathUtil.vector2Length(sX1-self.fieldEndGabLastPos[1], sZ1-self.fieldEndGabLastPos[2]))
565 end
566
567 local sX2, sZ2, wX2, wZ2, hX2, hZ2 = getAreaDimensions(leftMarker, rightMarker, safetyOffset, -2, 0.75)
568 local area3, _ = AIVehicleUtil.getAIAreaOfVehicle(implement.object, sX2,sZ2, wX2,wZ2, hX2,hZ2, false)
569 if #self.fieldEndGabLastPos > 0 then
570 distance2 = math.abs(MathUtil.vector2Length(sX2-self.fieldEndGabLastPos[1], sZ2-self.fieldEndGabLastPos[2]))
571 end
572
573 if area3 == 0 and area2 > 0 and distance1 > 0 and distance2 > 0 then
574 if distance1 > 3 then
575 if distance1 > distance2 then
576 self.fieldEndGabDetected = true
577 self.toolLineStates[i] = 1
578 end
579 end
580 end
581
582 if area2 > 0 then
583 self.fieldEndGabLastPos[1] = sX1
584 self.fieldEndGabLastPos[2] = sZ1
585 end
586 end
587
588 -- check the left and right side of the tool for valid ground
589 -- in case we got a "L" field we save the last valid position and reverse to that position if we reach the headland but don't have valid ground
590 if not self.driveExtraDistanceToFieldBorder then
591 local usedAreaLength = math.abs(areaLength)
592 if markerZOffset ~= 0 then
593 usedAreaLength = 0
594 end
595
596 local dir = self.turnLeft and -1 or 1
597 local width = calcDistanceFrom(leftMarker, rightMarker)
598 sX, sZ, wX, wZ, hX, hZ = getAreaDimensions(leftMarker, rightMarker, dir*width, markerZOffset-usedAreaLength, size, false)
599 area, _ = AIVehicleUtil.getAIAreaOfVehicle(implement.object, sX,sZ, wX,wZ, hX,hZ, false)
600 local x, y, z = 0, 0, 0
601
602 if not AIVehicleUtil.getIsAreaOwned(self.vehicle, sX, sZ, wX, wZ, hX, hZ) then
603 area = 0
604 end
605
606 if area > 0 then
607 x, y, z = getWorldTranslation(self.vehicle:getAIVehicleDirectionNode())
608 end
609
610 -- eather check the other side if we found nothing
611 -- or if we don't have a turn direction set yet so we check in both directions to be able to turn in both
612 if area == 0 or self.turnLeft == nil then
613 sX, sZ, wX, wZ, hX, hZ = getAreaDimensions(leftMarker, rightMarker, -dir*width, markerZOffset-usedAreaLength, size, false)
614 local areaOpp, _ = AIVehicleUtil.getAIAreaOfVehicle(implement.object, sX,sZ, wX,wZ, hX,hZ, false)
615
616 if not AIVehicleUtil.getIsAreaOwned(self.vehicle, sX, sZ, wX, wZ, hX, hZ) then
617 areaOpp = 0
618 end
619
620 if areaOpp > 0 then
621 x, y, z = getWorldTranslation(self.vehicle:getAIVehicleDirectionNode())
622 end
623
624 if self.turnLeft ~= nil then
625 area = areaOpp
626 dir = -dir
627 else
628 -- if we don't have a turn direction set yet we check in both directions for valid ground and set the lastValidPosition on both sides
629 if area > 0 then
630 self.lastValidTurnLeftPosition[1], self.lastValidTurnLeftPosition[2], self.lastValidTurnLeftPosition[3] = x, y, z
631 self.lastValidTurnLeftValue = math.max(area, self.lastValidTurnLeftValue)
632 end
633
634 if areaOpp > 0 then
635 self.lastValidTurnRightPosition[1], self.lastValidTurnRightPosition[2], self.lastValidTurnRightPosition[3] = x, y, z
636 self.lastValidTurnRightValue = math.max(area, self.lastValidTurnRightValue)
637 end
638 end
639 end
640
641 if self.turnLeft ~= nil then
642 if area > 0 then
643 if dir > 0 then
644 if area > self.lastValidTurnRightValue then
645 self.lastValidTurnLeftPosition[1], self.lastValidTurnLeftPosition[2], self.lastValidTurnLeftPosition[3] = x, y, z
646 self.lastValidTurnLeftValue = math.max(area, self.lastValidTurnLeftValue)
647 self.lastValidTurnRightPosition[1], self.lastValidTurnRightPosition[2], self.lastValidTurnRightPosition[3] = 0, 0, 0
648 end
649 else
650 if area > self.lastValidTurnLeftValue then
651 self.lastValidTurnRightPosition[1], self.lastValidTurnRightPosition[2], self.lastValidTurnRightPosition[3] = x, y, z
652 self.lastValidTurnRightValue = math.max(area, self.lastValidTurnRightValue)
653 self.lastValidTurnLeftPosition[1], self.lastValidTurnLeftPosition[2], self.lastValidTurnLeftPosition[3] = 0, 0, 0
654 end
655 end
656 end
657 end
658
659 self.lastValidTurnCheckPosition[1], self.lastValidTurnCheckPosition[2], self.lastValidTurnCheckPosition[3] = getWorldTranslation(self.vehicle:getAIVehicleDirectionNode())
660
661 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
662 DebugUtil.drawDebugGizmoAtWorldPos(self.lastValidTurnLeftPosition[1], self.lastValidTurnLeftPosition[2], self.lastValidTurnLeftPosition[3], 0, 1, 0, 0, 1, 0, "last valid left", true)
663 DebugUtil.drawDebugGizmoAtWorldPos(self.lastValidTurnRightPosition[1], self.lastValidTurnRightPosition[2], self.lastValidTurnRightPosition[3], 0, 1, 0, 0, 1, 0, "last valid right", true)
664 end
665 end
666 else
667 -- If we don't need to lower, check if we need to raise it, either not on field or nothing close by (check slightly further ahead than for lowering)
668 if self.lastHasNoField then
669 self.toolLineStates[i] = 1
670 else
671 local lX, _, lZ = localToWorld(leftMarker, -safetyOffset, 0, markerZOffset)
672 local hX2 = lX + (math.max(distanceToTurn, 2.5) * self.vehicle.aiDriveDirection[1])
673 local hZ2 = lZ + (math.max(distanceToTurn, 2.5) * self.vehicle.aiDriveDirection[2])
674
675 local area2, _ = AIVehicleUtil.getAIAreaOfVehicle(implement.object, sX,sZ, wX,wZ, hX2,hZ2, false)
676 if area2 <= 0 then
677 self.toolLineStates[i] = 1
678 end
679 end
680 end
681 end
682 end
683
684 for i=nrOfImplements,1,-1 do
685 local implement = attachedAIImplements[i]
686
687 if implement.aiLastStateChangeDistance == nil then
688 implement.aiLastStateChangeDistance = 0
689 end
690
691 -- allow state changes only after the vehicle has been moved at least 25cm
692 -- this helps us if the area position slightly changes while lowering/lifting
693 -- and the result of the ai area checks is then different -> this can result in an endless lower/lift cycle [Bug #32754]
694 implement.aiLastStateChangeDistance = implement.aiLastStateChangeDistance + implement.object.lastMovedDistance
695 if implement.aiLastStateChangeDistance > 0.25 then
696 if self.toolLineStates[i] == -1 then
697 if implement.aiStartLineCalled == nil or not implement.aiStartLineCalled then
698 implement.aiStartLineCalled = true
699 implement.aiEndLineCalled = nil
700
701 implement.object:aiImplementStartLine()
702 implement.aiLastStateChangeDistance = 0
703
704 local rootVehicle = implement.object:getRootVehicle()
705 rootVehicle:raiseStateChange(Vehicle.STATE_CHANGE_AI_START_LINE)
706 end
707 elseif self.toolLineStates[i] == 1 then
708 if implement.aiEndLineCalled == nil or not implement.aiEndLineCalled then
709 implement.aiEndLineCalled = true
710 implement.aiStartLineCalled = nil
711
712 implement.object:aiImplementEndLine()
713 implement.aiLastStateChangeDistance = 0
714
715 local rootVehicle = implement.object:getRootVehicle()
716 rootVehicle:raiseStateChange(Vehicle.STATE_CHANGE_AI_END_LINE)
717 end
718 end
719 end
720 end
721
722 local canContinueWork = self.vehicle:getCanAIVehicleContinueWork()
723 if not canContinueWork then
724 maxSpeed = 0
725 end
726
727 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
728 self.vehicle:addAIDebugText(string.format("===> canContinueWork: %s", tostring(canContinueWork)))
729 self.vehicle:addAIDebugLine({vX,vY,vZ}, {tX,vY,tZ}, {1,1,1})
730 end
731
732 --detect gabs to other fields
733 self.gabAllowTurnLeft = true
734 self.gabAllowTurnRight = true
735 local gabBits = ""
736 local gabPos = -1
737
738 -- do gab detection only for the tool with the higest working width
739 -- assumes that the tools are aligned well behind the tractor and the widest tool covers the full width
740 local maxWidth = -math.huge
741 local maxWidthVehicle
742 for _, implement in ipairs(attachedAIImplements) do
743 local leftMarker, rightMarker, _ = implement.object:getAIMarkers()
744
745 -- only calculate for tool with the biggest width
746 local width = calcDistanceFrom(leftMarker, rightMarker) + 0.8
747 if width > maxWidth then
748 maxWidth = width
749 maxWidthVehicle = implement.object
750 end
751 end
752
753 if maxWidthVehicle ~= nil then
754 local leftMarker, rightMarker, _ = maxWidthVehicle:getAIMarkers()
755
756 local changeCounter = 0
757 local lastBit = false
758 local gabPosCount = 0
759
760 local divisions = 2.5
761 if maxWidth < 8.5 then
762 divisions = 1.5
763 end
764 if maxWidth < 4.5 then
765 divisions = 1
766 end
767
768 local checkpoints = maxWidthVehicle.aiImplementGabCheckpoints
769 if checkpoints == nil then
770 checkpoints = MathUtil.round(maxWidth / divisions, 0)
771 if checkpoints > 0 then
772 checkpoints = checkpoints + 1
773 end
774
775 maxWidthVehicle.aiImplementGabCheckpoints = checkpoints
776 end
777 if maxWidthVehicle.aiImplementGabCheckpointValues == nil or self.resetGabDetection then
778 maxWidthVehicle.aiImplementGabCheckpointValues = {}
779 self.resetGabDetection = false
780 end
781 local values = maxWidthVehicle.aiImplementGabCheckpointValues
782
783 maxWidthVehicle.aiImplementCurCheckpoint = (maxWidthVehicle.aiImplementCurCheckpoint or -1) + 1
784 if maxWidthVehicle.aiImplementCurCheckpoint >= checkpoints then
785 maxWidthVehicle.aiImplementCurCheckpoint = 0
786 end
787 local currentCheckpoint = maxWidthVehicle.aiImplementCurCheckpoint
788
789 if checkpoints > 2 then
790 local checkpointWidth = maxWidth / (checkpoints - 1)
791
792 local x1, y1, z1 = localToWorld(leftMarker, 0.4, 0, 0)
793 local x2, y2, z2 = localToWorld(rightMarker, -0.4, 0, 0)
794
795 local x = x1 - ((x1 - x2) * ((currentCheckpoint*checkpointWidth) / maxWidth))
796 local y = y1 - ((y1 - y2) * ((currentCheckpoint*checkpointWidth) / maxWidth))
797 local z = z1 - ((z1 - z2) * ((currentCheckpoint*checkpointWidth) / maxWidth))
798
799
800 local isOnField = getDensityAtWorldPos(g_currentMission.terrainDetailId, x, y, z) ~= 0
801 local bit = values[currentCheckpoint+1]
802 bit = bit or isOnField
803
804 values[currentCheckpoint+1] = bit
805
806 for i=1, checkpoints do
807 if values[i] ~= lastBit then
808 changeCounter = changeCounter + 1
809 if changeCounter == 2 then
810 gabPosCount = i/checkpoints
811 elseif changeCounter == 3 then
812 gabPosCount = gabPosCount + (i-1)/checkpoints
813 gabPos = gabPosCount / 2
814 end
815 lastBit = values[i]
816 end
817
818 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
819 gabBits = gabBits .. tostring((values[i] and 1) or 0)
820 end
821 end
822
823 -- block turning on both directions if we found a permanent gab in the field
824 -- this blocked ai working if you start the ai on a gab
825 -- but correctly behaves if you reach the field border on the other side of the field
826 local hasGab = gabPos > 0
827 self.gabAllowTurnLeft = self.gabAllowTurnLeft and values[1] and not hasGab
828 self.gabAllowTurnRight = self.gabAllowTurnRight and values[#values] and not hasGab
829 end
830 end
831
832 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
833 self.vehicle:addAIDebugText(string.format("===> gab bits: %s (%s)", gabBits, Utils.getFilenameInfo(maxWidthVehicle.configFileName, true)))
834 if gabPos > 0 then
835 self.vehicle:addAIDebugText(string.format("===> gab pos: %.2f%% side: %s", gabPos*100, (gabPos < 0.5 and "left") or "right"))
836 end
837 self.vehicle:addAIDebugText(string.format("===> gab allow Left: %s", self.gabAllowTurnLeft))
838 self.vehicle:addAIDebugText(string.format("===> gab allow right: %s", self.gabAllowTurnRight))
839 end
840
841 return tX, tZ, true, maxSpeed, distanceToTurn
842end

new

Description
Definition
new()
Code
11function AIDriveStrategyStraight:new(customMt)
12 if customMt == nil then
13 customMt = AIDriveStrategyStraight_mt
14 end
15
16 local self = AIDriveStrategy:new(customMt)
17
18 return self
19end

setAIVehicle

Description
Definition
setAIVehicle()
Code
36function AIDriveStrategyStraight:setAIVehicle(vehicle)
37 AIDriveStrategyStraight:superClass().setAIVehicle(self, vehicle)
38
39 --# set AI direction acccording to current orientation
40 local dx,_,dz = localDirectionToWorld(self.vehicle:getAIVehicleDirectionNode(), 0, 0, 1)
41 if g_currentMission.snapAIDirection then
42 local snapAngle = self.vehicle:getDirectionSnapAngle()
43 snapAngle = math.max(snapAngle, math.pi/(g_currentMission.terrainDetailAngleMaxValue+1))
44
45 local angleRad = MathUtil.getYRotationFromDirection(dx, dz)
46 angleRad = math.floor(angleRad / snapAngle + 0.5) * snapAngle
47
48 dx, dz = MathUtil.getDirectionFromYRotation(angleRad)
49 else
50 local length = MathUtil.vector2Length(dx,dz)
51 dx = dx / length
52 dz = dz / length
53 end
54 self.vehicle.aiDriveDirection = {dx, dz}
55
56 local x,_,z = getWorldTranslation(self.vehicle:getAIVehicleDirectionNode())
57 self.vehicle.aiDriveTarget = {x, z}
58
59 --# create turn strategy
60 local useDefault = true
61
62 -- check if all attached implements allow turning backward
63 self.allowTurnBackward = AIVehicleUtil.getAttachedImplementsAllowTurnBackward(vehicle)
64
65 if not self.allowTurnBackward then
66 useDefault = false
67 end
68
69 self.aiToolReverserDirectionNode = AIVehicleUtil.getAIToolReverserDirectionNode(self.vehicle)
70 self.vehicleAIReverserNode = self.vehicle:getAIVehicleReverserNode()
71
72 self.turnStrategies = {}
73
74 local usedStrategies = ''
75
76 if useDefault then
77 usedStrategies = usedStrategies .. " +DEFAULT "
78 table.insert(self.turnStrategies, AITurnStrategyDefault:new())
79
80 usedStrategies = usedStrategies .. " +DEFAULT (reverse)"
81 table.insert(self.turnStrategies, AITurnStrategyDefaultReverse:new())
82 end
83
84 if self.aiToolReverserDirectionNode ~= nil or useDefault or self.vehicleAIReverserNode then
85 usedStrategies = usedStrategies .. " +BULBs (reverse)"
86 table.insert(self.turnStrategies, AITurnStrategyBulb1Reverse:new())
87 table.insert(self.turnStrategies, AITurnStrategyBulb2Reverse:new())
88 table.insert(self.turnStrategies, AITurnStrategyBulb3Reverse:new())
89 else
90 usedStrategies = usedStrategies .. " +BULBs"
91 table.insert(self.turnStrategies, AITurnStrategyBulb1:new())
92 table.insert(self.turnStrategies, AITurnStrategyBulb2:new())
93 table.insert(self.turnStrategies, AITurnStrategyBulb3:new())
94 end
95
96 if self.aiToolReverserDirectionNode ~= nil or useDefault or self.vehicleAIReverserNode then
97 usedStrategies = usedStrategies .. " +HALFCIRCLE (reverse)"
98 table.insert(self.turnStrategies, AITurnStrategyHalfCircleReverse:new())
99 else
100 usedStrategies = usedStrategies .. " +HALFCIRCLE"
101 table.insert(self.turnStrategies, AITurnStrategyHalfCircle:new())
102 end
103
104 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
105 print("AI is using strategies: "..usedStrategies.." for "..tostring(self.vehicle.configFileName))
106 end
107
108 for _,turnStrategy in pairs(self.turnStrategies) do
109 turnStrategy:setAIVehicle(self.vehicle, self)
110 end
111
112 self.activeTurnStrategy = nil
113
114 self.turnDataIsStable = false
115 self.turnDataIsStableCounter = 0
116
117 self.fieldEndGabDetected = false
118 self.fieldEndGabLastPos = {}
119 self.gabAllowTurnLeft = true
120 self.gabAllowTurnRight = true
121 self.resetGabDetection = true
122
123 self.lastValidTurnLeftPosition = {0,0,0}
124 self.lastValidTurnLeftValue = 0
125 self.lastValidTurnRightPosition = {0,0,0}
126 self.lastValidTurnRightValue = 0
127 self.lastValidTurnCheckPosition = {0,0,0}
128
129 self.useCorridor = false
130 self.useCorridorStart = nil
131 self.useCorridorTimeOut = 0
132
133 self.rowStartTranslation = nil
134
135 self.lastLookAheadDistance = 5
136
137 self.driveExtraDistanceToFieldBorder = false
138
139 self.toolLineStates = {}
140end

update

Description
Definition
update()
Code
144function AIDriveStrategyStraight:update(dt)
145 for _,strategy in pairs(self.turnStrategies) do
146 strategy:update(dt)
147 end
148end

updateTurnData

Description
Definition
updateTurnData()
Code
847function AIDriveStrategyStraight:updateTurnData()
848 -- calculate basic data, which is needed by turn strategies
849 self.turnData = Utils.getNoNil(self.turnData, {})
850
851 local attachedAIImplements = self.vehicle:getAttachedAIImplements()
852
853 -- determine turning radius
854 self.turnData.radius = self.vehicle.maxTurningRadius * 1.1 -- needs ackermann steering
855 if self.vehicle:getAIMinTurningRadius() ~= nil then
856 self.turnData.radius = math.max(self.turnData.radius, self.vehicle:getAIMinTurningRadius())
857 end
858
859 local maxToolRadius = 0
860 for _,implement in pairs(attachedAIImplements) do
861 maxToolRadius = math.max(maxToolRadius, AIVehicleUtil.getMaxToolRadius(implement))
862 end
863 self.turnData.radius = math.max(self.turnData.radius, maxToolRadius)
864
865 -- determine tool with smallest AI area and zOffsets for estimation of waypoints/segments
866 local minWidthOfAIArea = math.huge
867 self.turnData.maxZOffset = -math.huge
868 self.turnData.minZOffset = math.huge
869 self.turnData.aiAreaMaxX = -math.huge
870 self.turnData.aiAreaMinX = math.huge
871
872 local lastTypeName = nil
873 local allImplementsOfSameType = true
874
875 for _,implement in pairs(attachedAIImplements) do
876 if lastTypeName == nil then
877 lastTypeName = implement.object.typeName
878 end
879
880 allImplementsOfSameType = allImplementsOfSameType and lastTypeName == implement.object.typeName
881
882 local leftMarker, rightMarker, backMarker = implement.object:getAIMarkers()
883
884 local xL,_,zL = localToLocal(leftMarker, self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
885 local xR,_,zR = localToLocal(rightMarker, self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
886 local xB,_,zB = localToLocal(backMarker, self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
887
888 local lrDistance = math.abs(xL - xR)
889 if lrDistance < minWidthOfAIArea then
890 minWidthOfAIArea = lrDistance
891 self.turnData.minAreaImplement = implement
892 end
893
894 self.turnData.aiAreaMinX = math.min(self.turnData.aiAreaMinX, xL, xR, xB)
895 self.turnData.aiAreaMaxX = math.max(self.turnData.aiAreaMaxX, xL, xR, xB)
896
897 self.turnData.maxZOffset = math.max(self.turnData.maxZOffset, math.max(zL, zR))
898 self.turnData.minZOffset = math.min(self.turnData.minZOffset, math.min(zL, zR))
899 end
900
901 self.turnData.allImplementsOfSameType = allImplementsOfSameType
902
903 if self.turnData.maxZOffset == self.turnData.minZOffset then
904 self.turnData.zOffset = 2 * self.turnData.maxZOffset
905 self.turnData.zOffsetTurn = math.max(1, 2 * self.turnData.maxZOffset)
906 else
907 if self.turnData.maxZOffset > 0 and self.turnData.minZOffset < 0 then
908 self.turnData.zOffset = self.turnData.minZOffset + self.turnData.maxZOffset
909 self.turnData.zOffsetTurn = math.max(1, self.turnData.minZOffset + self.turnData.maxZOffset)
910 elseif self.turnData.maxZOffset > 0 and self.turnData.minZOffset > 0 then
911 self.turnData.zOffset = 2 * self.turnData.maxZOffset
912 self.turnData.zOffsetTurn = math.max(1, 2 * self.turnData.maxZOffset)
913 elseif self.turnData.maxZOffset < 0 and self.turnData.minZOffset < 0 then
914 self.turnData.zOffset = self.turnData.minZOffset + self.turnData.maxZOffset
915 self.turnData.zOffsetTurn = math.max(1, self.turnData.minZOffset + self.turnData.maxZOffset)
916 end
917 end
918
919 local minLeftMarker, minRightMarker, _ = self.turnData.minAreaImplement.object:getAIMarkers()
920 self.turnData.sideOffsetLeft = localToLocal(minLeftMarker, self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
921 self.turnData.sideOffsetRight = localToLocal(minRightMarker, self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
922
923 if allImplementsOfSameType then
924 self.turnData.sideOffsetLeft = self.turnData.aiAreaMaxX
925 self.turnData.sideOffsetRight = self.turnData.aiAreaMinX
926 end
927
928 -- shrink area size to force overlap (0.26m)
929 local overlapPerSide = AIVehicleUtil.AREA_OVERLAP / 2
930 self.turnData.sideOffsetLeft = self.turnData.sideOffsetLeft - overlapPerSide
931 self.turnData.sideOffsetRight = self.turnData.sideOffsetRight + overlapPerSide
932
933 -- turn radius should be always higher than the side offset (otherwise some calculation in the strategies won't work)
934 self.turnData.radius = math.max(self.turnData.radius, self.turnData.sideOffsetLeft, -self.turnData.sideOffsetRight)
935
936 -- check if we need to invert the side offset (happens on plows that are rotating)
937 if self.turnLeft ~= nil then
938 local canInvertMarkerOnTurn = false
939 for _, implement in pairs(attachedAIImplements) do
940 canInvertMarkerOnTurn = canInvertMarkerOnTurn or implement.object:getAIInvertMarkersOnTurn(self.turnLeft)
941 end
942
943 if canInvertMarkerOnTurn then
944 local offset = math.abs(self.turnData.sideOffsetLeft - self.turnData.sideOffsetRight)/2
945 self.turnData.sideOffsetLeft = offset
946 self.turnData.sideOffsetRight = -offset
947 end
948 end
949
950 self.turnData.useExtraStraightLeft = self.turnData.sideOffsetLeft > self.turnData.radius
951 self.turnData.useExtraStraightRight = self.turnData.sideOffsetRight < -self.turnData.radius
952
953 self.turnData.toolOverhang = {}
954 self.turnData.toolOverhang['front'] = {}
955 self.turnData.toolOverhang['back'] = {}
956
957
958 self.turnData.allToolsAtFront = true
959
960 -- ToDo: ? improve this fallback estimation of overhang ...
961 local xt = self.vehicle.sizeWidth * 0.5
962 local zt = self.vehicle.sizeLength * 0.75
963 local alphaX = math.atan(-zt /(xt + self.turnData.radius))
964 local alphaZ = math.atan((xt + self.turnData.radius) / zt)
965 local xb = math.cos(alphaX)*xt - math.sin(alphaX)*zt + math.cos(alphaX)*self.turnData.radius
966 local zb = math.sin(alphaZ)*xt + math.cos(alphaZ)*zt + math.sin(alphaZ)*self.turnData.radius
967 for _,side in pairs({'front', 'back'}) do
968 self.turnData.toolOverhang[side].xt = xt
969 self.turnData.toolOverhang[side].zt = zt
970 self.turnData.toolOverhang[side].xb = xb
971 self.turnData.toolOverhang[side].zb = zb
972 end
973
974 -- overhang for tools in hydraulic
975 for _,implement in pairs(attachedAIImplements) do
976 if implement.object:getAIAllowTurnBackward() then
977 local leftMarker, rightMarker, backMarker = implement.object:getAIMarkers()
978 local leftSizeMarker, rightSizeMarker, backSizeMarker = implement.object:getAISizeMarkers()
979
980 local xL,_,zL = localToLocal( Utils.getNoNil(leftSizeMarker, leftMarker), self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
981 local xR,_,zR = localToLocal( Utils.getNoNil(rightSizeMarker, rightMarker), self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
982 local xB,_,zB = localToLocal( Utils.getNoNil(backSizeMarker, backMarker), self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
983
984 self.turnData.allToolsAtFront = self.turnData.allToolsAtFront and zB > 0
985
986 local xt = math.max(math.abs(xL), math.abs(xR), math.abs(xB))
987 local zt = math.max(math.abs(zL), math.abs(zR), math.abs(zB))
988
989 --local alphaX = math.atan(-zt /(xt + self.turnData.radius))
990 --local alphaZ = math.atan((xt + self.turnData.radius) / zt)
991 --
992 --local xb = math.cos(alphaX)*xt - math.sin(alphaX)*zt + math.cos(alphaX)*self.turnData.radius
993 --local zb = math.sin(alphaZ)*xt + math.cos(alphaZ)*zt + math.sin(alphaZ)*self.turnData.radius
994 --
995 ----print(" --------------------------------------------------- ")
996 ----print( string.format(" OLD :: alphaX=%.2f alphaZ=%.2f ", math.deg(alphaX), math.deg(alphaZ)) )
997 ----print( string.format(" -> xb /zb = %.2f / %.2f ", xb, zb) )
998 ----
999 ----
1000 ----local alphaX = math.atan( -(self.turnData.radius + xt) / zt )
1001 ----local alphaZ = math.atan( xt / (self.turnData.radius + zt) )
1002 ----
1003 ----local xb = math.cos(alphaX)*xt - math.sin(alphaX)*zt + math.cos(alphaX)*self.turnData.radius
1004 ----local zb = math.sin(alphaZ)*xt + math.cos(alphaZ)*zt + math.sin(alphaZ)*self.turnData.radius
1005 ----
1006 ----print( string.format(" NEW :: alphaX=%.2f alphaZ=%.2f ", math.deg(alphaX), math.deg(alphaZ)) )
1007 ----print( string.format(" -> xb /zb = %.2f / %.2f ", xb, zb) )
1008 ----print( string.format(" -> xt /zt = %.2f / %.2f ", xt, zt) )
1009 ----
1010 ----print( string.format(" -> c = %.2f / r = %.2f / c+r = %.2f", math.sqrt(xt*xt + zt*zt), self.turnData.radius, math.sqrt(xt*xt + zt*zt)+self.turnData.radius) )
1011
1012 -- dismiss all the math, just take the worst case
1013 local xb = math.sqrt(xt*xt + zt*zt) + self.turnData.radius
1014 local zb = math.sqrt(xt*xt + zt*zt) + self.turnData.radius
1015
1016
1017 local side = 'back'
1018 if zB > 0 then
1019 side = 'front'
1020 end
1021
1022 self.turnData.toolOverhang[side].xb = math.max(xb, self.turnData.toolOverhang[side].xb)
1023 self.turnData.toolOverhang[side].zb = math.max(zb, self.turnData.toolOverhang[side].zb)
1024 self.turnData.toolOverhang[side].xt = math.max(xt, self.turnData.toolOverhang[side].xt)
1025 self.turnData.toolOverhang[side].zt = math.max(zt, self.turnData.toolOverhang[side].zt)
1026 end
1027 end
1028
1029 -- overhang for trailer implements
1030 local rotTime = 1/self.vehicle.wheelSteeringDuration * math.atan(1/self.turnData.radius) / math.atan(1/self.vehicle.maxTurningRadius)
1031 local angle
1032 if rotTime >= 0 then
1033 angle = (rotTime / self.vehicle.maxRotTime) * self.vehicle.maxRotation
1034 else
1035 angle = (rotTime / self.vehicle.minRotTime) * self.vehicle.maxRotation
1036 end
1037
1038 for _,implement in pairs(attachedAIImplements) do
1039 if not implement.object:getAIAllowTurnBackward() then
1040 local leftMarker, rightMarker, backMarker = implement.object:getAIMarkers()
1041 local leftSizeMarker, rightSizeMarker, backSizeMarker = implement.object:getAISizeMarkers()
1042
1043 local lX,_,lZ = localToLocal(Utils.getNoNil(leftSizeMarker, leftMarker), implement.object.components[1].node, 0,0,0)
1044 local rX,_,rZ = localToLocal(Utils.getNoNil(rightSizeMarker, rightMarker), implement.object.components[1].node, 0,0,0)
1045 local bX,_,bZ = localToLocal(Utils.getNoNil(backSizeMarker, backMarker), implement.object.components[1].node, 0,0,0)
1046
1047 local nX = math.max( math.abs(lX), math.abs(rX), math.abs(bX) )
1048 local nZ = math.min( -math.abs(lZ), -math.abs(rZ), -math.abs(bZ) )
1049
1050 if implement.object.getActiveInputAttacherJoint then
1051 local inputAttacherJoint = implement.object:getActiveInputAttacherJoint()
1052
1053 local xAtt,_,zAtt = localToLocal(implement.object.components[1].node, inputAttacherJoint.node, nX,0,nZ)
1054 xAtt, zAtt = zAtt, -xAtt
1055
1056 local xRot = xAtt*math.cos(-angle) - zAtt*math.sin(-angle)
1057 local zRot = xAtt*math.sin(-angle) + zAtt*math.cos(-angle)
1058
1059 local xFin,_,_ = localToLocal(implement.object.components[1].node, self.vehicle:getAIVehicleDirectionNode(), xRot,0,zRot)
1060
1061 xFin = xFin + self.turnData.radius
1062
1063 self.turnData.toolOverhang.back.xb = math.max(self.turnData.toolOverhang.back.xb, xFin)
1064
1065 local xL,_,_ = localToLocal( Utils.getNoNil(leftSizeMarker, leftMarker), self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
1066 local xR,_,_ = localToLocal( Utils.getNoNil(rightSizeMarker, rightMarker), self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
1067 local _,_,zB = localToLocal( Utils.getNoNil(backSizeMarker, backMarker), self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
1068 self.turnData.toolOverhang.back.xt = math.max(self.turnData.toolOverhang.back.xt, math.max(math.abs(xL), math.abs(xR)))
1069 self.turnData.toolOverhang.back.zt = math.max(self.turnData.toolOverhang.back.zt, -zB)
1070
1071 local _, rotationJoint, wheels = implement.object:getAITurnRadiusLimitation()
1072
1073
1074 local angleSteer = 0
1075 if rotationJoint ~= nil then
1076 for _, wheel in pairs(wheels) do
1077 if wheel.steeringAxleScale ~= 0 and wheel.steeringAxleRotMax ~= 0 then
1078 angleSteer = math.max(angleSteer, math.abs(wheel.steeringAxleRotMax))
1079 end
1080 end
1081 end
1082
1083 if angleSteer ~= 0 and rotationJoint ~= nil then
1084 local wheelIndexCount = #wheels
1085 if rotationJoint ~= nil and wheelIndexCount > 0 then
1086 local cx,cz = 0,0
1087 for _, wheel in pairs(wheels) do
1088 local x,_,z = localToLocal(wheel.repr, implement.object.components[1].node, 0,0,0)
1089 cx = cx + x
1090 cz = cz + z
1091 end
1092
1093 cx = cx / wheelIndexCount
1094 cz = cz / wheelIndexCount
1095
1096 local dx = nX - cx
1097 local dz = nZ - cz
1098
1099 local delta = math.sqrt(dx*dx + dz*dz)
1100 local xFin = delta + self.turnData.radius
1101
1102 self.turnData.toolOverhang.back.xb = math.max(self.turnData.toolOverhang.back.xb, xFin)
1103 end
1104 end
1105 end
1106 end
1107 end
1108
1109 for _,implement in pairs(attachedAIImplements) do
1110 local leftMarker, _, _ = implement.object:getAIMarkers()
1111 local _,_,z = localToLocal(leftMarker, self.vehicle:getAIVehicleDirectionNode(), 0,0,0)
1112 implement.distToVehicle = z
1113 end
1114
1115 local sortImplementsByDistance = function(arg1, arg2)
1116 return arg1.distToVehicle > arg2.distToVehicle
1117 end
1118
1119 table.sort(attachedAIImplements, sortImplementsByDistance)
1120
1121 -- check if turn data is stable
1122 if self.lastTurnData == nil then
1123 self.lastTurnData = {}
1124 self.lastTurnData.radius = self.turnData.radius
1125 self.lastTurnData.maxZOffset = self.turnData.maxZOffset
1126 self.lastTurnData.minZOffset = self.turnData.minZOffset
1127 self.lastTurnData.aiAreaMaxX = self.turnData.aiAreaMaxX
1128 self.lastTurnData.aiAreaMinX = self.turnData.aiAreaMinX
1129 self.lastTurnData.sideOffsetLeft = self.turnData.sideOffsetLeft
1130 self.lastTurnData.sideOffsetRight = self.turnData.sideOffsetRight
1131 else
1132 if self.vehicle:getLastSpeed() > 2 and
1133 math.abs(self.lastTurnData.radius - self.turnData.radius) < 0.03 and
1134 math.abs(self.lastTurnData.maxZOffset - self.turnData.maxZOffset) < 0.03 and
1135 math.abs(self.lastTurnData.minZOffset - self.turnData.minZOffset) < 0.03 and
1136 math.abs(self.lastTurnData.aiAreaMaxX - self.turnData.aiAreaMaxX) < 0.03 and
1137 math.abs(self.lastTurnData.aiAreaMinX - self.turnData.aiAreaMinX) < 0.03 and
1138 math.abs(self.lastTurnData.sideOffsetLeft - self.turnData.sideOffsetLeft) < 0.03 and
1139 math.abs(self.lastTurnData.sideOffsetRight - self.turnData.sideOffsetRight) < 0.03
1140 then
1141 self.turnDataIsStableCounter = self.turnDataIsStableCounter + 1
1142 if self.turnDataIsStableCounter > 120 then
1143 self.turnDataIsStable = true
1144 end
1145 else
1146 self.lastTurnData.radius = self.turnData.radius
1147 self.lastTurnData.maxZOffset = self.turnData.maxZOffset
1148 self.lastTurnData.minZOffset = self.turnData.minZOffset
1149 self.lastTurnData.aiAreaMaxX = self.turnData.aiAreaMaxX
1150 self.lastTurnData.aiAreaMinX = self.turnData.aiAreaMinX
1151 self.lastTurnData.sideOffsetLeft = self.turnData.sideOffsetLeft
1152 self.lastTurnData.sideOffsetRight = self.turnData.sideOffsetRight
1153 self.turnDataIsStableCounter = 0
1154 end
1155 end
1156end