LUADOC - Farming Simulator 19

Script v1.7.1.0

Engine v1.7.1.0

Foundation Reference

AITurnStrategy

Description
Base class for a turn strategy Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.
Functions

addNoFullCoverageSegment

Description
Definition
addNoFullCoverageSegment()
Code
1144function AITurnStrategy:addNoFullCoverageSegment(turnSegments)
1145 local offset = self:getNoFullCoverageZOffset()
1146 if offset ~= 0 then
1147 local segment = {}
1148 segment.isCurve = false
1149 segment.moveForward = false
1150 segment.slowDown = true
1151 segment.startPoint = self:getVehicleToWorld(0, 0, -offset, true)
1152 segment.endPoint = self:getVehicleToWorld(0, 0, 0, true)
1153 table.insert(turnSegments, segment)
1154 end
1155end

adjustHeightOfTurningSizeBox

Description
Definition
adjustHeightOfTurningSizeBox()
Code
1090function AITurnStrategy:adjustHeightOfTurningSizeBox(box)
1091 local yMax, yMin = -math.huge, math.huge
1092 for i=1, self.numHeightChecks do
1093 local check = self.heightChecks[i]
1094 local x, _, z = localToWorld(self.vehicleDirectionNode, box.center[1] + box.size[1] * check[1], 0, box.center[3] + box.size[3] * check[2])
1095 local h = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z)
1096 yMax = math.max(yMax, h)
1097 yMin = math.min(yMin, h)
1098 end
1099
1100 local height = math.max(6, (yMax - yMin) + 2)
1101
1102 box.size[2] = height
1103 box.center[2] = 0
1104end

checkCollisionInFront

Description
Definition
checkCollisionInFront()
Code
1067function AITurnStrategy:checkCollisionInFront(turnData, lookAheadDistance)
1068 lookAheadDistance = lookAheadDistance or 5
1069
1070 local maxX = turnData.sideOffsetLeft
1071 local minX = turnData.sideOffsetRight
1072 local maxZ = math.max(4, turnData.toolOverhang.front.zt)
1073
1074 local box = {name="checkCollisionInFront"}
1075 box.center = {maxX - (maxX-minX)/2, 0, maxZ/2 + lookAheadDistance/2}
1076 box.rotation = {0,0,0}
1077 box.size = {(maxX-minX)/2, 5, maxZ/2 + lookAheadDistance/2}
1078
1079 self.collisionHit = self:getIsBoxColliding(box)
1080
1081 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
1082 table.insert(self.maxTurningSizeBoxes, box)
1083 end
1084
1085 return self.collisionHit
1086end

collisionTestCallback

Description
Definition
collisionTestCallback()
Code
989function AITurnStrategy:collisionTestCallback(transformId)
990 -- ai should not collide with vehicles and objects, only with the dynamic traffic
991 local object = g_currentMission:getNodeObject(transformId)
992 if object == nil or object:isa(Placeable) then
993 self.collisionHit = true
994 return false
995 end
996end

createTurningSizeBox

Description
Definition
createTurningSizeBox()
Code
465function AITurnStrategy:createTurningSizeBox()
466 local box = {name=self.strategyName}
467 box.center = {0, 0, 0}
468 box.rotation = {0, 0, 0}
469 box.size = {0, 0, 0}
470
471 return box
472end

debugPrint

Description
Definition
debugPrint()
Code
1159function AITurnStrategy:debugPrint(text)
1160 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
1161 print(string.format("DEBUG: %s", text))
1162 end
1163end

delete

Description
Definition
delete()
Code
60function AITurnStrategy:delete()
61end

drawTurnSegments

Description
Definition
drawTurnSegments()
Code
948function AITurnStrategy.drawTurnSegments(segments)
949 for i,segment in pairs(segments) do
950 if segment.isCurve == true then
951 local oX,oY,oZ = localToWorld(segment.o, 0,2,0)
952 local xX,xY,xZ = localToWorld(segment.o, 2,2,0)
953 local yX,yY,yZ = localToWorld(segment.o, 0,4,0)
954 local zX,zY,zZ = localToWorld(segment.o, 0,2,2)
955
956 drawDebugLine(oX,oY,oZ, 1,0,0, xX,xY,xZ, 1,0,0)
957 drawDebugLine(oX,oY,oZ, 0,1,0, yX,yY,yZ, 0,1,0)
958 drawDebugLine(oX,oY,oZ, 0,0,1, zX,zY,zZ, 0,0,1)
959
960 Utils.renderTextAtWorldPosition(yX,yY,yZ, tostring(i), 0.02, 0)
961
962 local ts = 20
963 for i=0,ts-1 do
964 local x1 = segment.radius * math.cos(segment.startAngle + i*(segment.endAngle-segment.startAngle)/ts)
965 local z1 = segment.radius * math.sin(segment.startAngle + i*(segment.endAngle-segment.startAngle)/ts)
966 local x2 = segment.radius * math.cos(segment.startAngle + (i+1)*(segment.endAngle-segment.startAngle)/ts)
967 local z2 = segment.radius * math.sin(segment.startAngle + (i+1)*(segment.endAngle-segment.startAngle)/ts)
968 local w1X,w1Y,w1Z = localToWorld(segment.o, x1,0,z1)
969 local w2X,w2Y,w2Z = localToWorld(segment.o, x2,0,z2)
970 local w1Y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, w1X,w1Y,w1Z) + 1
971 local w2Y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, w2X,w2Y,w2Z) + 1
972 drawDebugLine(w1X,w1Y,w1Z, (ts-i)/ts,i/ts,0, w2X,w2Y,w2Z, (ts-i-1)/ts,(i+1)/ts,0)
973 end
974 else
975 local sY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, unpack(segment.startPoint)) + 1
976 local eY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, unpack(segment.endPoint)) + 1
977 drawDebugLine(segment.startPoint[1],sY,segment.startPoint[3], 1,0,0, segment.endPoint[1],eY,segment.endPoint[3], 0,1,0)
978
979 drawDebugLine(segment.startPoint[1],sY,segment.startPoint[3], 1,1,1, segment.startPoint[1],sY+2,segment.startPoint[3], 1,1,1)
980 drawDebugLine(segment.endPoint[1],sY,segment.endPoint[3], 1,1,1, segment.endPoint[1],sY+2,segment.endPoint[3], 1,1,1)
981
982 Utils.renderTextAtWorldPosition((segment.startPoint[1]+segment.endPoint[1])/2, (sY+eY)/2, (segment.startPoint[3]+segment.endPoint[3])/2, tostring(i), 0.02, 0)
983 end
984 end
985end

evaluateCollisionHits

