Skip to content

Commit 260ac47

Browse files
committed
Merge remote-tracking branch 'upstream/main' into ChopperSupport
2 parents f738317 + a28758c commit 260ac47

34 files changed

+211
-58
lines changed

config/MasterTranslations.xml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@
8383
<Text language="de"><![CDATA[Keinen Abladepunkt gefunden.]]></Text>
8484
<Text language="en"><![CDATA[No unloading point found.]]></Text>
8585
</Translation>
86+
<Translation name="CP_error_unload_target_to_far_away_from_silo">
87+
<Text language="de"><![CDATA[Ausgewählter Abladepunkt ist zu weit vom Silo entfernt.]]></Text>
88+
<Text language="en"><![CDATA[Chosen unloading point is to far away from the silo.]]></Text>
89+
</Translation>
8690
</Category>
8791
<Category name="AI job parameters">
8892
<Translation name="CP_jobParameters_fieldPosition_title">
@@ -2544,21 +2548,20 @@ The shovel to shred sugar beets is completly functional as well.
25442548
<Translation name="CP_help_page_shovelLoaderBasic_text">
25452549
<Text language="de"><![CDATA[
25462550
Um einen Radlader Fahrer zu starten, müssen zunächst über das Ziel Icon im HUD die Lade- und Entladepositionen gewählt werden.
2547-
Die Ladeposition wird wie auch bereits beim Lader gewählt. Ein blauer Ramen entsteht um den gefunden Haufen.
2551+
Die Ladeposition wird wie auch bereits beim Lader gewählt. Ein blauer Rahmen entsteht um den gefunden Haufen.
25482552
25492553
Die Abladeposition hängt davon ab, ob du in einen Anhänger oder in eine Abladestation entladen möchtest.
2550-
Abladen in einen Anhänger funktioniert automatisch und braucht keine zusätzliche Position.
2551-
Es wird der naheste, stehende Anhänger gewählt und seitlich angefahren.
2552-
2554+
Falls Abladen in einen Anhänger aktiviert ist, muss der Bereich, wo der Anhänger abgestellt wird auf der AI Karte ausgewählt werden.
2555+
Der Helfer fährt automatisch stehende Anhänger in diesem Bereich an. Die Richtung des Pfeils von der AI Karte spielt hier keine Rolle.
25532556
Möchtest du in eine Abladestation entladen, muss zunächst das Ziel umgestellt werden und anschließend der Marker auf den Trigger gesetzt werden.
25542557
]]></Text>
25552558
<Text language="en"><![CDATA[
25562559
To start a wheel loader helper, you need to set the load and unload positions by clicking the target icon on the hud.
25572560
The loading position works the same like the one from the loader mode. A blue square will be created arround the heap.
25582561
25592562
The unloading position depends if you want to unload into a trailer or into an unloading station.
2560-
Unloading into a trailer works automatically and doesn't need a dedicated unload position. The driver will check for the nearest standing trailer and unloads to it from the side.
2561-
2563+
If unloading into the trailer is selected, then the area where the trailer will be parked needs to be selected on the AI Menu.
2564+
The helper will drive to any parked trailer in the area. The direction of the marker has no real meaning.
25622565
If you want to unload into an unloading station, you need to switch the target and then mark the trigger with the unloading position.
25632566
]]></Text>
25642567
</Translation>

config/jobParameters/SiloLoaderJobParameterSetup.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<Text>unloadTrigger</Text>
2424
</Texts>
2525
</Setting>
26-
<Setting classType="CpAIParameterPositionAngle" name="unloadPosition" positionParameterType="UNLOAD" isDisabled="isUnloadPositionDisabled"/>
26+
<Setting classType="CpAIParameterPositionAngle" name="unloadPosition" positionParameterType="UNLOAD"/>
2727
<Setting classType="CpAIParameterUnloadingStation" name="unloadStation" isDisabled="isUnloadStationDisabled" generateValuesFunction="generateUnloadingStations"></Setting>
2828
</SettingSubTitle>
2929
</Settings>

modDesc.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<?xml version="1.0" encoding="utf-8" standalone="no" ?>
22
<modDesc descVersion="76">
3-
<version>7.3.0.6</version>
3+
<version>7.3.1.2</version>
44
<author>Pops64</author>
5-
<version>7.3.1.1</version>
65
<title>
76
<en>CoursePlay - Chopper Support</en>
87
<cs>自动作业 - Chopper Support</cs>

