forked from Courseplay/courseplay
-
Notifications
You must be signed in to change notification settings - Fork 0
/
reverse.lua
429 lines (379 loc) · 17.3 KB
/
reverse.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
local abs, max, rad, sin = math.abs, math.max, math.rad, math.sin;
function courseplay:goReverse(vehicle,lx,lz,mode2)
local fwd = false;
local workTool = courseplay:getFirstReversingWheeledWorkTool(vehicle) or vehicle.cp.workTools[1];
local newTarget;
if vehicle.cp.turnTargets and vehicle.cp.curTurnIndex then
newTarget = vehicle.cp.turnTargets[vehicle.cp.curTurnIndex];
end;
if workTool then
-- Attacher modules and HookLift modules that needs the hookLiftTrailer
if courseplay:isHookLift(workTool) or courseplay:isAttacherModule(workTool) then
workTool = workTool.attacherVehicle;
if workTool == vehicle and vehicle.cp.workTools[2] ~= nil then
workTool = vehicle.cp.workTools[2];
if courseplay:isAttacherModule(workTool) then
workTool = workTool.attacherVehicle;
end;
end;
end;
end;
local debugActive = courseplay.debugChannels[13];
local isNotValid = vehicle.cp.numWorkTools == 0 or workTool == nil or workTool.cp.isPivot == nil or not workTool.cp.frontNode or vehicle.cp.mode == 9;
if isNotValid then
-- Simple reversing, no trailer to back up, so set the direction and get out of here, no need for
-- all the sophisticated reversing
if newTarget then
-- If we have the revPosX, revPosZ set, use those
if newTarget.revPosX and newTarget.revPosZ then
local _, vehicleY, _ = getWorldTranslation(vehicle.cp.DirectionNode);
lx, lz = AIVehicleUtil.getDriveDirection(vehicle.cp.DirectionNode, newTarget.revPosX, vehicleY, newTarget.revPosZ);
end;
elseif vehicle.cp.mode ~= 9 then
-- Start: Fixes issue #525
local tx, ty, tz = localToWorld(vehicle.cp.DirectionNode, 0, 1, -3);
local nx, ny, nz = localDirectionToWorld(vehicle.cp.DirectionNode, lx, 0, lz);
courseplay:doTriggerRaycasts(vehicle, 'tipTrigger', 'rev', false, tx, ty, tz, nx, ny, nz);
-- End: Fixes issue #525
end
-- false means that this is a trivial reverse and can be handled by drive
return -lx,-lz,fwd, false;
end;
local node = workTool.cp.realTurningNode;
if mode2 then
vehicle.cp.toolsRealTurningNode = node;
end
local isPivot = workTool.cp.isPivot;
local xTipper,yTipper,zTipper = getWorldTranslation(node);
if debugActive then drawDebugPoint(xTipper, yTipper+5, zTipper, 1, 0 , 0, 1) end;
local frontNode = workTool.cp.frontNode;
local xFrontNode,yFrontNode,zFrontNode = getWorldTranslation(frontNode);
local tcx,tcy,tcz =0,0,0;
local index = vehicle.cp.waypointIndex + 1;
if debugActive and not newTarget then
drawDebugPoint(xFrontNode,yFrontNode+3,zFrontNode, 1, 0 , 0, 1);
if not vehicle.cp.checkReverseValdityPrinted then
local checkValdity = false;
for i=index, vehicle.cp.numWaypoints do
if vehicle.Waypoints[i].rev then
tcx = vehicle.Waypoints[i].cx;
tcz = vehicle.Waypoints[i].cz;
local _,_,z = worldToLocal(node, tcx,yTipper,tcz);
if z < 0 then
checkValdity = true;
break;
end;
else
break;
end;
end;
if not checkValdity then
print(nameNum(vehicle) ..": reverse course is not valid");
end;
vehicle.cp.checkReverseValdityPrinted = true;
end;
end;
if newTarget then
if newTarget.revPosX and newTarget.revPosZ then
tcx = newTarget.revPosX;
tcz = newTarget.revPosZ;
else
tcx = newTarget.posX;
tcz = newTarget.posZ;
end;
elseif not mode2 then
for i= index, vehicle.cp.numWaypoints do
if vehicle.Waypoints[i].rev and not vehicle.Waypoints[i-1].wait then
tcx = vehicle.Waypoints[i].cx;
tcz = vehicle.Waypoints[i].cz;
else
local dx, dz, _ = courseplay.generation:getPointDirection(vehicle.Waypoints[i-2], vehicle.Waypoints[i-1]);
tcx = vehicle.Waypoints[i-1].cx + dx * (vehicle.Waypoints[i-1].wait and 15 or 30);
tcz = vehicle.Waypoints[i-1].cz + dz * (vehicle.Waypoints[i-1].wait and 15 or 30);
end;
local distance = courseplay:distance(xTipper,zTipper, vehicle.Waypoints[i-1].cx ,vehicle.Waypoints[i-1].cz);
local waitingPoint;
local unloadPoint;
if vehicle.Waypoints[i-1].wait then
waitingPoint = i-1;
end;
if vehicle.Waypoints[i].wait then
waitingPoint = i;
end;
if vehicle.Waypoints[i-1].unload then
unloadPoint = i-1;
end;
if vehicle.Waypoints[i].unload then
unloadPoint = i;
end;
-- HANDLE WAITING POINT WAYPOINT CHANGE
if waitingPoint then
if workTool.cp.realUnloadOrFillNode then
local _,y,_ = getWorldTranslation(workTool.cp.realUnloadOrFillNode);
local _,_,z = worldToLocal(workTool.cp.realUnloadOrFillNode, vehicle.Waypoints[waitingPoint].cx, y, vehicle.Waypoints[waitingPoint].cz);
if z >= 0 then
courseplay:setWaypointIndex(vehicle, waitingPoint + 1);
courseplay:debug(string.format("%s: Is at waiting point", nameNum(vehicle)), 13);
break;
end;
else
if distance <= 2 then
courseplay:setWaypointIndex(vehicle, waitingPoint + 1);
courseplay:debug(string.format("%s: Is at waiting point", nameNum(vehicle)), 13);
break;
end;
end;
if distance > 3 then
local _,_,z = worldToLocal(node, tcx,yTipper,tcz);
if z < 0 then
courseplay:setWaypointIndex(vehicle, i - 1);
break;
end;
end;
break;
elseif unloadPoint then
if workTool.cp.rearTipRefPoint then
local tipRefPoint = workTool.tipReferencePoints[workTool.cp.rearTipRefPoint].node
local x,y,z = getWorldTranslation(tipRefPoint);
local tipDistanceToPoint = courseplay:distance(x,z,vehicle.Waypoints[unloadPoint].cx,vehicle.Waypoints[unloadPoint].cz)
courseplay:debug(string.format("%s:workTool.cp.rearTipRefPoint: tipDistanceToPoint: %s", nameNum(vehicle),tostring(tipDistanceToPoint)), 13);
if tipDistanceToPoint < 0.5 then
courseplay:setWaypointIndex(vehicle, unloadPoint + 1);
courseplay:debug(string.format("%s: Is at unload point", nameNum(vehicle)), 13);
break;
end;
end
if distance > 3 then
local _,_,z = worldToLocal(node, tcx,yTipper,tcz);
if z < 0 then
courseplay:setWaypointIndex(vehicle, i - 1);
break;
end;
end;
break
-- HANDLE LAST REVERSE WAYPOINT CHANGE
elseif vehicle.Waypoints[i-1].rev and not vehicle.Waypoints[i].rev then
if distance <= 2 then
courseplay:setWaypointIndex(vehicle, courseplay:getNextFwdPoint(vehicle));
vehicle.cp.ppc:initialize()
courseplay:debug(string.format("%s: Change direction to forward", nameNum(vehicle)), 13);
end;
break;
-- FIND THE RIGHT START REVERSING WAYPOINT
elseif vehicle.Waypoints[i-1].rev and not vehicle.Waypoints[i-2].rev then
for recNum = index, vehicle.cp.numWaypoints do
local srX,srZ = vehicle.Waypoints[recNum].cx,vehicle.Waypoints[recNum].cz;
local _,_,tsrZ = worldToLocal(node,srX,yTipper,srZ);
if tsrZ < -2 then
courseplay:setWaypointIndex(vehicle, recNum);
courseplay:debug(string.format("%s: First reverse point -> Change waypoint to behind trailer: %q", nameNum(vehicle), recNum), 13);
break;
end;
end;
break;
-- HANDLE REVERSE WAYPOINT CHANGE
elseif distance > 3 then
local _,_,z = worldToLocal(node, tcx,yTipper,tcz);
if z < 0 then
courseplay:setWaypointIndex(vehicle, i - 1);
break;
end;
end;
end;
elseif mode2 then
tcx,tcz = vehicle.cp.curTarget.x, vehicle.cp.curTarget.z;
end;
if debugActive then
drawDebugPoint(tcx, yTipper+3, tcz, 1, 1 , 1, 1)
if workTool.cp.realUnloadOrFillNode then
local xUOFNode,yUOFNode,zUOFNode = getWorldTranslation(workTool.cp.realUnloadOrFillNode);
drawDebugPoint(xUOFNode,yUOFNode+5,zUOFNode, 0, 1 , 0.5, 1);
end;
end;
local lxTipper, lzTipper = AIVehicleUtil.getDriveDirection(node, tcx, yTipper, tcz);
courseplay:showDirection(node,lxTipper, lzTipper);
local lxFrontNode, lzFrontNode = AIVehicleUtil.getDriveDirection(frontNode, xTipper,yTipper,zTipper);
local lxTractor, lzTractor = 0,0;
local maxTractorAngle = rad(60);
if isPivot then
courseplay:showDirection(frontNode,lxFrontNode, lzFrontNode);
lxTractor, lzTractor = AIVehicleUtil.getDriveDirection(vehicle.cp.DirectionNode, xFrontNode,yFrontNode,zFrontNode);
courseplay:showDirection(vehicle.cp.DirectionNode,lxTractor, lzTractor);
local rotDelta = (workTool.cp.nodeDistance * (0.5 - (0.023 * workTool.cp.nodeDistance - 0.073)));
local trailerToWaypointAngle = courseplay:getLocalYRotationToPoint(node, tcx, yTipper, tcz, -1) * rotDelta;
trailerToWaypointAngle = Utils.clamp(trailerToWaypointAngle, -rad(90), rad(90));
local dollyToTrailerAngle = courseplay:getLocalYRotationToPoint(frontNode, xTipper, yTipper, zTipper, -1);
local tractorToDollyAngle = courseplay:getLocalYRotationToPoint(vehicle.cp.DirectionNode, xFrontNode, yFrontNode, zFrontNode, -1);
local rearAngleDiff = (dollyToTrailerAngle - trailerToWaypointAngle);
rearAngleDiff = Utils.clamp(rearAngleDiff, -rad(45), rad(45));
local frontAngleDiff = (tractorToDollyAngle - dollyToTrailerAngle);
frontAngleDiff = Utils.clamp(frontAngleDiff, -rad(45), rad(45));
local angleDiff = (frontAngleDiff - rearAngleDiff) * (1.5 - (workTool.cp.nodeDistance * 0.4 - 0.9) + rotDelta);
angleDiff = Utils.clamp(angleDiff, -rad(45), rad(45));
lx, lz = Utils.getDirectionFromYRotation(angleDiff);
else
lxTractor, lzTractor = AIVehicleUtil.getDriveDirection(vehicle.cp.DirectionNode, xTipper,yTipper,zTipper);
courseplay:showDirection(vehicle.cp.DirectionNode,lxTractor, lzTractor);
local rotDelta = workTool.cp.nodeDistance * 0.3;
local trailerToWaypointAngle = courseplay:getLocalYRotationToPoint(node, tcx, yTipper, tcz, -1) * rotDelta;
trailerToWaypointAngle = Utils.clamp(trailerToWaypointAngle, -math.rad(90), math.rad(90));
local tractorToTrailerAngle = courseplay:getLocalYRotationToPoint(vehicle.cp.DirectionNode, xTipper, yTipper, zTipper, -1);
local angleDiff = (tractorToTrailerAngle - trailerToWaypointAngle) * (1 + rotDelta);
-- If we only have stearing axle on the worktool and they turn when reversing, we need to stear allot more to counter this.
if workTool.cp.steeringAxleUpdateBackwards then
angleDiff = angleDiff * 4;
end;
angleDiff = Utils.clamp(angleDiff, -maxTractorAngle, maxTractorAngle);
lx, lz = Utils.getDirectionFromYRotation(angleDiff);
end;
if (vehicle.cp.mode == courseplay.MODE_GRAIN_TRANSPORT or vehicle.cp.mode == courseplay.MODE_COMBI or vehicle.cp.mode == courseplay.MODE_FIELDWORK) and vehicle.cp.currentTipTrigger == nil and (vehicle.cp.totalFillLevel ~= nil and vehicle.cp.totalFillLevel > 0) then
local nx, ny, nz = localDirectionToWorld(node, lxTipper, 0, lzTipper);
courseplay:doTriggerRaycasts(vehicle, 'tipTrigger', 'rev', false, xTipper, yTipper + 1, zTipper, nx, ny, nz);
end;
courseplay:showDirection(vehicle.cp.DirectionNode,lx,lz);
-- true means this code is taking care of the reversing as this is not a trivial case
-- for instance because of a trailer
return lx,lz,fwd, true;
end;
function courseplay:getFirstReversingWheeledWorkTool(vehicle)
-- Checl all attached implements if we are an wheeled workTool behind the tractor
for _, imp in ipairs(vehicle.attachedImplements) do
-- Check if the implement is behind
if courseplay:isRearAttached(vehicle, imp.jointDescIndex) then
if courseplay:isWheeledWorkTool(imp.object) then
-- If the implement is a wheeled workTool, then return the object
return imp.object;
else
-- If the implement is not a wheeled workTool, then check if that implement have an attached wheeled workTool and return that.
return courseplay:getFirstReversingWheeledWorkTool(imp.object);
end;
end;
end;
-- If we didnt find any workTool, return nil
return nil;
end;
function courseplay:getLocalYRotationToPoint(node, x, y, z, direction)
direction = direction or 1;
local dx, _, dz = worldToLocal(node, x, y, z);
dx = dx * direction;
dz = dz * direction;
return Utils.getYRotationFromDirection(dx, dz);
end;
function courseplay:showDirection(node,lx,lz)
if courseplay.debugChannels[13] then
local x,y,z = getWorldTranslation(node);
local ctx,_,ctz = localToWorld(node,lx*5,y,lz*5);
drawDebugLine(x, y+5, z, 1, 0, 0, ctx, y+5, ctz, 1, 0, 0);
end
end
-- Find the first forward waypoint ahead of the vehicle
function courseplay:getNextFwdPoint(vehicle, isTurning)
local directionNode = vehicle.isReverseDriving and vehicle.cp.reverseDrivingDirectionNode or vehicle.cp.DirectionNode;
if isTurning then
courseplay:debug(('%s: getNextFwdPoint()'):format(nameNum(vehicle)), 14);
-- scan only the next few waypoints, we don't want to end up way further in the course, missing
-- many waypoints. The proper solution here would be to take the workarea into account as the tractor may
-- be well ahead of the turnEnd point in case of long implements. Instead we just assume 10 waypoints is
-- long enough.
for i = vehicle.cp.waypointIndex, math.min( vehicle.cp.waypointIndex + 10, vehicle.cp.numWaypoints ) do
if vehicle.cp.abortWork and vehicle.cp.abortWork == i then
vehicle.cp.abortWork = nil;
end;
local waypointToCeck = vehicle.Waypoints[i]
if not waypointToCeck.rev and not waypointToCeck.turnEnd then
local wpX, wpZ = waypointToCeck.cx, waypointToCeck.cz;
local _, _, disZ = worldToLocal(directionNode, wpX, getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wpX, 300, wpZ), wpZ);
local vX, _, vZ = localToWorld( directionNode, 0, 0, 0 )
courseplay:debug(('%s: getNextFwdPoint(), vX = %.1f, vZ = %.1f, i = %d, wpX = %.1f, wpZ = %.1f, disZ = %.1f '):format(nameNum(vehicle), vX, vZ, i, wpX, wpZ, disZ ), 14);
if disZ > 5 then
courseplay:debug(('--> return (%d) as waypointIndex'):format(i), 14);
return i;
end;
end;
end;
local ix = math.min(vehicle.cp.waypointIndex + 1, vehicle.cp.numWaypoints)
courseplay:debug(('\tno waypoint found in front of us, returning next waypoint (%d)'):format(ix), 14);
return ix
else
local maxVarianceX = sin(rad(30));
local firstFwd, firstFwdOver3;
courseplay:debug(('%s: getNextFwdPoint()'):format(nameNum(vehicle)), 13);
for i = vehicle.cp.waypointIndex, vehicle.cp.numWaypoints do
if not vehicle.Waypoints[i].rev then
local x, y, z = getWorldTranslation(directionNode);
local wdx, _, wdz, dist = courseplay:getWorldDirection(x, 0, z, vehicle.Waypoints[i].cx, 0, vehicle.Waypoints[i].cz);
local dx,_,dz = worldDirectionToLocal(directionNode, wdx, 0, wdz);
if not firstFwd then
firstFwd = i;
courseplay:debug(('--> set firstFwd as %d'):format(i), 13);
end;
if not firstFwdOver3 and dz and dist and dz * dist >= 3 then
firstFwdOver3 = i;
courseplay:debug(('--> set firstFwdOver3 as %d'):format(i), 13);
end;
courseplay:debug(('--> point %d, dx=%.4f, dz=%.4f, dist=%.2f, maxVarianceX=%.4f'):format(i, dx, dz, dist, maxVarianceX), 13);
if dz > 0 and abs(dx) <= maxVarianceX then -- forward and x angle <= 30°
courseplay:debug('----> return as waypointIndex', 13);
return i;
end;
end;
end;
if firstFwdOver3 then
courseplay:debug(('\treturn firstFwdOver3 (%d)'):format(firstFwdOver3), 13);
return firstFwdOver3;
elseif firstFwd then
courseplay:debug(('\treturn firstFwd (%d)'):format(firstFwd), 13);
return firstFwd;
end;
end;
courseplay:debug('\treturn 1', 13);
return 1;
end;
function courseplay:getReverseProperties(vehicle, workTool)
courseplay:debug(('getReverseProperties(%q, %q)'):format(nameNum(vehicle), nameNum(workTool)), 13);
-- Make sure they are reset so they wont conflict when changing worktools
workTool.cp.frontNode = nil;
workTool.cp.isPivot = nil;
if workTool == vehicle then
courseplay:debug('--> workTool is vehicle (steerable) -> return', 13);
return;
end;
if vehicle.cp.hasSpecializationShovel then
courseplay:debug('--> vehicle has "Shovel" spec -> return', 13);
return;
end;
if workTool.cp.hasSpecializationShovel then
courseplay:debug('--> workTool has "Shovel" spec -> return', 13);
return;
end;
if not courseplay:isWheeledWorkTool(workTool) or courseplay:isHookLift(workTool) or courseplay:isAttacherModule(workTool) then
courseplay:debug('--> workTool doesn\'t need reverse properties -> return', 13);
return;
end;
--------------------------------------------------
if not workTool.cp.distances then
workTool.cp.distances = courseplay:getDistances(workTool);
end;
workTool.cp.realTurningNode = courseplay:getRealTurningNode(workTool);
workTool.cp.realUnloadOrFillNode = courseplay:getRealUnloadOrFillNode(workTool);
if workTool.attacherVehicle == vehicle or vehicle.cp.isHookLiftTrailer or workTool.attacherVehicle.cp.isAttacherModule then
workTool.cp.frontNode = courseplay:getRealTrailerFrontNode(workTool);
else
workTool.cp.frontNode = courseplay:getRealDollyFrontNode(workTool.attacherVehicle);
if workTool.cp.frontNode then
courseplay:debug(string.format('--> workTool %q has dolly', nameNum(workTool)), 13);
else
courseplay:debug(string.format('--> workTool %q has invalid dolly -> return', nameNum(workTool)), 13);
return;
end;
end;
workTool.cp.nodeDistance = courseplay:getRealTrailerDistanceToPivot(workTool);
courseplay:debug("--> tz: "..tostring(workTool.cp.nodeDistance).." workTool.cp.realTurningNode: "..tostring(workTool.cp.realTurningNode), 13);
if workTool.cp.realTurningNode == workTool.cp.frontNode then
courseplay:debug('--> workTool.cp.realTurningNode == workTool.cp.frontNode', 13);
workTool.cp.isPivot = false;
else
workTool.cp.isPivot = true;
end;
courseplay:debug(('--> isPivot=%s, frontNode=%s'):format(tostring(workTool.cp.isPivot), tostring(workTool.cp.frontNode)), 13);
end;