Description
Definition
evaluateCollisionHits()
Code
1000function AITurnStrategy:evaluateCollisionHits(vX,vY,vZ, collisionHitLeft, collisionHitRight, turnData)
1001
1002 -- Remove the collision flag, as soon as one of the collisions is free again
1003 if not collisionHitLeft and self.collisionEndPosLeft ~= nil then
1004 local _,_,z = worldToLocal(self.vehicleDirectionNode, self.collisionEndPosLeft[1],self.collisionEndPosLeft[2],self.collisionEndPosLeft[3])
1005 if z < -1 then
1006 self.collisionEndPosLeft = nil
1007 self.collisionDetected = false
1008 self.collisionDetectedPosX = nil
1009 end
1010 end
1011 if not collisionHitRight and self.collisionEndPosRight ~= nil then
1012 local _,_,z = worldToLocal(self.vehicleDirectionNode, self.collisionEndPosRight[1],self.collisionEndPosRight[2],self.collisionEndPosRight[3])
1013 if z < -1 then
1014 self.collisionEndPosRight = nil
1015 self.collisionDetected = false
1016 self.collisionDetectedPosX = nil
1017 end
1018 end
1019
1020 -- self.turnLeft always refers to last direction, it is inverted during/before start of next turn
1021 if self.turnLeft == nil then
1022 if collisionHitLeft or collisionHitRight then
1023
1024 local allowLeft = self.usesExtraStraight == turnData.useExtraStraightLeft
1025 local allowRight = self.usesExtraStraight == turnData.useExtraStraightRight
1026
1027 if collisionHitLeft and collisionHitRight then
1028 self.collisionDetected = true
1029 else
1030 local leftAreaPercentage, rightAreaPercentage = AIVehicleUtil.getValidityOfTurnDirections(self.vehicle, turnData)
1031 allowRight = allowRight and rightAreaPercentage >= leftAreaPercentage
1032 allowLeft = allowLeft and leftAreaPercentage >= rightAreaPercentage
1033
1034 -- if we hit a collision on one side we check if we can turn to the other side
1035 -- if the other side is also blocked (e.g. cause of there is no valid ground to work) we start to turn
1036 -- if the other side is free we use it as next turn direction
1037 if collisionHitLeft and not allowRight then
1038 self.collisionDetected = true
1039 self.turnLeft = false
1040 elseif collisionHitRight and not allowLeft then
1041 self.collisionDetected = true
1042 self.turnLeft = true
1043 else
1044 self.turnLeft = collisionHitLeft
1045 end
1046 end
1047 end
1048 else
1049 if self.turnLeft then
1050 if collisionHitRight then
1051 self.collisionDetected = true
1052 end
1053 else
1054 if collisionHitLeft then
1055 self.collisionDetected = true
1056 end
1057 end
1058 end
1059
1060 if self.collisionDetected and self.collisionDetectedPosX == nil then
1061 self.collisionDetectedPosX, self.collisionDetectedPosY, self.collisionDetectedPosZ = vX,vY,vZ
1062 end
1063end

getAngleInSegment

Description
Definition
getAngleInSegment()
Code
937function AITurnStrategy.getAngleInSegment(node, segment, pos)
938 if pos == nil then
939 pos = {0,0,0}
940 end
941
942 local vX,_,vZ = localToLocal(node, segment.o, pos[1],pos[2],pos[3])
943 return math.atan2(vZ, vX)
944end

getCollisionBoxSlope

Description
Definition
getCollisionBoxSlope()
Code
656function AITurnStrategy:getCollisionBoxSlope(rootNode, x1, y1, z1, x2, y2, z2)
657 x1, y1, z1 = localToWorld(self.vehicleDirectionNode, x1, y1, z1)
658 x2, y2, z2 = localToWorld(self.vehicleDirectionNode, x2, y2, z2)
659
660 local terrain1 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x1, 0, z1)
661 local terrain2 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x2, 0, z2)
662
663 local length = MathUtil.vector3Length(x1-x2, y1-y2, z1-z2)
664 local angleBetween = math.atan(math.abs(terrain1-terrain2)/length)
665
666 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
667 self.vehicle:addAIDebugLine({x1, terrain1+1, z1}, {x2, terrain2+1, z2}, {1,0,0})
668 Utils.renderTextAtWorldPosition((x1+x2)/2, (terrain1+1+terrain2+1)/2, (z1+z2)/2, string.format("angle: %.1f", math.deg(angleBetween)), getCorrectTextSize(0.012), 0)
669 end
670
671 return angleBetween
672end

getDistanceToCollision