scripts/ai/AIDriveStrategyDriveToFieldWorkStart.lua

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,6 @@ function AIDriveStrategyDriveToFieldWorkStart:onPathfindingDoneToCourseStart(pat
210210
self:debug('Pathfinding to start fieldwork finished with %d waypoints (%d ms)',
211211
#path, g_currentMission.time - (self.pathfindingStartedAt or 0))
212212
courseToStart = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true)
213-
-- make sure the course extends to the first waypoint
214-
courseToStart:appendWaypoints({fieldWorkCourse:getWaypoint(ix)})
215213
courseToStart:adjustForTowedImplements(2)
216214
else
217215
self:debug('Pathfinding to start fieldwork failed, using alignment course instead')

scripts/ai/AIDriveStrategyShovelSiloLoader.lua

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ AIDriveStrategyShovelSiloLoader.myStates = {
5858
REVERSING_AWAY_FROM_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0},
5959
}
6060

61-
AIDriveStrategyShovelSiloLoader.maxValidTrailerDistanceToSiloFront = 30
6261
AIDriveStrategyShovelSiloLoader.searchForTrailerDelaySec = 10
6362
AIDriveStrategyShovelSiloLoader.distShovelTrailerPreUnload = 7
6463
AIDriveStrategyShovelSiloLoader.distShovelUnloadStationPreUnload = 8
@@ -122,6 +121,7 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters)
122121
--- to place the unload position node slightly in front.
123122
local x, y, z = getWorldTranslation(self.unloadTrigger:getFillUnitExactFillRootNode(1))
124123
setTranslation(self.unloadPositionNode, x, y, z)
124+
---@type CpAIParameterPositionAngle
125125
local position = jobParameters.unloadPosition
126126
local dirX, dirZ = position:getDirection()
127127
setDirection(self.unloadPositionNode, dirX, 0, dirZ, 0, 0, 1)
@@ -130,6 +130,11 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters)
130130
setTranslation(self.unloadPositionNode, dx, dy, dz)
131131
else
132132
self:debug("Starting shovel silo to unload into trailer.")
133+
---@type CpAIParameterPositionAngle
134+
local position = jobParameters.unloadPosition
135+
local _
136+
_, self.trailerSearchArea = CpAIJobSiloLoader.getTrailerUnloadArea(position)
137+
133138
end
134139
if self.bunkerSilo ~= nil then
135140
self:debug("Bunker silo was found.")
@@ -333,10 +338,6 @@ end
333338

334339
function AIDriveStrategyShovelSiloLoader:update(dt)
335340
if CpDebug:isChannelActive(CpDebug.DBG_SILO, self.vehicle) then
336-
if self.siloFrontNode and self.state == self.states.WAITING_FOR_TRAILER then
337-
DebugUtil.drawDebugCircleAtNode(self.siloFrontNode, self.maxValidTrailerDistanceToSiloFront,
338-
math.ceil(self.maxValidTrailerDistanceToSiloFront), nil, false, {0, 3, 0})
339-
end
340341
if self.course:isTemporary() then
341342
self.course:draw()
342343
elseif self.ppc:getCourse():isTemporary() then
@@ -400,6 +401,22 @@ function AIDriveStrategyShovelSiloLoader:setNewState(newState)
400401
self.state = newState
401402
end
402403

