Script v1_7_1_0
- AI
- Animals
- Collections
- Contracts
- Debug
- Economy
- Elements
- EnvironmentalScore
- Errors
- Events
- GUI
- Handtools
- Hud
- I3d
- Input
- Jobs
- Maps
- Materials
- Misc
- Objects
- Parameters
- Placeables
- Placement
- Player
- Shop
- Sounds
- Specialization
- Specializations
- StateMachine
- Statistics
- Tasks
- Triggers
- Utils
- Vehicles
Engine v1_7_1_0
- AI
- Animation
- Camera
- Entity
- Fillplanes
- general
- General
- I3D
- Input
- Lighting
- Math
- Network
- Node
- NoteNode
- Overlays
- Particle System
- Physics
- Rendering
- Scenegraph
- Shape
- Sound
- Spline
- String
- Terrain Detail
- Text Rendering
- Tire Track
- VoiceChat
- XML
Foundation Reference
AIVehicleUtil
DescriptionUtil class for various ai vehicle functionsFunctions
- driveAlongCurvature
- driveInDirection
- driveToPoint
- getAIAreaOfVehicle
- getAIDensityHeightArea
- getAIFruitArea
- getAIToolReverserDirectionNode
- getAreaDimensions
- getAttachedImplementsAllowTurnBackward
- getAttachedImplementsBlockTurnBackward
- getAttachedImplementsMaxTurnRadius
- getAverageDriveDirection
- getDriveDirection
- getIsAreaOwned
- getMaxToolRadius
- getValidityOfTurnDirections
- updateInvertLeftRightMarkers
driveAlongCurvature
DescriptionDefinitiondriveAlongCurvature()Code
101 | function AIVehicleUtil.driveAlongCurvature(self, dt, curvature, maxSpeed, acceleration) |
102 | local targetRotTime = self:getSteeringRotTimeByCurvature(curvature) |
103 | |
104 | -- if targetRotTime > self.rotatedTime then |
105 | -- self.rotatedTime = math.min(self.rotatedTime + dt*self:getAISteeringSpeed(), targetRotTime) |
106 | -- else |
107 | -- self.rotatedTime = math.max(self.rotatedTime - dt*self:getAISteeringSpeed(), targetRotTime) |
108 | -- end |
109 | |
110 | self.rotatedTime = -targetRotTime |
111 | |
112 | if self.finishedFirstUpdate then |
113 | local acc = acceleration |
114 | if maxSpeed ~= nil and maxSpeed > 0 then |
115 | self:getMotor():setSpeedLimit(maxSpeed) |
116 | |
117 | if self:getCruiseControlState() ~= Drivable.CRUISECONTROL_STATE_ACTIVE then |
118 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE) |
119 | end |
120 | else |
121 | acc = 0 |
122 | end |
123 | |
124 | WheelsUtil.updateWheelsPhysics(self, dt, self.lastSpeedReal*self.movingDirection, acc, maxSpeed > 0, true) |
125 | end |
126 | end |
driveInDirection
DescriptionDrive in given directionDefinition
driveInDirection(table self, float dt, float steeringAngleLimit, float acceleration, float slowAcceleration, float slowAngleLimit, boolean allowedToDrive, boolean moveForwards, float lx, float lz, float maxSpeed, float slowDownFactor)Arguments
table | self | object of vehicle |
float | dt | time since last call in ms |
float | steeringAngleLimit | limit for steering angle |
float | acceleration | acceleration |
float | slowAcceleration | slow acceleration |
float | slowAngleLimit | limit of slow angle |
boolean | allowedToDrive | allow to drive |
boolean | moveForwards | move forwards |
float | lx | x direction |
float | lz | z direction |
float | maxSpeed | max speed |
float | slowDownFactor | slow down factor |
143 | function AIVehicleUtil.driveInDirection(self, dt, steeringAngleLimit, acceleration, slowAcceleration, slowAngleLimit, allowedToDrive, moveForwards, lx, lz, maxSpeed, slowDownFactor) |
144 | |
145 | local angle = 0 |
146 | if lx ~= nil and lz ~= nil then |
147 | local dot = lz |
148 | angle = math.deg(math.acos(dot)) |
149 | if angle < 0 then |
150 | angle = angle+180 |
151 | end |
152 | |
153 | local turnLeft = lx > 0.00001 |
154 | if not moveForwards then |
155 | turnLeft = not turnLeft |
156 | end |
157 | |
158 | local targetRotTime |
159 | if turnLeft then |
160 | --rotate to the left |
161 | targetRotTime = self.maxRotTime*math.min(angle/steeringAngleLimit, 1) |
162 | else |
163 | --rotate to the right |
164 | targetRotTime = self.minRotTime*math.min(angle/steeringAngleLimit, 1) |
165 | end |
166 | |
167 | if targetRotTime > self.rotatedTime then |
168 | self.rotatedTime = math.min(self.rotatedTime + dt*self:getAISteeringSpeed(), targetRotTime) |
169 | else |
170 | self.rotatedTime = math.max(self.rotatedTime - dt*self:getAISteeringSpeed(), targetRotTime) |
171 | end |
172 | end |
173 | |
174 | |
175 | if self.finishedFirstUpdate then |
176 | local acc = acceleration |
177 | if maxSpeed ~= nil and maxSpeed ~= 0 then |
178 | if math.abs(angle) >= slowAngleLimit then |
179 | maxSpeed = maxSpeed * slowDownFactor |
180 | end |
181 | self.motor:setSpeedLimit(maxSpeed) |
182 | |
183 | if self.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_ACTIVE then |
184 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE) |
185 | end |
186 | else |
187 | if math.abs(angle) >= slowAngleLimit then |
188 | acc = slowAcceleration |
189 | end |
190 | end |
191 | if not allowedToDrive then |
192 | acc = 0 |
193 | end |
194 | if not moveForwards then |
195 | acc = -acc |
196 | end |
197 | WheelsUtil.updateWheelsPhysics(self, dt, self.lastSpeedReal*self.movingDirection, acc, not allowedToDrive, true) |
198 | end |
199 | end |
driveToPoint
DescriptionDrive vehicle to given pointDefinition
driveToPoint(table self, float dt, float acceleration, boolean allowedToDrive, boolean moveForwards, float tX, float tY, float maxSpeed, boolean doNotSteer)Arguments
table | self | object of vehicle to move |
float | dt | time since last call in ms |
float | acceleration | acceleration |
boolean | allowedToDrive | allowed to drive |
boolean | moveForwards | move forwards |
float | tX | local space x position |
float | tY | local space y position |
float | maxSpeed | speed limit |
boolean | doNotSteer | do not steer |
26 | function AIVehicleUtil.driveToPoint(self, dt, acceleration, allowedToDrive, moveForwards, tX, tZ, maxSpeed, doNotSteer) |
27 | if self.finishedFirstUpdate then |
28 | |
29 | if allowedToDrive then |
30 | |
31 | local tX_2 = tX * 0.5 |
32 | local tZ_2 = tZ * 0.5 |
33 | |
34 | local d1X, d1Z = tZ_2, -tX_2 |
35 | if tX > 0 then |
36 | d1X, d1Z = -tZ_2, tX_2 |
37 | end |
38 | |
39 | local hit,_,f2 = MathUtil.getLineLineIntersection2D(tX_2,tZ_2, d1X,d1Z, 0,0, tX, 0) |
40 | |
41 | if doNotSteer == nil or not doNotSteer then |
42 | local rotTime = 0 |
43 | if hit and math.abs(f2) < 100000 then |
44 | local radius = tX * f2 |
45 | rotTime = self:getSteeringRotTimeByCurvature(1 / radius) |
46 | |
47 | if self:getReverserDirection() < 0 then |
48 | rotTime = -rotTime |
49 | end |
50 | |
51 | --rotTime = self.wheelSteeringDuration * ( math.atan(1/radius) / math.atan(1/self.maxTurningRadius) ) |
52 | end |
53 | |
54 | local targetRotTime |
55 | if rotTime >= 0 then |
56 | targetRotTime = math.min(rotTime, self.maxRotTime) |
57 | else |
58 | targetRotTime = math.max(rotTime, self.minRotTime) |
59 | end |
60 | |
61 | if targetRotTime > self.rotatedTime then |
62 | self.rotatedTime = math.min(self.rotatedTime + dt*self:getAISteeringSpeed(), targetRotTime) |
63 | else |
64 | self.rotatedTime = math.max(self.rotatedTime - dt*self:getAISteeringSpeed(), targetRotTime) |
65 | end |
66 | |
67 | -- adjust maxSpeed |
68 | local steerDiff = targetRotTime - self.rotatedTime |
69 | local fac = math.abs(steerDiff) / math.max(self.maxRotTime, -self.minRotTime) |
70 | local speedReduction = 1.0 - math.pow(fac, 0.25) |
71 | |
72 | -- if the speed is decreased to less than 1 km/h we do not accelrate anymore |
73 | if maxSpeed * speedReduction < 1 then |
74 | acceleration = 0 |
75 | speedReduction = 1 / maxSpeed |
76 | end |
77 | |
78 | maxSpeed = maxSpeed * speedReduction |
79 | end |
80 | end |
81 | |
82 | self:getMotor():setSpeedLimit(math.min(maxSpeed, self:getCruiseControlSpeed())) |
83 | if self:getCruiseControlState() ~= Drivable.CRUISECONTROL_STATE_ACTIVE then |
84 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE) |
85 | end |
86 | |
87 | if not allowedToDrive then |
88 | acceleration = 0 |
89 | end |
90 | if not moveForwards then |
91 | acceleration = -acceleration |
92 | end |
93 | |
94 | WheelsUtil.updateWheelsPhysics(self, dt, self.lastSpeedReal*self.movingDirection, acceleration, not allowedToDrive, true) |
95 | |
96 | end |
97 | end |
getAIAreaOfVehicle
DescriptionReturns amount of fruit to work for ai vehicle is in given areaDefinition
getAIAreaOfVehicle(table vehicle, float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ)Arguments
table | vehicle | vehicle |
float | startWorldX | start world x |
float | startWorldZ | start world z |
float | widthWorldX | width world x |
float | widthWorldZ | width world z |
float | heightWorldX | height world x |
float | heightWorldZ | height world z |
float | area | area found |
float | totalArea | total area checked |
634 | function AIVehicleUtil.getAIAreaOfVehicle(vehicle, startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ) |
635 | local useDensityHeightMap = #vehicle:getAIDensityHeightTypeRequirements() > 0 |
636 | |
637 | if not useDensityHeightMap then |
638 | local query, isValid = vehicle:getFieldCropsQuery() |
639 | if isValid then |
640 | return AIVehicleUtil.getAIFruitArea(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, query) |
641 | else |
642 | return 0, 0 |
643 | end |
644 | else |
645 | local densityHeightTypeRequirements = vehicle:getAIDensityHeightTypeRequirements() |
646 | return AIVehicleUtil.getAIDensityHeightArea(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, densityHeightTypeRequirements) |
647 | end |
648 | end |
getAIDensityHeightArea
DescriptionReturns amount of density height to work is in given areaDefinition
getAIDensityHeightArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, table fruitRequirements, boolean useWindrowed)Arguments
float | startWorldX | start world x |
float | startWorldZ | start world z |
float | widthWorldX | width world x |
float | widthWorldZ | width world z |
float | heightWorldX | height world x |
float | heightWorldZ | height world z |
table | fruitRequirements | table with all required fruit types |
boolean | useWindrowed | use windrow |
float | area | area found |
float | totalArea | total area checked |
678 | function AIVehicleUtil.getAIDensityHeightArea(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, densityHeightTypeRequirements) |
679 | -- first check if we are on a field |
680 | local _, detailArea, _ = FSDensityMapUtil.getFieldDensity(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ) |
681 | if detailArea == 0 then |
682 | return 0, 0 |
683 | end |
684 | |
685 | local retArea, retTotalArea = 0, 0 |
686 | for _, densityHeightTypeRequirement in pairs(densityHeightTypeRequirements) do |
687 | if densityHeightTypeRequirement.fillType ~= FillType.UNKNOWN then |
688 | local _, area, totalArea = DensityMapHeightUtil.getFillLevelAtArea(densityHeightTypeRequirement.fillType, startWorldX,startWorldZ, widthWorldX,widthWorldZ, heightWorldX,heightWorldZ) |
689 | retArea, retTotalArea = retArea+area, totalArea |
690 | end |
691 | end |
692 | |
693 | return retArea, retTotalArea |
694 | end |
getAIFruitArea
DescriptionReturns amount of fruit to work is in given areaDefinition
getAIFruitArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, table query)Arguments
float | startWorldX | start world x |
float | startWorldZ | start world z |
float | widthWorldX | width world x |
float | widthWorldZ | width world z |
float | heightWorldX | height world x |
float | heightWorldZ | height world z |
table | query | field crops query of vehicle |
float | area | area found |
float | totalArea | total area checked |
661 | function AIVehicleUtil.getAIFruitArea(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, query) |
662 | local x,z, widthX,widthZ, heightX,heightZ = MathUtil.getXZWidthAndHeight(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ) |
663 | return query:getParallelogram(x,z, widthX,widthZ, heightX,heightZ, false) |
664 | end |
getAIToolReverserDirectionNode
DescriptionReturns reverser direction node of attached ai toolDefinition
getAIToolReverserDirectionNode(table vehicle)Arguments
table | vehicle | vehicle to check |
integer | aiToolReverserDirectionNode | reverser direction node of ai tool |
335 | function AIVehicleUtil.getAIToolReverserDirectionNode(vehicle) |
336 | for _, implement in pairs(vehicle:getAttachedImplements()) do |
337 | if implement.object ~= nil and implement.object.getAIToolReverserDirectionNode ~= nil then |
338 | local reverserNode = implement.object:getAIToolReverserDirectionNode() |
339 | |
340 | local attachedReverserNode = AIVehicleUtil.getAIToolReverserDirectionNode(implement.object) |
341 | reverserNode = reverserNode or attachedReverserNode |
342 | |
343 | if reverserNode ~= nil then |
344 | return reverserNode |
345 | end |
346 | end |
347 | end |
348 | end |
getAreaDimensions
DescriptionDefinitiongetAreaDimensions()Code
588 | function AIVehicleUtil.getAreaDimensions(directionX, directionZ, leftNode, rightNode, xOffset, zOffset, areaSize, invertXOffset) |
589 | local xOffsetLeft, xOffsetRight = xOffset, xOffset |
590 | if invertXOffset == nil or invertXOffset then |
591 | xOffsetLeft = -xOffsetLeft |
592 | end |
593 | local lX, _, lZ = localToWorld(leftNode, xOffsetLeft, 0, zOffset) |
594 | local rX, _, rZ = localToWorld(rightNode, xOffsetRight, 0, zOffset) |
595 | |
596 | local sX = lX - (0.5 * directionX) |
597 | local sZ = lZ - (0.5 * directionZ) |
598 | local wX = rX - (0.5 * directionX) |
599 | local wZ = rZ - (0.5 * directionZ) |
600 | local hX = lX + (areaSize * directionX) |
601 | local hZ = lZ + (areaSize * directionZ) |
602 | |
603 | return sX, sZ, wX, wZ, hX, hZ |
604 | end |
getAttachedImplementsAllowTurnBackward
DescriptionReturns if trailer or trailer low is attachedDefinition
getAttachedImplementsAllowTurnBackward(table vehicle)Arguments
table | vehicle | vehicle to check |
boolean | isAttached | is attached |
247 | function AIVehicleUtil.getAttachedImplementsAllowTurnBackward(vehicle) |
248 | if vehicle.getAIAllowTurnBackward ~= nil then |
249 | if not vehicle:getAIAllowTurnBackward() then |
250 | return false |
251 | end |
252 | end |
253 | |
254 | if vehicle.getAttachedImplements ~= nil then |
255 | for _, implement in pairs(vehicle:getAttachedImplements()) do |
256 | local object = implement.object |
257 | if object ~= nil then |
258 | if object.getAIAllowTurnBackward ~= nil then |
259 | if not object:getAIAllowTurnBackward() then |
260 | return false |
261 | end |
262 | end |
263 | |
264 | if not AIVehicleUtil.getAttachedImplementsAllowTurnBackward(object) then |
265 | return false |
266 | end |
267 | end |
268 | end |
269 | end |
270 | |
271 | return true |
272 | end |
getAttachedImplementsBlockTurnBackward
DescriptionReturns if one of the attached implements blocks reverse drivingDefinition
getAttachedImplementsBlockTurnBackward(table vehicle)Arguments
table | vehicle | vehicle to check |
boolean | doesBlock | implement does block |
278 | function AIVehicleUtil.getAttachedImplementsBlockTurnBackward(vehicle) |
279 | if vehicle.getAIBlockTurnBackward ~= nil then |
280 | if vehicle:getAIBlockTurnBackward() then |
281 | return true |
282 | end |
283 | end |
284 | |
285 | if vehicle.getAttachedImplements ~= nil then |
286 | for _, implement in pairs(vehicle:getAttachedImplements()) do |
287 | local object = implement.object |
288 | if object ~= nil then |
289 | if object.getAIBlockTurnBackward ~= nil then |
290 | if object:getAIBlockTurnBackward() then |
291 | return true |
292 | end |
293 | end |
294 | |
295 | if AIVehicleUtil.getAttachedImplementsBlockTurnBackward(object) then |
296 | return true |
297 | end |
298 | end |
299 | end |
300 | end |
301 | |
302 | return false |
303 | end |
getAttachedImplementsMaxTurnRadius
DescriptionDefinitiongetAttachedImplementsMaxTurnRadius()Code
307 | function AIVehicleUtil.getAttachedImplementsMaxTurnRadius(vehicle) |
308 | local maxRadius = -1 |
309 | if vehicle.getAttachedImplements ~= nil then |
310 | for _, implement in pairs(vehicle:getAttachedImplements()) do |
311 | local object = implement.object |
312 | if object ~= nil then |
313 | if object.getAITurnRadiusLimitation ~= nil then |
314 | local radius = object:getAITurnRadiusLimitation() |
315 | if radius ~= nil and radius > maxRadius then |
316 | maxRadius = radius |
317 | end |
318 | end |
319 | |
320 | local radius = AIVehicleUtil.getAttachedImplementsMaxTurnRadius(object) |
321 | if radius > maxRadius then |
322 | maxRadius = radius |
323 | end |
324 | end |
325 | end |
326 | end |
327 | |
328 | return maxRadius |
329 | end |
getAverageDriveDirection
DescriptionReturns average drive direction between 2 given vectorsDefinition
getAverageDriveDirection(integer refNode, float x, float y, float z, float x2, float y2, float z2)Arguments
integer | refNode | id of ref node |
float | x | world x 1 |
float | y | world y 1 |
float | z | world z 1 |
float | x2 | world x 2 |
float | y2 | world y 2 |
float | z2 | world z 2 |
float | lx | average x direction |
float | lz | average z direction |
232 | function AIVehicleUtil.getAverageDriveDirection(refNode, x, y, z, x2, y2, z2) |
233 | local lx, _, lz = worldToLocal(refNode, (x+x2)*0.5, (y+y2)*0.5, (z+z2)*0.5) |
234 | |
235 | local length = MathUtil.vector2Length(lx, lz) |
236 | if length > 0.00001 then |
237 | lx = lx/length |
238 | lz = lz/length |
239 | end |
240 | return lx, lz, length |
241 | end |
getDriveDirection
DescriptionReturns drive directionDefinition
getDriveDirection(integer refNode, float x, float y, float z)Arguments
integer | refNode | id of ref node |
float | x | world x |
float | y | world y |
float | z | world z |
float | lx | x direction |
float | lz | z direction |
209 | function AIVehicleUtil.getDriveDirection(refNode, x, y, z) |
210 | local lx, _, lz = worldToLocal(refNode, x, y, z) |
211 | |
212 | local length = MathUtil.vector2Length(lx, lz) |
213 | if length > 0.00001 then |
214 | length = 1/length |
215 | lx = lx*length |
216 | lz = lz*length |
217 | end |
218 | return lx, lz |
219 | end |
getIsAreaOwned
DescriptionDefinitiongetIsAreaOwned()Code
608 | function AIVehicleUtil.getIsAreaOwned(vehicle, sX, sZ, wX, wZ, hX, hZ) |
609 | local farmId = vehicle:getAIJobFarmId() |
610 | local centerX, centerZ = (sX + wX)*0.5, (sZ + wZ)*0.5 |
611 | if g_farmlandManager:getIsOwnedByFarmAtWorldPosition(farmId, centerX, centerZ) then |
612 | return true |
613 | end |
614 | |
615 | if g_missionManager:getIsMissionWorkAllowed(farmId, centerX, centerZ, nil) then |
616 | return true |
617 | end |
618 | |
619 | return false |
620 | end |
getMaxToolRadius
DescriptionReturns max tool turn radiusDefinition
getMaxToolRadius(table implement)Arguments
table | implement | implement to check |
float | maxTurnRadius | max turn radius |
354 | function AIVehicleUtil.getMaxToolRadius(implement) |
355 | local radius = 0 |
356 | |
357 | local _, rotationNode, wheels, rotLimitFactor = implement.object:getAITurnRadiusLimitation() |
358 | |
359 | -- collect the max manual defined turn radius of all vehicles, not only valid ai implements |
360 | local rootVehicle = implement.object.rootVehicle |
361 | local retRadius = AIVehicleUtil.getAttachedImplementsMaxTurnRadius(rootVehicle) |
362 | |
363 | if retRadius ~= -1 then |
364 | radius = retRadius |
365 | end |
366 | |
367 | if rotationNode then |
368 | local activeInputAttacherJoint = implement.object:getActiveInputAttacherJoint() |
369 | local refNode = rotationNode |
370 | |
371 | -- If the refNode is any attacher joint, we always use the currently used attacher joint |
372 | for _, inputAttacherJoint in pairs(implement.object:getInputAttacherJoints()) do |
373 | if refNode == inputAttacherJoint.node then |
374 | refNode = activeInputAttacherJoint.node |
375 | break |
376 | end |
377 | end |
378 | |
379 | local rx,_,rz = localToLocal(refNode, implement.object.components[1].node, 0,0,0) |
380 | |
381 | for _, wheel in pairs(wheels) do |
382 | local nx,_,nz = localToLocal(wheel.repr, implement.object.components[1].node, 0,0,0) |
383 | |
384 | local x,z = nx-rx, nz-rz |
385 | local cx,cz = 0,0 |
386 | |
387 | -- get max rotation |
388 | local rotMax |
389 | if refNode == activeInputAttacherJoint.node then |
390 | local attacherVehicle = implement.object:getAttacherVehicle() |
391 | local jointDesc = attacherVehicle:getAttacherJointDescFromObject(implement.object) |
392 | rotMax = math.max(jointDesc.upperRotLimit[2], jointDesc.lowerRotLimit[2]) * activeInputAttacherJoint.lowerRotLimitScale[2] |
393 | else |
394 | for _,compJoint in pairs(implement.object.componentJoints) do |
395 | if refNode == compJoint.jointNode then |
396 | rotMax = compJoint.rotLimit[2] |
397 | break |
398 | end |
399 | end |
400 | end |
401 | |
402 | rotMax = rotMax * rotLimitFactor |
403 | |
404 | -- calc turning radius |
405 | local x1 = x*math.cos(rotMax) - z*math.sin(rotMax) |
406 | local z1 = x*math.sin(rotMax) + z*math.cos(rotMax) |
407 | |
408 | local dx = -z1 |
409 | local dz = x1 |
410 | if wheel.steeringAxleScale ~= 0 and wheel.steeringAxleRotMax ~= 0 then |
411 | local tmpx, tmpz = dx, dz |
412 | dx = tmpx*math.cos(wheel.steeringAxleRotMax) - tmpz*math.sin(wheel.steeringAxleRotMax) |
413 | dz = tmpx*math.sin(wheel.steeringAxleRotMax) + tmpz*math.cos(wheel.steeringAxleRotMax) |
414 | end |
415 | |
416 | local hit,f1,_ = MathUtil.getLineLineIntersection2D(cx,cz, 1,0, x1,z1, dx,dz) |
417 | if hit then |
418 | radius = math.max(radius, math.abs(f1)) |
419 | end |
420 | end |
421 | end |
422 | |
423 | return radius |
424 | end |
getValidityOfTurnDirections
DescriptionChecks fruits on left and right side of vehicle to decide the turn directionDefinition
getValidityOfTurnDirections(table vehicle, float checkFrontDistance, table turnData)Arguments
table | vehicle | vehicle to check |
float | checkFrontDistance | distance to check in front of vehicle |
table | turnData | properties for turning |
float | leftAreaPercentage | left area percentage |
float | rightAreaPercentage | right area percentage |
451 | function AIVehicleUtil.getValidityOfTurnDirections(vehicle, turnData) |
452 | -- let's check the area at/around the marker which is farest behind of vehicle |
453 | local directionNode = vehicle:getAIDirectionNode() |
454 | local attachedAIImplements = vehicle:getAttachedAIImplements() |
455 | local checkFrontDistance = 5 |
456 | |
457 | local leftAreaPercentage = 0 |
458 | local rightAreaPercentage = 0 |
459 | |
460 | local minZ = math.huge |
461 | local maxZ = -math.huge |
462 | for _,implement in pairs(attachedAIImplements) do |
463 | local leftMarker, rightMarker, backMarker = implement.object:getAIMarkers() |
464 | |
465 | local _,_,zl = localToLocal(leftMarker, directionNode, 0,0,0) |
466 | local _,_,zr = localToLocal(rightMarker, directionNode, 0,0,0) |
467 | local _,_,zb = localToLocal(backMarker, directionNode, 0,0,0) |
468 | |
469 | minZ = math.min(minZ, zl, zr, zb) |
470 | maxZ = math.max(maxZ, zl, zr, zb) |
471 | end |
472 | |
473 | local sideDistance |
474 | if turnData == nil then |
475 | local minAreaWidth = math.huge |
476 | for _,implement in pairs(attachedAIImplements) do |
477 | local leftMarker, rightMarker, _ = implement.object:getAIMarkers() |
478 | |
479 | local lx, _, _ = localToLocal(leftMarker, directionNode, 0,0,0) |
480 | local rx, _, _ = localToLocal(rightMarker, directionNode, 0,0,0) |
481 | minAreaWidth = math.min(minAreaWidth, math.abs(lx-rx)) |
482 | end |
483 | sideDistance = minAreaWidth |
484 | else |
485 | sideDistance = math.abs(turnData.sideOffsetRight - turnData.sideOffsetLeft) |
486 | end |
487 | |
488 | local dx, dz = vehicle.aiDriveDirection[1], vehicle.aiDriveDirection[2] |
489 | local sx, sz = -dz, dx |
490 | |
491 | for _,implement in pairs(attachedAIImplements) do |
492 | local leftMarker, rightMarker, _ = implement.object:getAIMarkers() |
493 | |
494 | local lx, ly, lz = localToLocal(leftMarker, directionNode, 0,0,0) |
495 | local rx, ry, rz = localToLocal(rightMarker, directionNode, 0,0,0) |
496 | |
497 | local width = math.abs(lx-rx) |
498 | local length = checkFrontDistance + (maxZ - minZ) + math.max(sideDistance*1.3 + 2, checkFrontDistance) -- 1.3~tan(53) allows detecting back along a field side with angle 53 (and 2m extra compensates for some variances, or higher angles with small tools) |
499 | |
500 | lx, _, lz = localToWorld(directionNode, lx,ly,maxZ + checkFrontDistance) |
501 | rx, _, rz = localToWorld(directionNode, rx,ry,maxZ + checkFrontDistance) |
502 | |
503 | local lSX = lx |
504 | local lSZ = lz |
505 | local lWX = lSX - sx * width |
506 | local lWZ = lSZ - sz * width |
507 | local lHX = lSX - dx * length |
508 | local lHZ = lSZ - dz * length |
509 | |
510 | local rSX = rx |
511 | local rSZ = rz |
512 | local rWX = rSX + sx * width |
513 | local rWZ = rSZ + sz * width |
514 | local rHX = rSX - dx * length |
515 | local rHZ = rSZ - dz * length |
516 | |
517 | local lArea, lTotal = AIVehicleUtil.getAIAreaOfVehicle(implement.object, lSX,lSZ, lWX,lWZ, lHX,lHZ, false) |
518 | local rArea, rTotal = AIVehicleUtil.getAIAreaOfVehicle(implement.object, rSX,rSZ, rWX,rWZ, rHX,rHZ, false) |
519 | |
520 | if lTotal > 0 then |
521 | leftAreaPercentage = leftAreaPercentage + (lArea / lTotal) |
522 | end |
523 | if rTotal > 0 then |
524 | rightAreaPercentage = rightAreaPercentage + (rArea / rTotal) |
525 | end |
526 | |
527 | -- just visual debuging |
528 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
529 | local lSY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, lSX,0,lSZ)+2 |
530 | local lWY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, lWX,0,lWZ)+2 |
531 | local lHY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, lHX,0,lHZ)+2 |
532 | local rSY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rSX,0,rSZ)+2 |
533 | local rWY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rWX,0,rWZ)+2 |
534 | local rHY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rHX,0,rHZ)+2 |
535 | |
536 | vehicle:addAIDebugLine({lSX,lSY,lSZ}, {lWX,lWY,lWZ}, {0.5, 0.5, 0.5}) |
537 | vehicle:addAIDebugLine({lSX,lSY,lSZ}, {lHX,lHY,lHZ}, {0.5, 0.5, 0.5}) |
538 | vehicle:addAIDebugLine({rSX,rSY,rSZ}, {rWX,rWY,rWZ}, {0.5, 0.5, 0.5}) |
539 | vehicle:addAIDebugLine({rSX,rSY,rSZ}, {rHX,rHY,rHZ}, {0.5, 0.5, 0.5}) |
540 | end |
541 | end |
542 | |
543 | leftAreaPercentage = leftAreaPercentage / #attachedAIImplements |
544 | rightAreaPercentage = rightAreaPercentage / #attachedAIImplements |
545 | |
546 | return leftAreaPercentage, rightAreaPercentage |
547 | end |
updateInvertLeftRightMarkers
DescriptionUpdate invertation of ai left and right markers on vehicleDefinition
updateInvertLeftRightMarkers(table rootAttacherVehicle, table vehicle)Arguments
table | rootAttacherVehicle | root attacher vehicle |
table | vehicle | vehicle |
430 | function AIVehicleUtil.updateInvertLeftRightMarkers(rootAttacherVehicle, vehicle) |
431 | if vehicle.getAIMarkers ~= nil then |
432 | local leftMarker, rightMarker, _ = vehicle:getAIMarkers() |
433 | if leftMarker ~= nil and rightMarker ~= nil then |
434 | local lX, _, _ = localToLocal(leftMarker, rootAttacherVehicle:getAIDirectionNode(), 0,0,0) |
435 | local rX, _, _ = localToLocal(rightMarker, rootAttacherVehicle:getAIDirectionNode(), 0,0,0) |
436 | |
437 | if rX > lX then |
438 | vehicle:setAIMarkersInverted() |
439 | end |
440 | end |
441 | end |
442 | end |