Description
Definition
getDistanceToCollision()
Code
476function AITurnStrategy:getDistanceToCollision(dt, vX,vY,vZ, turnData, lookAheadDistance)
477 local allowLeft = self.usesExtraStraight == turnData.useExtraStraightLeft
478 local allowRight = self.usesExtraStraight == turnData.useExtraStraightRight
479
480 local distanceToTurn = lookAheadDistance
481
482 -- Disable this turn strategy immediately if we are supposed to turn in a direction that is not allwoed
483 -- or if the only allowed side does not have any work to be done
484 if not allowLeft and not allowRight then
485 distanceToTurn = -1
486 elseif self.turnLeft ~= nil then
487 if (self.turnLeft and not allowRight) or (not self.turnLeft and not allowLeft) then
488 distanceToTurn = -1
489 end
490 else
491 local allowLeftWithCol = allowLeft and self.collisionEndPosLeft == nil
492 local allowRightWithCol = allowRight and self.collisionEndPosRight == nil
493
494 -- Turn if not allowed or has collision on one side and the other side has no work to be done
495 if not allowLeftWithCol or not allowRightWithCol then
496 local leftAreaPercentage, rightAreaPercentage = AIVehicleUtil.getValidityOfTurnDirections(self.vehicle, turnData)
497
498 if allowLeftWithCol and leftAreaPercentage <= 3*AIVehicleUtil.VALID_AREA_THRESHOLD then
499 distanceToTurn = -1
500 end
501 if allowRightWithCol and rightAreaPercentage <= 3*AIVehicleUtil.VALID_AREA_THRESHOLD then
502 distanceToTurn = -1
503 end
504 end
505 end
506
507 if self.collisionDetected then
508 if self.collisionDetectedPosX ~= nil then
509 local dist = MathUtil.vector3Length(vX-self.collisionDetectedPosX, vY-self.collisionDetectedPosY, vZ-self.collisionDetectedPosZ)
510 distanceToTurn = math.min(distanceToTurn, lookAheadDistance - dist)
511 else
512 distanceToTurn = -1
513 end
514 end
515
516 self.distanceToCollision = distanceToTurn
517
518 -- increase the size of the collision check boxes more to the back
519 -- this helps to avoid issues if an object is between the field end and the vehicle direction node -> so the boxes cover also 5m behind the direction node
520 local boxLookBackDistance = 0
521 if self.parent ~= nil then
522 if self.parent.rowStartTranslation ~= nil then
523 local x, _, z = getWorldTranslation(self.vehicle:getAIVehicleDirectionNode())
524 boxLookBackDistance = math.min(MathUtil.vector2Length(x-self.parent.rowStartTranslation[1], z-self.parent.rowStartTranslation[3]) * 0.66, 5)
525 end
526 end
527
528 --
529 for i=#self.maxTurningSizeBoxes, 1, -1 do
530 self.maxTurningSizeBoxes[i] = nil
531 end
532
533 local collisionHitLeft = false
534 local collisionHitRight = false
535
536 if (self.turnLeft == nil or self.turnLeft == false) and allowLeft then
537 local turnLeft = true
538 self:updateTurningSizeBox(self.leftBox, turnLeft, turnData, math.max(0,distanceToTurn))
539 local box = self.leftBox
540 box.center[3] = box.center[3]-boxLookBackDistance/2
541 box.size[3] = box.size[3]+boxLookBackDistance
542
543 if not self:validateCollisionBox(box) then
544 self.vehicle.stopAIVehicle(AIVehicle.STOP_REASON_UNKOWN)
545 return distanceToTurn
546 end
547
548 collisionHitLeft = self:getIsBoxColliding(box)
549
550 table.insert(self.maxTurningSizeBoxes, box)
551
552 if collisionHitLeft and self.collisionEndPosLeft == nil then
553 self.collisionEndPosLeft = { localToWorld(self.vehicleDirectionNode, 0,0,box.size[3]) }
554 end
555 end
556
557 if (self.turnLeft == nil or self.turnLeft == true) and allowRight then
558 local turnLeft = false
559 self:updateTurningSizeBox(self.rightBox, turnLeft, turnData, math.max(0,distanceToTurn))
560 local box = self.rightBox
561 box.center[3] = box.center[3]-boxLookBackDistance/2
562 box.size[3] = box.size[3]+boxLookBackDistance
563
564 if not self:validateCollisionBox(box) then
565 self.vehicle.stopAIVehicle(AIVehicle.STOP_REASON_UNKOWN)
566 return distanceToTurn
567 end
568
569 collisionHitRight = self:getIsBoxColliding(box)
570
571 table.insert(self.maxTurningSizeBoxes, box)
572
573 if collisionHitRight and self.collisionEndPosRight == nil then
574 self.collisionEndPosRight = { localToWorld(self.vehicleDirectionNode, 0,0,box.size[3]) }
575 end
576 end
577
578 self:evaluateCollisionHits(vX,vY,vZ, collisionHitLeft, collisionHitRight, turnData)
579
580 return distanceToTurn
581end

getDriveData