404+
--- Checks if a valid target was found, which means either a trailer or a manure spreader.
405+
---@param trailer table
406+
---@return boolean
407+
function AIDriveStrategyShovelSiloLoader:hasTrailerValidSpecializations(trailer)
408+
if SpecializationUtil.hasSpecialization(Trailer, trailer.specializations) then
409+
--- All normal trailers
410+
return true
411+
end
412+
if SpecializationUtil.hasSpecialization(Sprayer, trailer.specializations)
413+
and trailer.spec_sprayer.isManureSpreader then
414+
--- Manure spreader
415+
return true
416+
end
417+
return false
418+
end
419+
403420
--- Is the trailer valid or not?
404421
---@param trailer table
405422
---@param trailerToIgnore table|nil
@@ -410,22 +427,23 @@ function AIDriveStrategyShovelSiloLoader:isValidTrailer(trailer, trailerToIgnore
410427
self:debug("%s attached to: %s => %s", CpUtil.getName(trailer),
411428
trailer.rootVehicle and CpUtil.getName(trailer.rootVehicle) or "no root vehicle", string.format(...))
412429
end
413-
if not SpecializationUtil.hasSpecialization(Trailer, trailer.specializations) then
430+
if not self:hasTrailerValidSpecializations(trailer) then
431+
debug("has not valid specializations setup")
414432
return false
415433
end
416434
if trailer.rootVehicle and not AIUtil.isStopped(trailer.rootVehicle) then
417-
self:debug("is not stopped!", CpUtil.getName(trailer))
435+
debug("is not stopped!")
418436
return false
419437
end
420438
if trailerToIgnore and table.hasElement(trailerToIgnore, trailer) then
421-
debug("will be ignored!", CpUtil.getName(trailer))
439+
debug("will be ignored!")
422440
return false
423441
end
424442
local canLoad, fillUnitIndex, fillType, exactFillRootNode =
425443
ImplementUtil.getCanLoadTo(trailer, self.shovelImplement,
426444
nil, debug)
427445
if not canLoad or exactFillRootNode == nil then
428-
debug("can't be used!", CpUtil.getName(trailer))
446+
debug("can't be used!")
429447
return false
430448
end
431449
return true, { fillUnitIndex = fillUnitIndex,
@@ -442,12 +460,15 @@ function AIDriveStrategyShovelSiloLoader:getClosestTrailerAndDistance(trailerToI
442460
local closestDistance = math.huge
443461
local closestTrailerData = nil
444462
for i, vehicle in pairs(g_currentMission.vehicles) do
445-
local dist = calcDistanceFrom(vehicle.rootNode, self.siloFrontNode)
446-
if dist < closestDistance then
447-
local valid, trailerData = self:isValidTrailer(vehicle, trailerToIgnore)
448-
if valid then
449-
closestDistance = dist
450-
closestTrailerData = trailerData
463+
local x, _, z = getWorldTranslation(vehicle.rootNode)
464+
if CpMathUtil.isPointInPolygon(self.trailerSearchArea, x, z ) then
465+
local dist = calcDistanceFrom(vehicle.rootNode, self.siloFrontNode)
466+
if dist < closestDistance then
467+
local valid, trailerData = self:isValidTrailer(vehicle, trailerToIgnore)
468+
if valid then
469+
closestDistance = dist
470+
closestTrailerData = trailerData
471+
end
451472
end
452473
end
453474
end
@@ -464,13 +485,6 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto()
464485
return
465486
end
466487
local trailer = trailerData.trailer
467-
if dist > self.maxValidTrailerDistanceToSiloFront then
468-
self:debug("Closest Trailer %s attached to %s with the distance %.2fm/%.2fm found is to far away!",
469-
CpUtil.getName(trailer), trailer.rootVehicle and CpUtil.getName(trailer.rootVehicle) or "no root vehicle",
470-
dist, self.maxValidTrailerDistanceToSiloFront)
471-
self:setInfoText(InfoTextManager.WAITING_FOR_UNLOADER)
472-
return
473-
end
474488
self:clearInfoText(InfoTextManager.WAITING_FOR_UNLOADER)
475489
--- Sets the unload position node in front of the closest side of the trailer.
476490
self:debug("Found a valid trailer %s within distance %.2f", CpUtil.getName(trailer), dist)

scripts/ai/jobs/CpAIJob.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ function CpAIJob.new(isServer, customMt)
2626

2727
self:setupJobParameters()
2828

29+
self.debugChannel = CpDebug.DBG_FIELDWORK
2930
return self
3031
end
3132

@@ -347,6 +348,16 @@ function CpAIJob:getCanGenerateFieldWorkCourse()
347348
return false
348349
end
349350

351+
function CpAIJob:debug(...)
352+
local vehicle = self:getVehicle()
353+
if vehicle then
354+
CpUtil.debugVehicle(self.debugChannel, vehicle, ...)
355+
else
356+
CpUtil.debugFormat(self.debugChannel, ...)
357+
end
358+
end
359+
360+
350361
--- Ugly hack to fix a mp problem from giants, where the job class can not be found.
351362
function CpAIJob.getJobTypeIndex(aiJobTypeManager, superFunc, job)
352363
local ret = superFunc(aiJobTypeManager, job)

scripts/ai/jobs/CpAIJobSiloLoader.lua

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
--- AI Job for silo loader like the ropa maus or wheel loaders.
22
---@class CpAIJobSiloLoader : CpAIJob
33
---@field heapPlot HeapPlot
4+
---@field trailerAreaPlot HeapPlot
45
---@field heapNode number
56
CpAIJobSiloLoader = {
67
name = "SILO_LOADER_CP",
@@ -9,15 +10,26 @@ CpAIJobSiloLoader = {
910
}
1011
local AIJobCombineUnloaderCp_mt = Class(CpAIJobSiloLoader, CpAIJob)
1112

13+
--- Trailer unload marker length, -TRAILER_SEARCH_LENGTH/2 to TRAILER_SEARCH_LENGTH/2
14+
CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH = 25
15+
--- Trailer unload marker width, -TRAILER_SEARCH_WIDTH/2 to TRAILER_SEARCH_WIDTH/2
16+
CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH = 20
17+
--- Max distance the trailer unload spot can be from the silo/heap.
18+
CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO = 180
19+
1220
function CpAIJobSiloLoader.new(isServer, customMt)
1321
local self = CpAIJob.new(isServer, customMt or AIJobCombineUnloaderCp_mt)
1422

1523
self.heapPlot = HeapPlot(g_currentMission.inGameMenu.ingameMap)
1624
self.heapPlot:setVisible(false)
1725

26+
self.trailerAreaPlot = HeapPlot(g_currentMission.inGameMenu.ingameMap)
27+
28+
1829
self.heapNode = CpUtil.createNode("siloNode", 0, 0, 0, nil)
1930
self.heap = nil
2031
self.hasValidPosition = false
32+
self.debugChannel = CpDebug.DBG_SILO
2133
return self
2234
end
2335

@@ -101,6 +113,7 @@ end
101113
--- Called when parameters change, scan field
102114
function CpAIJobSiloLoader:validate(farmId)
103115
self.heapPlot:setVisible(false)
116+
self.trailerAreaPlot:setVisible(false)
104117
self.heap = nil
105118
self.bunkerSilo = nil
106119
self.unloadStation = nil
@@ -134,7 +147,7 @@ function CpAIJobSiloLoader:validate(farmId)
134147
if not AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) then
135148
if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then
136149
--- Validates the unload trigger setup
137-
local found, unloadTrigger, unloadStation = self:getUnloadTriggerAt(self.cpJobParameters.unloadPosition)
150+
local found, unloadTrigger, unloadStation, validDistanceToSilo = self:getUnloadTriggerAt(self.cpJobParameters.unloadPosition)
138151
if found then
139152
self.unloadStation = unloadStation
140153
self.unloadTrigger = unloadTrigger
@@ -156,11 +169,81 @@ function CpAIJobSiloLoader:validate(farmId)
156169
if unloadPosition.x == nil or unloadPosition.angle == nil then
157170
return false, g_i18n:getText("CP_error_no_unload_trigger_found")
158171
end
172+
if not validDistanceToSilo then
173+
return false, g_i18n:getText("CP_error_unload_target_to_far_away_from_silo")
174+
end
175+
else
176+
local found, area, validDistanceToSilo = CpAIJobSiloLoader.getTrailerUnloadArea(
177+
self.cpJobParameters.unloadPosition, self.bunkerSilo or self.heap)
178+
if found then
179+
self.trailerAreaPlot:setVisible(true)
180+
self.trailerAreaPlot:setArea(area)
181+
end
182+
if not validDistanceToSilo then
183+
return false, g_i18n:getText("CP_error_unload_target_to_far_away_from_silo")
184+
end
159185
end
160186
end
161187
return isValid, errorMessage
162188
end
163189

190+
--- Gets the area to search for trailers
191+
--- and optional check if the trailer area is close enough to the silo
192+
---@param position CpAIParameterPositionAngle
193+
---@param silo CpSilo|nil
194+
---@return boolean found?
195+
---@return table area
196+
---@return boolean distance to silo is valid
197+
function CpAIJobSiloLoader.getTrailerUnloadArea(position, silo)
198+
local x, z = position:getPosition()
199+
local dirX, dirZ = position:getDirection()
200+
if x == nil or dirX == nil then
201+
return false, {}, false
202+
end
203+
--- Rotation matrix to rotate Z directions to x directions
204+
local dirX2 = dirX * math.cos(math.pi/2) - dirZ * math.sin(math.pi/2)
205+
local dirZ2 = dirX * math.sin(math.pi/2) + dirZ * math.cos(math.pi/2)
206+
--- Creates a rectangle for the trailer unload area
207+
local area = {
208+
{
209+
x = x + dirX * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirX2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2,
210+
z = z + dirZ * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirZ2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2
211+
},
212+
{
213+
x = x + dirX * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 - dirX2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2,
214+
z = z + dirZ * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 - dirZ2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2
215+
},
216+
{
217+
x = x - dirX * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 - dirX2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2,
218+
z = z - dirZ * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 - dirZ2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2
219+
},
220+
{
221+
x = x - dirX * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirX2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2,
222+
z = z - dirZ * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirZ2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2
223+
},
224+
{
225+
x = x + dirX * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirX2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2,
226+
z = z + dirZ * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirZ2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2
227+
},
228+
}
229+
if silo then
230+
--- Checks if the distance between the front or back of the bunker silo/heap
231+
--- to the trailer unload area marker is which the max limit.
232+
local fx, fz = silo:getFrontCenter()
233+
local bx, bz = silo:getBackCenter()
234+
local dist1 = MathUtil.vector2Length(x-fx, z-fz)
235+
local dist2 = MathUtil.vector2Length(x-bx, z-bz)
236+
CpUtil.debugFormat(CpDebug.DBG_SILO, "Trailer marker is %.1fm/%.1fm away from the silo",
237+
math.min(dist1, dist2), CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO)
238+
if dist1 > CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO and
239+
dist2 > CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO then
240+
--- Trailer unload area is to far away from the silo
241+
return true, area, false
242+
end
243+
end
244+
return true, area, true
245+
end
246+
164247
--- Gets the bunker silo or heap at the loading position in that order.
165248
---@param loadPosition CpAIParameterPositionAngle
166249
---@param node number
@@ -185,10 +268,15 @@ function CpAIJobSiloLoader:getBunkerSiloOrHeap(loadPosition, node)
185268
end
186269

187270
--- Gets the unload trigger at the unload position.
271+
--- Checks for the correct fill type
272+
--- between the silo and the unload target.
273+
--- Also checks if the unloading target
274+
--- is close enough to the silo.
188275
---@param unloadPosition CpAIParameterPositionAngle
189276
---@return boolean found?
190277
---@return table|nil Trigger
191278
---@return table|nil unloadStation
279+
---@return boolean|nil distance is close enough to the bunker silo/heap
192280
function CpAIJobSiloLoader:getUnloadTriggerAt(unloadPosition)
193281
local x, z = unloadPosition:getPosition()
194282
local dirX, dirZ = unloadPosition:getDirection()
@@ -217,6 +305,19 @@ function CpAIJobSiloLoader:getUnloadTriggerAt(unloadPosition)
217305
end
218306
end
219307
end
308+
local fx, fz = silo:getFrontCenter()
309+
local bx, bz = silo:getBackCenter()
310+
--- Checks the distance of the unloading station to the bunker silo/heap
311+
local dist1 = MathUtil.vector2Length(x-fx, z-fz)
312+
local dist2 = MathUtil.vector2Length(x-bx, z-bz)
313+
self:debug("Unloading trigger: %s is %.1fm/%.1fm away from the silo",
314+
CpUtil.getName(station), math.min(dist1, dist2),
315+
self.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO)
316+
if dist1 < CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO or
317+
dist2 < CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO then
318+
--- Unloading point is close enough to the bunker silo.
319+
return found, trigger, station, true
320+
end
220321
return found, trigger, station
221322
end
222323

@@ -230,6 +331,9 @@ function CpAIJobSiloLoader:drawSilos(map)
230331
table.insert(fillTypes, silo:getFillType())
231332
end
232333
g_triggerManager:drawDischargeableTriggers(map, self.unloadTrigger, fillTypes)
334+
else
335+
--- Drawing trailer area
336+
self.trailerAreaPlot:draw(map)
233337
end
234338
end
235339

0 commit comments

Comments
 (0)