Description
Definition
getDriveData()
Code
165function AITurnStrategy:getDriveData(dt, vX,vY,vZ, turnData)
166 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
167 self.vehicle:addAIDebugText(string.format("strategy: %s", self.strategyName))
168 end
169
170 local tX, tY, tZ
171 local maxSpeed = self.vehicle:getSpeedLimit()
172 maxSpeed = math.min(14, maxSpeed)
173 local distanceToStop
174
175 local segment = self.turnSegments[self.activeTurnSegmentIndex]
176 local segmentIsFinished = false
177
178 local moveForwards = segment.moveForward
179
180 if segment.isCurve then
181 local angleDirSign = MathUtil.sign(segment.endAngle - segment.startAngle)
182
183 local curAngle
184 if self.reverserDirectionNode ~= nil and not moveForwards then
185 curAngle = AITurnStrategy.getAngleInSegment(self.reverserDirectionNode, segment)
186 else
187 curAngle = AITurnStrategy.getAngleInSegment(self.vehicleAISteeringNode, segment)
188 end
189
190 local nextAngleDistance = math.max(3, 0.33 * self.vehicle.maxTurningRadius)
191
192 local nextAngle = curAngle + angleDirSign * nextAngleDistance / segment.radius
193 if nextAngle > math.pi then
194 nextAngle = nextAngle - 2*math.pi
195 elseif nextAngle < -math.pi then
196 nextAngle = nextAngle + 2*math.pi
197 end
198
199 local endAngle = segment.endAngle
200 if endAngle > math.pi then
201 endAngle = endAngle - 2*math.pi
202 elseif endAngle < -math.pi then
203 endAngle = endAngle + 2*math.pi
204 end
205
206 angleDirSign = MathUtil.sign(segment.endAngle - segment.startAngle)
207
208 local curAngleDiff = angleDirSign * (curAngle - endAngle) -- > 0 if after endAngle
209 if curAngleDiff > math.rad(10) then
210 curAngleDiff = curAngleDiff - 2*math.pi
211 elseif curAngleDiff < -2*math.pi + math.rad(10) then
212 curAngleDiff = curAngleDiff + 2*math.pi
213 end
214
215 local nextAngleDiff = angleDirSign * (nextAngle - endAngle)
216 if nextAngleDiff > math.rad(10) then
217 nextAngleDiff = nextAngleDiff - 2*math.pi
218 elseif nextAngleDiff < -2*math.pi + math.rad(10) then
219 nextAngleDiff = nextAngleDiff + 2*math.pi
220 end
221
222 local pX = math.cos(nextAngle) * segment.radius
223 local pZ = math.sin(nextAngle) * segment.radius
224 tX,tY,tZ = localToWorld(segment.o, pX,0,pZ)
225
226 -- condition for segment finish
227 distanceToStop = -curAngleDiff * segment.radius
228
229 if distanceToStop < 0.01 or (segment.usePredictionToSkipToNextSegment ~= false and nextAngleDiff > 0) then
230 segmentIsFinished = true
231 end
232
233 if segment.checkForSkipToNextSegment then
234 local nextSegment = self.turnSegments[self.activeTurnSegmentIndex+1]
235
236 local dirX = nextSegment.endPoint[1] - nextSegment.startPoint[1]
237 local dirZ = nextSegment.endPoint[3] - nextSegment.startPoint[3]
238 local dirLength = MathUtil.vector2Length(dirX, dirZ)
239 local dx,_,_
240 if self.reverserDirectionNode ~= nil and not moveForwards then
241 dx,_,_ = worldDirectionToLocal(self.reverserDirectionNode, dirX/dirLength,0,dirZ/dirLength)
242 else
243 dx,_,_ = worldDirectionToLocal(self.vehicleAISteeringNode, dirX/dirLength,0,dirZ/dirLength)
244 end
245
246 local l = MathUtil.vector2Length(dirX, dirZ)
247 dirX, dirZ = dirX / l, dirZ / l
248 pX, pZ = MathUtil.projectOnLine(vX, vZ, nextSegment.startPoint[1], nextSegment.startPoint[3], dirX, dirZ)
249 local dist = MathUtil.vector2Length(vX-pX, vZ-pZ)
250
251 local distToStart = MathUtil.vector2Length(vX-nextSegment.startPoint[1], vZ-nextSegment.startPoint[3])
252
253 if dist < 1.5 and math.abs(dx) < 0.15 and distToStart < self.vehicle.sizeLength/2 then
254 segmentIsFinished = true
255 end
256 end
257
258 --
259 if not moveForwards and self.reverserDirectionNode ~= nil then
260 local x,_,z = worldToLocal(self.reverserDirectionNode, tX,vY,tZ)
261 local alpha = Utils.getYRotationBetweenNodes(self.vehicleAISteeringNode, self.reverserDirectionNode)
262 local ltX = math.cos(alpha)*x - math.sin(alpha)*z
263 local ltZ = math.sin(alpha)*x + math.cos(alpha)*z
264 ltX = -ltX
265 tX,_,tZ = localToWorld(self.vehicleAISteeringNode, ltX,0,ltZ)
266 end
267
268 -- just visual debuging
269 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
270 drawDebugLine(vX,vY+2,vZ, 1,1,0, tX,tY+2,tZ, 1,1,0)
271 end
272 else
273 local toolX,_,toolZ
274 if self.reverserDirectionNode ~= nil then
275 toolX,_,toolZ = getWorldTranslation(self.reverserDirectionNode)
276 end
277
278 local dirX = segment.endPoint[1] - segment.startPoint[1]
279 local dirZ = segment.endPoint[3] - segment.startPoint[3]
280
281 local l = MathUtil.vector2Length(dirX, dirZ)
282 dirX, dirZ = dirX / l, dirZ / l
283 local pX, pZ
284 if self.reverserDirectionNode ~= nil and not moveForwards then
285 pX, pZ = MathUtil.projectOnLine(toolX, toolZ, segment.startPoint[1], segment.startPoint[3], dirX, dirZ)
286 else
287 if self.vehicleAIReverserNode ~= nil and not moveForwards then
288 toolX,_,toolZ = getWorldTranslation(self.vehicleAIReverserNode)
289 pX, pZ = MathUtil.projectOnLine(toolX, toolZ, segment.startPoint[1], segment.startPoint[3], dirX, dirZ)
290 else
291 pX, pZ = MathUtil.projectOnLine(vX, vZ, segment.startPoint[1], segment.startPoint[3], dirX, dirZ)
292 end
293 end
294
295 local factor = 1.0
296 tX = pX + (dirX * factor * self.vehicle.maxTurningRadius)
297 tZ = pZ + (dirZ * factor * self.vehicle.maxTurningRadius)
298
299 if self.reverserDirectionNode ~= nil and not moveForwards then
300 local x,_,z = worldToLocal(self.reverserDirectionNode, tX,vY,tZ)
301 local alpha = Utils.getYRotationBetweenNodes(self.vehicleAISteeringNode, self.reverserDirectionNode)
302
303 local articulatedAxisSpec = self.vehicle.spec_articulatedAxis
304 if articulatedAxisSpec ~= nil and articulatedAxisSpec.componentJoint ~= nil then
305 local node1 = self.vehicle.components[articulatedAxisSpec.componentJoint.componentIndices[1]].node
306 local node2 = self.vehicle.components[articulatedAxisSpec.componentJoint.componentIndices[2]].node
307 if articulatedAxisSpec.anchorActor == 1 then
308 node1, node2 = node2, node1
309 end
310
311 local beta = Utils.getYRotationBetweenNodes(node1, node2)
312 alpha = alpha - beta
313 end
314
315 local ltX = math.cos(alpha)*x - math.sin(alpha)*z
316 local ltZ = math.sin(alpha)*x + math.cos(alpha)*z
317 ltX = -ltX
318 tX,_,tZ = localToWorld(self.vehicleAISteeringNode, ltX,0,ltZ)
319 end
320
321 distanceToStop = MathUtil.vector3Length(segment.endPoint[1]-vX, segment.endPoint[2]-vY, segment.endPoint[3]-vZ)
322
323
324 local _,_,lz = worldToLocal(self.vehicleAISteeringNode, segment.endPoint[1],segment.endPoint[2],segment.endPoint[3])
325 if (segment.moveForward and lz < 0) or (not segment.moveForward and lz > 0) then
326 segmentIsFinished = true
327 end
328
329 -- check during pre final straight, only used by reverse strategies
330 if segment.checkAlignmentToSkipSegment then
331 local d1x,_,d1z = localDirectionToWorld(self.vehicleAISteeringNode, 0,0,1)
332 local l1 = MathUtil.vector2Length(d1x, d1z)
333 d1x, d1z = d1x/l1, d1z/l1
334 local a1 = math.acos( d1x * dirX + d1z * dirZ )
335 local dist = MathUtil.vector2Length(vX-pX, vZ-pZ)
336 local canSkip = math.deg(a1) < 8 and dist < 0.6
337
338 if self.vehicle.spec_articulatedAxis ~= nil and self.vehicle.spec_articulatedAxis.componentJoint ~= nil then
339 for i=1,2 do
340 local node = self.vehicle.components[self.vehicle.spec_articulatedAxis.componentJoint.componentIndices[i]].node
341
342 d1x,_,d1z = localDirectionToWorld(node, 0,0,1)
343 l1 = MathUtil.vector2Length(d1x, d1z)
344 d1x, d1z = d1x/l1, d1z/l1
345 local a = math.acos( d1x * dirX + d1z * dirZ )
346 canSkip = canSkip and math.deg(a) < 8
347 end
348 end
349
350 if self.reverserDirectionNode ~= nil then
351 local d2x,_,d2z = localDirectionToWorld(self.reverserDirectionNode, 0,0,1)
352 local l2 = MathUtil.vector2Length(d2x, d2z)
353 d2x, d2z = d2x/l2, d2z/l2
354 local a2 = math.acos( d2x * dirX + d2z * dirZ )
355 pX, pZ = MathUtil.projectOnLine(toolX,toolZ, segment.startPoint[1],segment.startPoint[3], dirX,dirZ)
356 dist = MathUtil.vector2Length(toolX-pX, toolZ-pZ)
357 canSkip = canSkip and math.deg(a2) < 6 and dist < 0.6
358 end
359
360 local nextSegment = self.turnSegments[self.activeTurnSegmentIndex+1]
361 local _,_,sz = worldToLocal(self.vehicleDirectionNode, nextSegment.startPoint[1],nextSegment.startPoint[2],nextSegment.startPoint[3])
362 local _,_,ez = worldToLocal(self.vehicleDirectionNode, nextSegment.endPoint[1],nextSegment.endPoint[2],nextSegment.endPoint[3])
363 canSkip = canSkip and (sz < 0 or ez < 0)
364
365 if canSkip then
366 segmentIsFinished = true
367 end
368 end
369
370 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
371 local sY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, tX,vY,tZ)
372 drawDebugLine(vX,vY+2,vZ, 1,1,0, tX,sY+2,tZ, 1,1,0)
373 end
374 end
375
376 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
377 self.vehicle:addAIDebugText(string.format("active segment: %d", self.activeTurnSegmentIndex))
378 end
379
380 --# check if a tool can already work
381 if segment.checkForValidArea then
382 local lookAheadDist = 0
383 local lookAheadSize = 1
384 if not moveForwards then
385 lookAheadSize = -1
386 end
387 if AIVehicleUtil.checkImplementListForValidGround(self.vehicle, lookAheadDist, lookAheadSize) then
388 segmentIsFinished = true
389 self.activeTurnSegmentIndex = #self.turnSegments
390 end
391 end
392
393 --#
394 if segment.findEndOfField then
395 local lookAheadDist = 0
396 local lookAheadSize = 1
397 if not moveForwards then
398 lookAheadSize = -1
399 end
400 if not AIVehicleUtil.checkImplementListForValidGround(self.vehicle, lookAheadDist, lookAheadSize) then
401 segmentIsFinished = true
402 end
403 end
404
405 -- activate next segment or stop turn
406 if segmentIsFinished then
407 self.activeTurnSegmentIndex = self.activeTurnSegmentIndex + 1
408
409 if self.turnSegments[self.activeTurnSegmentIndex] == nil then
410 self.isTurning = false
411 return nil
412 end
413 end
414
415 -- calculate turn progress percentage
416 local totalSegmentLength = 0
417 local usedSegmentDistance = 0
418 for i, turnSegment in ipairs(self.turnSegments) do
419 -- exclude the last straight part(s) since we don't know how far we need to go
420 if turnSegment.checkAlignmentToSkipSegment then
421 break
422 end
423
424 local segmentLength
425 if turnSegment.isCurve then
426 segmentLength = math.abs(turnSegment.endAngle - turnSegment.startAngle) * turnSegment.radius
427 else
428 segmentLength = math.abs(MathUtil.vector3Length(turnSegment.endPoint[1]-turnSegment.startPoint[1],
429 turnSegment.endPoint[2]-turnSegment.startPoint[2],
430 turnSegment.endPoint[3]-turnSegment.startPoint[3]))
431 end
432 segmentLength = math.abs(segmentLength)
433
434 totalSegmentLength = totalSegmentLength + segmentLength
435
436 if i < self.activeTurnSegmentIndex then
437 usedSegmentDistance = usedSegmentDistance + segmentLength
438 elseif i == self.activeTurnSegmentIndex then
439 usedSegmentDistance = usedSegmentDistance + (segmentLength-distanceToStop)
440 end
441 end
442
443 local turnProgress = usedSegmentDistance / totalSegmentLength
444 self.vehicle:aiTurnProgress(turnProgress, self.turnLeft)
445 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
446 self.vehicle:addAIDebugText(string.format("turn progress: %.1f%%", turnProgress*100))
447 end
448
449 if not segment.slowDown then
450 distanceToStop = math.huge
451 end
452
453 return tX, tZ, moveForwards, maxSpeed, distanceToStop
454end

getIsBoxColliding

Description
Definition
getIsBoxColliding()
Code
585function AITurnStrategy:getIsBoxColliding(box)
586 box.x, box.y, box.z = localToWorld(self.vehicleDirectionNode, box.center[1], box.center[2], box.center[3])
587 box.zx, box.zy, box.zz = localDirectionToWorld(self.vehicleDirectionNode, 0,0,1)
588 box.xx, box.xy, box.xz = localDirectionToWorld(self.vehicleDirectionNode, 1,0,0)
589 box.ry = math.atan2(box.zx, box.zz)
590 box.color = AITurnStrategy.COLLISION_BOX_COLOR_OK
591
592 self.collisionHit = false
593 overlapBox(box.x,box.y,box.z, 0,box.ry,0, box.size[1],box.size[2],box.size[3], "collisionTestCallback", self, AIVehicleUtil.COLLISION_MASK, true, true, true)
594 if self.collisionHit then
595 box.color = AITurnStrategy.COLLISION_BOX_COLOR_HIT
596 return true
597 end
598
599 local x1, _, z1 = localToWorld(self.vehicleDirectionNode, box.center[1]+box.size[1], 0, box.center[3]+box.size[3])
600 local x2, _, z2 = localToWorld(self.vehicleDirectionNode, box.center[1]+box.size[1], 0, box.center[3]-box.size[3])
601
602 local t1 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x1, 0, z1)
603 local t2 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x2, 0, z2)
604
605 -- check if at the box start or end position is water
606 if t1 < g_currentMission.waterY or t2 < g_currentMission.waterY then
607 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
608 self.vehicle:addAIDebugText(string.format(" hit water: b%.1f f%.1f w%.1f", t1, t2, g_currentMission.waterY))
609 end
610
611 box.color = AITurnStrategy.COLLISION_BOX_COLOR_HIT
612 return true
613 end
614
615 local testLength = 3
616
617 -- left and right side of the box
618 local angle1 = self:getCollisionBoxSlope(self.vehicleDirectionNode, box.center[1]+box.size[1], 0, box.center[3]+box.size[3], box.center[1]+box.size[1], 0, box.center[3]+box.size[3]-testLength)
619 local angle2 = self:getCollisionBoxSlope(self.vehicleDirectionNode, box.center[1]-box.size[1], 0, box.center[3]+box.size[3], box.center[1]-box.size[1], 0, box.center[3]+box.size[3]-testLength)
620
621 -- center of vehicle
622 local angle3 = self:getCollisionBoxSlope(self.vehicleDirectionNode, 0, 0, box.center[3]+box.size[3], 0, 0, box.center[3]+box.size[3]-testLength)
623
624 -- side angle of box
625 local angle4 = self:getCollisionBoxSlope(self.vehicleDirectionNode, box.center[1]+box.size[1]-testLength, 0, box.center[3]+box.size[3], box.center[1]+box.size[1], 0, box.center[3]+box.size[3])
626 local angle5 = self:getCollisionBoxSlope(self.vehicleDirectionNode, box.center[1]-box.size[1]+testLength, 0, box.center[3]+box.size[3], box.center[1]-box.size[1], 0, box.center[3]+box.size[3])
627
628 local angleBetween = math.max(angle1, angle2, angle3, angle4, angle5)
629
630 if angleBetween > AITurnStrategy.SLOPE_DETECTION_THRESHOLD then
631 box.color = AITurnStrategy.COLLISION_BOX_COLOR_HIT
632 return true
633 end
634
635 -- check for density height heaps higher than 2m
636 x1, _, z1 = localToWorld(self.vehicleDirectionNode, box.center[1]+box.size[1], 0, box.center[3]+box.size[3])
637 x2, _, z2 = localToWorld(self.vehicleDirectionNode, box.center[1]-box.size[1], 0, box.center[3]+box.size[3])
638 local length = MathUtil.vector2Length(x1-x2, z1-z2)
639 local steps = math.floor(length/3)
640 for i=0,steps do
641 local alpha = math.min(1/(steps+1)*(i+math.random()), 1)
642 local x, z = MathUtil.lerp(x1, x2, alpha), MathUtil.lerp(z1, z2, alpha)
643 local _, densityHeight = DensityMapHeightUtil.getHeightAtWorldPos(x, 0, z)
644
645 if densityHeight >= AITurnStrategy.DENSITY_HEIGHT_THRESHOLD then
646 box.color = AITurnStrategy.COLLISION_BOX_COLOR_HIT
647 return true
648 end
649 end
650
651 return false
652end

getNoFullCoverageZOffset

Description
Definition
getNoFullCoverageZOffset()
Code
1109function AITurnStrategy:getNoFullCoverageZOffset()
1110 local offset = 0
1111
1112 if AIVehicleUtil.getAttachedImplementsBlockTurnBackward(self.vehicle) then
1113 return 0
1114 end
1115
1116 local attachedAIImplements = self.vehicle:getAttachedAIImplements()
1117 for _, implement in pairs(attachedAIImplements) do
1118 if implement.object:getAIHasNoFullCoverageArea() then
1119 local leftMarker, _, backMarker = implement.object:getAIMarkers()
1120 local _, _, markerZOffset = localToLocal(backMarker, leftMarker, 0,0,0)
1121 offset = offset + markerZOffset
1122 end
1123 end
1124
1125 offset = offset + self.corridorPositionOffset
1126 offset = offset + self.lastValidTurnPositionOffset
1127
1128 return offset
1129end

getVehicleToWorld

Description
Definition
getVehicleToWorld()
Code
1133function AITurnStrategy:getVehicleToWorld(x, y, z, returnTable)
1134 x, y, z = localToWorld(self.vehicleDirectionNode, x, y, z+self:getNoFullCoverageZOffset())
1135 if returnTable then
1136 return {x, y, z}
1137 end
1138
1139 return x, y, z
1140end

getZOffsetForTurn

Description
Definition
getZOffsetForTurn()
Code
853function AITurnStrategy:getZOffsetForTurn(box0)
854 local box = {name="ZoffsetForTurn", center={box0.center[1],box0.center[2],box0.center[3]}, size={box0.size[1],box0.size[2],box0.size[3]}}
855
856 local length = math.max( self.distanceToCollision + 2 * box0.size[3], 20)
857
858 box.center[3] = length/2
859 box.size[3] = length/2
860 box.vFront = { localDirectionToWorld(self.vehicleDirectionNode, 0,0,1) }
861 box.vLeft = { localDirectionToWorld(self.vehicleDirectionNode, 1,0,0) }
862
863 local zOffset = self.distanceToCollision
864
865 local i = 0
866 while box.size[3] > 0.5 do
867 self.collisionHit = self:getIsBoxColliding(box)
868
869 if self.collisionHit then
870 box.center[3] = box.center[3] - box.size[3]/2
871 else
872 zOffset = box.center[3] + box.size[3]
873 box.center[3] = box.center[3] + 3*box.size[3]/2
874 end
875 box.size[3] = box.size[3]/2
876
877 i = i + 1
878 end
879
880 return zOffset
881end

new

Description
Definition
new()
Code
18function AITurnStrategy:new(customMt)
19 if customMt == nil then
20 customMt = AITurnStrategy_mt
21 end
22
23 local self = {}
24 setmetatable(self, customMt)
25
26 self.isTurning = false
27 self.turnLeft = nil
28
29 self.collisionDetected = false
30
31 self.usesExtraStraight = false
32
33 self.distanceToCollision = 5
34
35 self.maxTurningSizeBoxes = {}
36 self.maxTurningSizeBoxes2 = {}
37
38 self.turnSegments = {}
39
40 self.lastValidTurnPositionOffset = 0
41 self.corridorPositionOffset = 0
42
43 self.strategyName = "AITurnStrategy"
44
45 self.leftBox = self:createTurningSizeBox()
46 self.rightBox = self:createTurningSizeBox()
47
48 self.heightChecks = {}
49 table.insert(self.heightChecks, {1, 1})
50 table.insert(self.heightChecks, {-1, 1})
51 table.insert(self.heightChecks, {1, -1})
52 table.insert(self.heightChecks, {-1, -1})
53 self.numHeightChecks = 4
54
55 return self
56end

onEndTurn

Description
Definition
onEndTurn()
Code
913function AITurnStrategy:onEndTurn(turnLeft)
914 if #self.turnSegments > 0 then
915 self.vehicle:aiEndTurn(self.turnLeft)
916 end
917
918 --#
919 self.collisionDetected = false
920 self.collisionEndPosLeft = nil
921 self.collisionEndPosRight = nil
922 self.collisionDetectedPosX = nil
923 self.turnSegments = {}
924 self.turnLeft = turnLeft
925 self.maxTurningSizeBoxes = {}
926 self.maxTurningSizeBoxes2 = {}
927
928 AIVehicleUtil.updateInvertLeftRightMarkers(self.vehicle, self.vehicle)
929 for _,implement in pairs(self.vehicle:getAttachedAIImplements()) do
930 AIVehicleUtil.updateInvertLeftRightMarkers(self.vehicle, implement.object)
931 end
932end

setAIVehicle

Description
Definition
setAIVehicle()
Code
65function AITurnStrategy:setAIVehicle(vehicle, parent)
66 self.vehicle = vehicle
67 self.vehicleDirectionNode = self.vehicle:getAIVehicleDirectionNode()
68 self.vehicleAISteeringNode = self.vehicle:getAIVehicleSteeringNode()
69 self.vehicleAIReverserNode = self.vehicle:getAIVehicleReverserNode()
70 self.reverserDirectionNode = AIVehicleUtil.getAIToolReverserDirectionNode(self.vehicle)
71 self.parent = parent
72
73 AIVehicleUtil.updateInvertLeftRightMarkers(self.vehicle, self.vehicle)
74 for _,implement in pairs(self.vehicle:getAttachedAIImplements()) do
75 AIVehicleUtil.updateInvertLeftRightMarkers(self.vehicle, implement.object)
76 end
77end

startTurn

Description
Definition
startTurn()
Code
687function AITurnStrategy:startTurn(driveStrategyStraight)
688
689 local turnData = driveStrategyStraight.turnData
690
691 self.isTurning = true
692
693 -- clear turn segments
694 for _,segment in pairs(self.turnSegments) do
695 if segment.o ~= nil then
696 delete(segment.o)
697 end
698 end
699
700 self.turnSegments = {}
701 self.turnSegmentsTotalLength = 0
702 self.activeTurnSegmentIndex = 1
703
704 local allowLeft = self.usesExtraStraight == turnData.useExtraStraightLeft and driveStrategyStraight.gabAllowTurnLeft
705 local allowRight = self.usesExtraStraight == turnData.useExtraStraightRight and driveStrategyStraight.gabAllowTurnRight
706 if not allowLeft and not allowRight then
707 return false
708 end
709
710 --#
711 AIVehicleUtil.updateInvertLeftRightMarkers(self.vehicle, self.vehicle)
712 for _,implement in pairs(self.vehicle:getAttachedAIImplements()) do
713 AIVehicleUtil.updateInvertLeftRightMarkers(self.vehicle, implement.object)
714 end
715
716 -- determine turn direction
717 local leftAreaPercentage, rightAreaPercentage = AIVehicleUtil.getValidityOfTurnDirections(self.vehicle, turnData)
718
719 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
720 log(" --(I)--> self.turnLeft:", self.turnLeft, "leftAreaPercentage:", leftAreaPercentage, "rightAreaPercentage:", rightAreaPercentage)
721 end
722
723 if driveStrategyStraight.corridorDistance ~= nil then
724 self.corridorPositionOffset = -driveStrategyStraight.corridorDistance
725 end
726
727 local checkForLastValidPosition = function(vehicleNode, turnLeft, threshold)
728 if turnLeft and not allowLeft then
729 return false
730 end
731 if not turnLeft and not allowRight then
732 return false
733 end
734
735 local position = driveStrategyStraight.lastValidTurnLeftPosition
736 if not turnLeft then
737 position = driveStrategyStraight.lastValidTurnRightPosition
738 end
739
740 if position[1] ~= 0 and position[2] ~= 0 and position[3] ~= 0 then
741 local x, y, z = unpack(driveStrategyStraight.lastValidTurnCheckPosition)
742 local distance = MathUtil.vector3Length(position[1]-x, position[2]-y, position[3]-z)
743 if distance > threshold then
744 self.lastValidTurnPositionOffset = -distance
745
746 return true
747 end
748 end
749 end
750
751 if self.turnLeft == nil then
752 local forcePreferLeft = self.collisionEndPosLeft == nil and self.collisionEndPosRight ~= nil
753 local forcePreferRight = self.collisionEndPosRight == nil and self.collisionEndPosLeft ~= nil and allowRight and rightAreaPercentage > AIVehicleUtil.VALID_AREA_THRESHOLD
754 local preferLeft = ((leftAreaPercentage > rightAreaPercentage or forcePreferLeft) and not forcePreferRight)
755
756 if allowLeft and leftAreaPercentage > AIVehicleUtil.VALID_AREA_THRESHOLD and (preferLeft or not allowRight) then
757 self.turnLeft = true
758
759 -- still check for last valid turn position since the distance to the field could be greater than 5m
760 checkForLastValidPosition(self.vehicleDirectionNode, true, 5)
761 elseif allowRight and rightAreaPercentage > AIVehicleUtil.VALID_AREA_THRESHOLD then
762 self.turnLeft = false
763
764 -- still check for last valid turn position since the distance to the field could be greater than 5m
765 checkForLastValidPosition(self.vehicleDirectionNode, false, 5)
766 else
767 if not checkForLastValidPosition(self.vehicleDirectionNode, true, 5) then
768 if not checkForLastValidPosition(self.vehicleDirectionNode, false, 5) then
769 self:debugPrint("Stopping AIVehicle - no valid ground (I)")
770 return false
771 else
772 self.turnLeft = false
773 end
774 else
775 self.turnLeft = true
776 end
777 end
778 else
779 -- first, switch turn direction
780 self.turnLeft = not self.turnLeft
781
782 -- if we are not allowed to turn into a direction because of field border, collision etc
783 -- and we have valid ground on the opposite side, we try the opposite side instead of stopping the ai
784 -- this solves bug #32797
785 if self.turnLeft then
786 if not allowLeft and rightAreaPercentage > AIVehicleUtil.VALID_AREA_THRESHOLD then
787 self.turnLeft = not self.turnLeft
788 end
789 else
790 if not allowRight and leftAreaPercentage > AIVehicleUtil.VALID_AREA_THRESHOLD then
791 self.turnLeft = not self.turnLeft
792 end
793 end
794
795 if self.turnLeft then
796 if not allowLeft or leftAreaPercentage < AIVehicleUtil.VALID_AREA_THRESHOLD then
797 if not checkForLastValidPosition(self.vehicleDirectionNode, true, 5) then
798 return false
799 end
800 else
801 -- still check for last valid turn position since the distance to the field could be greater than 5m
802 checkForLastValidPosition(self.vehicleDirectionNode, true, 5)
803 end
804 else
805 if not allowRight or rightAreaPercentage < AIVehicleUtil.VALID_AREA_THRESHOLD then
806 if not checkForLastValidPosition(self.vehicleDirectionNode, false, 5) then
807 return false
808 end
809 else
810 -- still check for last valid turn position since the distance to the field could be greater than 5m
811 checkForLastValidPosition(self.vehicleDirectionNode, false, 5)
812 end
813 end
814 end
815
816 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
817 log(" --(II)--> self.turnLeft:", self.turnLeft, "leftAreaPercentage:", leftAreaPercentage, "rightAreaPercentage:", rightAreaPercentage)
818 end
819
820 -- update turn data
821 driveStrategyStraight.turnLeft = not self.turnLeft
822 driveStrategyStraight:updateTurnData()
823 driveStrategyStraight.turnLeft = nil
824
825 local checkFrontDistance = 5
826
827 --# finally set new AI direction and target before turn -> if turn gets interrupted the direction afterwards will still be ok
828 self.vehicle.aiDriveDirection[1], self.vehicle.aiDriveDirection[2] = -self.vehicle.aiDriveDirection[1], -self.vehicle.aiDriveDirection[2]
829
830 local sideOffset
831 if self.turnLeft then
832 sideOffset = turnData.sideOffsetLeft
833 else
834 sideOffset = turnData.sideOffsetRight
835 end
836
837 -- move the ai target by the work width to the turn direction
838 local x, z = self.vehicle.aiDriveTarget[1], self.vehicle.aiDriveTarget[2]
839 local dirX, dirZ = self.vehicle.aiDriveDirection[1], self.vehicle.aiDriveDirection[2]
840 local sideDistance = 2*sideOffset
841 local sideDirX, sideDirY = -dirZ, dirX
842 x, z = x+sideDirX*sideDistance, z+sideDirY*sideDistance
843
844 self.vehicle.aiDriveTarget[1], self.vehicle.aiDriveTarget[2] = x, z
845
846 self.vehicle:aiStartTurn(self.turnLeft)
847
848 return true
849end

startTurnFinalization

Description
Definition
startTurnFinalization()
Code
885function AITurnStrategy:startTurnFinalization()
886 --# adapt segments to ground
887 for _,segment in pairs(self.turnSegments) do
888 if segment.startPoint ~= nil then
889 segment.startPoint[2] = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, segment.startPoint[1],0,segment.startPoint[3])
890 segment.endPoint[2] = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, segment.endPoint[1],0,segment.endPoint[3])
891 elseif segment.o ~= nil then
892 local x,y,z = getWorldTranslation(segment.o)
893 y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x,y,z)
894 setTranslation(segment.o ,x,y,z)
895 end
896 end
897
898 --# calc length of segments
899 for _,segment in pairs(self.turnSegments) do
900 if segment.startPoint ~= nil then
901 segment.length = MathUtil.vector3Length(segment.endPoint[1]-segment.startPoint[1], segment.endPoint[2]-segment.startPoint[2], segment.endPoint[3]-segment.startPoint[3])
902 self.turnSegmentsTotalLength = self.turnSegmentsTotalLength + segment.length
903 else
904 segment.length = math.rad(segment.endAngle - segment.startAngle) * segment.radius
905 self.turnSegmentsTotalLength = self.turnSegmentsTotalLength + segment.length
906 end
907 end
908
909end

update

Description
Definition
update()
Code
81function AITurnStrategy:update(dt)
82
83 if VehicleDebug.state == VehicleDebug.DEBUG_AI then
84
85 AITurnStrategy.drawTurnSegments(self.turnSegments)
86
87 if self.maxTurningSizeBoxes ~= nil then
88 --if not self.isTurning then
89 if table.getn(self.maxTurningSizeBoxes) > 0 then
90 self.maxTurningSizeBoxes2 = {}
91 for _,box in pairs(self.maxTurningSizeBoxes) do
92 local box2 = {}
93 box2.points = {}
94 box2.name = box.name
95 box2.color = {unpack(box.color)}
96
97 local x,y,z = box.x,box.y,box.z
98
99 local blx = box.xx*box.size[1] - box.zx*box.size[3]
100 local blz = box.xz*box.size[1] - box.zz*box.size[3]
101
102 local brx = -box.xx*box.size[1] - box.zx*box.size[3]
103 local brz = -box.xz*box.size[1] - box.zz*box.size[3]
104
105 local flx = box.xx*box.size[1] + box.zx*box.size[3]
106 local flz = box.xz*box.size[1] + box.zz*box.size[3]
107
108 local frx = -box.xx*box.size[1] + box.zx*box.size[3]
109 local frz = -box.xz*box.size[1] + box.zz*box.size[3]
110
111
112 table.insert(box2.points, { x+blx, y-box.size[2], z+blz } ) -- lower: lb
113 table.insert(box2.points, { x+brx, y-box.size[2], z+brz } ) -- rb
114 table.insert(box2.points, { x+frx, y-box.size[2], z+frz } ) -- rf
115 table.insert(box2.points, { x+flx, y-box.size[2], z+flz } ) -- lf
116
117 table.insert(box2.points, { x+blx, y+box.size[2], z+blz } ) -- upper: lb
118 table.insert(box2.points, { x+brx, y+box.size[2], z+brz } )
119 table.insert(box2.points, { x+frx, y+box.size[2], z+frz } )
120 table.insert(box2.points, { x+flx, y+box.size[2], z+flz } )
121
122 table.insert(box2.points, { x, y, z } )
123
124 table.insert(self.maxTurningSizeBoxes2, box2)
125 end
126 self.maxTurningSizeBoxes = {}
127 end
128
129 if self.maxTurningSizeBoxes2 ~= nil then
130 for _,box2 in pairs(self.maxTurningSizeBoxes2) do
131 local p = box2.points
132 local c = box2.color
133
134 -- bottom
135 drawDebugLine(p[1][1],p[1][2],p[1][3], c[1],c[2],c[3], p[2][1],p[2][2],p[2][3], c[1],c[2],c[3])
136 drawDebugLine(p[2][1],p[2][2],p[2][3], c[1],c[2],c[3], p[3][1],p[3][2],p[3][3], c[1],c[2],c[3])
137 drawDebugLine(p[3][1],p[3][2],p[3][3], c[1],c[2],c[3], p[4][1],p[4][2],p[4][3], c[1],c[2],c[3])
138 drawDebugLine(p[4][1],p[4][2],p[4][3], c[1],c[2],c[3], p[1][1],p[1][2],p[1][3], c[1],c[2],c[3])
139 -- top
140 drawDebugLine(p[5][1],p[5][2],p[5][3], c[1],c[2],c[3], p[6][1],p[6][2],p[6][3], c[1],c[2],c[3])
141 drawDebugLine(p[6][1],p[6][2],p[6][3], c[1],c[2],c[3], p[7][1],p[7][2],p[7][3], c[1],c[2],c[3])
142 drawDebugLine(p[7][1],p[7][2],p[7][3], c[1],c[2],c[3], p[8][1],p[8][2],p[8][3], c[1],c[2],c[3])
143 drawDebugLine(p[8][1],p[8][2],p[8][3], c[1],c[2],c[3], p[5][1],p[5][2],p[5][3], c[1],c[2],c[3])
144 -- left
145 drawDebugLine(p[1][1],p[1][2],p[1][3], c[1],c[2],c[3], p[5][1],p[5][2],p[5][3], c[1],c[2],c[3])
146 drawDebugLine(p[4][1],p[4][2],p[4][3], c[1],c[2],c[3], p[8][1],p[8][2],p[8][3], c[1],c[2],c[3])
147 -- right
148 drawDebugLine(p[2][1],p[2][2],p[2][3], c[1],c[2],c[3], p[6][1],p[6][2],p[6][3], c[1],c[2],c[3])
149 drawDebugLine(p[3][1],p[3][2],p[3][3], c[1],c[2],c[3], p[7][1],p[7][2],p[7][3], c[1],c[2],c[3])
150 -- center
151 p[9][2] = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, p[9][1],p[9][2],p[9][3]) + 2
152 drawDebugPoint(p[9][1],p[9][2],p[9][3], 1, 0, 0, 1)
153
154 Utils.renderTextAtWorldPosition(p[9][1],p[9][2],p[9][3], box2.name, getCorrectTextSize(0.012), 0)
155 end
156 end
157 end
158
159 end
160
161end

updateTurningSizeBox

Description
Definition
updateTurningSizeBox()
Code
458function AITurnStrategy:updateTurningSizeBox(box, turnLeft, turnData, distanceToTurn)
459 box.center[3] = distanceToTurn/2
460 box.size[1], box.size[2], box.size[3] = 3, 5, distanceToTurn/2
461end