Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into ChopperSupport
Browse files Browse the repository at this point in the history
  • Loading branch information
pops64 committed Sep 26, 2023
2 parents f738317 + a28758c commit 260ac47
Show file tree
Hide file tree
Showing 34 changed files with 211 additions and 58 deletions.
15 changes: 9 additions & 6 deletions config/MasterTranslations.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@
<Text language="de"><![CDATA[Keinen Abladepunkt gefunden.]]></Text>
<Text language="en"><![CDATA[No unloading point found.]]></Text>
</Translation>
<Translation name="CP_error_unload_target_to_far_away_from_silo">
<Text language="de"><![CDATA[Ausgewählter Abladepunkt ist zu weit vom Silo entfernt.]]></Text>
<Text language="en"><![CDATA[Chosen unloading point is to far away from the silo.]]></Text>
</Translation>
</Category>
<Category name="AI job parameters">
<Translation name="CP_jobParameters_fieldPosition_title">
Expand Down Expand Up @@ -2544,21 +2548,20 @@ The shovel to shred sugar beets is completly functional as well.
<Translation name="CP_help_page_shovelLoaderBasic_text">
<Text language="de"><![CDATA[
Um einen Radlader Fahrer zu starten, müssen zunächst über das Ziel Icon im HUD die Lade- und Entladepositionen gewählt werden.
Die Ladeposition wird wie auch bereits beim Lader gewählt. Ein blauer Ramen entsteht um den gefunden Haufen.
Die Ladeposition wird wie auch bereits beim Lader gewählt. Ein blauer Rahmen entsteht um den gefunden Haufen.
Die Abladeposition hängt davon ab, ob du in einen Anhänger oder in eine Abladestation entladen möchtest.
Abladen in einen Anhänger funktioniert automatisch und braucht keine zusätzliche Position.
Es wird der naheste, stehende Anhänger gewählt und seitlich angefahren.
Falls Abladen in einen Anhänger aktiviert ist, muss der Bereich, wo der Anhänger abgestellt wird auf der AI Karte ausgewählt werden.
Der Helfer fährt automatisch stehende Anhänger in diesem Bereich an. Die Richtung des Pfeils von der AI Karte spielt hier keine Rolle.
Möchtest du in eine Abladestation entladen, muss zunächst das Ziel umgestellt werden und anschließend der Marker auf den Trigger gesetzt werden.
]]></Text>
<Text language="en"><![CDATA[
To start a wheel loader helper, you need to set the load and unload positions by clicking the target icon on the hud.
The loading position works the same like the one from the loader mode. A blue square will be created arround the heap.
The unloading position depends if you want to unload into a trailer or into an unloading station.
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.
If unloading into the trailer is selected, then the area where the trailer will be parked needs to be selected on the AI Menu.
The helper will drive to any parked trailer in the area. The direction of the marker has no real meaning.
If you want to unload into an unloading station, you need to switch the target and then mark the trigger with the unloading position.
]]></Text>
</Translation>
Expand Down
2 changes: 1 addition & 1 deletion config/jobParameters/SiloLoaderJobParameterSetup.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<Text>unloadTrigger</Text>
</Texts>
</Setting>
<Setting classType="CpAIParameterPositionAngle" name="unloadPosition" positionParameterType="UNLOAD" isDisabled="isUnloadPositionDisabled"/>
<Setting classType="CpAIParameterPositionAngle" name="unloadPosition" positionParameterType="UNLOAD"/>
<Setting classType="CpAIParameterUnloadingStation" name="unloadStation" isDisabled="isUnloadStationDisabled" generateValuesFunction="generateUnloadingStations"></Setting>
</SettingSubTitle>
</Settings>
3 changes: 1 addition & 2 deletions modDesc.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no" ?>
<modDesc descVersion="76">
<version>7.3.0.6</version>
<version>7.3.1.2</version>
<author>Pops64</author>
<version>7.3.1.1</version>
<title>
<en>CoursePlay - Chopper Support</en>
<cs>自动作业 - Chopper Support</cs>
Expand Down
2 changes: 0 additions & 2 deletions scripts/ai/AIDriveStrategyDriveToFieldWorkStart.lua
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,6 @@ function AIDriveStrategyDriveToFieldWorkStart:onPathfindingDoneToCourseStart(pat
self:debug('Pathfinding to start fieldwork finished with %d waypoints (%d ms)',
#path, g_currentMission.time - (self.pathfindingStartedAt or 0))
courseToStart = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true)
-- make sure the course extends to the first waypoint
courseToStart:appendWaypoints({fieldWorkCourse:getWaypoint(ix)})
courseToStart:adjustForTowedImplements(2)
else
self:debug('Pathfinding to start fieldwork failed, using alignment course instead')
Expand Down
58 changes: 36 additions & 22 deletions scripts/ai/AIDriveStrategyShovelSiloLoader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ AIDriveStrategyShovelSiloLoader.myStates = {
REVERSING_AWAY_FROM_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0},
}

AIDriveStrategyShovelSiloLoader.maxValidTrailerDistanceToSiloFront = 30
AIDriveStrategyShovelSiloLoader.searchForTrailerDelaySec = 10
AIDriveStrategyShovelSiloLoader.distShovelTrailerPreUnload = 7
AIDriveStrategyShovelSiloLoader.distShovelUnloadStationPreUnload = 8
Expand Down Expand Up @@ -122,6 +121,7 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters)
--- to place the unload position node slightly in front.
local x, y, z = getWorldTranslation(self.unloadTrigger:getFillUnitExactFillRootNode(1))
setTranslation(self.unloadPositionNode, x, y, z)
---@type CpAIParameterPositionAngle
local position = jobParameters.unloadPosition
local dirX, dirZ = position:getDirection()
setDirection(self.unloadPositionNode, dirX, 0, dirZ, 0, 0, 1)
Expand All @@ -130,6 +130,11 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters)
setTranslation(self.unloadPositionNode, dx, dy, dz)
else
self:debug("Starting shovel silo to unload into trailer.")
---@type CpAIParameterPositionAngle
local position = jobParameters.unloadPosition
local _
_, self.trailerSearchArea = CpAIJobSiloLoader.getTrailerUnloadArea(position)

end
if self.bunkerSilo ~= nil then
self:debug("Bunker silo was found.")
Expand Down Expand Up @@ -333,10 +338,6 @@ end

function AIDriveStrategyShovelSiloLoader:update(dt)
if CpDebug:isChannelActive(CpDebug.DBG_SILO, self.vehicle) then
if self.siloFrontNode and self.state == self.states.WAITING_FOR_TRAILER then
DebugUtil.drawDebugCircleAtNode(self.siloFrontNode, self.maxValidTrailerDistanceToSiloFront,
math.ceil(self.maxValidTrailerDistanceToSiloFront), nil, false, {0, 3, 0})
end
if self.course:isTemporary() then
self.course:draw()
elseif self.ppc:getCourse():isTemporary() then
Expand Down Expand Up @@ -400,6 +401,22 @@ function AIDriveStrategyShovelSiloLoader:setNewState(newState)
self.state = newState
end

--- Checks if a valid target was found, which means either a trailer or a manure spreader.
---@param trailer table
---@return boolean
function AIDriveStrategyShovelSiloLoader:hasTrailerValidSpecializations(trailer)
if SpecializationUtil.hasSpecialization(Trailer, trailer.specializations) then
--- All normal trailers
return true
end
if SpecializationUtil.hasSpecialization(Sprayer, trailer.specializations)
and trailer.spec_sprayer.isManureSpreader then
--- Manure spreader
return true
end
return false
end

--- Is the trailer valid or not?
---@param trailer table
---@param trailerToIgnore table|nil
Expand All @@ -410,22 +427,23 @@ function AIDriveStrategyShovelSiloLoader:isValidTrailer(trailer, trailerToIgnore
self:debug("%s attached to: %s => %s", CpUtil.getName(trailer),
trailer.rootVehicle and CpUtil.getName(trailer.rootVehicle) or "no root vehicle", string.format(...))
end
if not SpecializationUtil.hasSpecialization(Trailer, trailer.specializations) then
if not self:hasTrailerValidSpecializations(trailer) then
debug("has not valid specializations setup")
return false
end
if trailer.rootVehicle and not AIUtil.isStopped(trailer.rootVehicle) then
self:debug("is not stopped!", CpUtil.getName(trailer))
debug("is not stopped!")
return false
end
if trailerToIgnore and table.hasElement(trailerToIgnore, trailer) then
debug("will be ignored!", CpUtil.getName(trailer))
debug("will be ignored!")
return false
end
local canLoad, fillUnitIndex, fillType, exactFillRootNode =
ImplementUtil.getCanLoadTo(trailer, self.shovelImplement,
nil, debug)
if not canLoad or exactFillRootNode == nil then
debug("can't be used!", CpUtil.getName(trailer))
debug("can't be used!")
return false
end
return true, { fillUnitIndex = fillUnitIndex,
Expand All @@ -442,12 +460,15 @@ function AIDriveStrategyShovelSiloLoader:getClosestTrailerAndDistance(trailerToI
local closestDistance = math.huge
local closestTrailerData = nil
for i, vehicle in pairs(g_currentMission.vehicles) do
local dist = calcDistanceFrom(vehicle.rootNode, self.siloFrontNode)
if dist < closestDistance then
local valid, trailerData = self:isValidTrailer(vehicle, trailerToIgnore)
if valid then
closestDistance = dist
closestTrailerData = trailerData
local x, _, z = getWorldTranslation(vehicle.rootNode)
if CpMathUtil.isPointInPolygon(self.trailerSearchArea, x, z ) then
local dist = calcDistanceFrom(vehicle.rootNode, self.siloFrontNode)
if dist < closestDistance then
local valid, trailerData = self:isValidTrailer(vehicle, trailerToIgnore)
if valid then
closestDistance = dist
closestTrailerData = trailerData
end
end
end
end
Expand All @@ -464,13 +485,6 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto()
return
end
local trailer = trailerData.trailer
if dist > self.maxValidTrailerDistanceToSiloFront then
self:debug("Closest Trailer %s attached to %s with the distance %.2fm/%.2fm found is to far away!",
CpUtil.getName(trailer), trailer.rootVehicle and CpUtil.getName(trailer.rootVehicle) or "no root vehicle",
dist, self.maxValidTrailerDistanceToSiloFront)
self:setInfoText(InfoTextManager.WAITING_FOR_UNLOADER)
return
end
self:clearInfoText(InfoTextManager.WAITING_FOR_UNLOADER)
--- Sets the unload position node in front of the closest side of the trailer.
self:debug("Found a valid trailer %s within distance %.2f", CpUtil.getName(trailer), dist)
Expand Down
11 changes: 11 additions & 0 deletions scripts/ai/jobs/CpAIJob.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function CpAIJob.new(isServer, customMt)

self:setupJobParameters()

self.debugChannel = CpDebug.DBG_FIELDWORK
return self
end

Expand Down Expand Up @@ -347,6 +348,16 @@ function CpAIJob:getCanGenerateFieldWorkCourse()
return false
end

function CpAIJob:debug(...)
local vehicle = self:getVehicle()
if vehicle then
CpUtil.debugVehicle(self.debugChannel, vehicle, ...)
else
CpUtil.debugFormat(self.debugChannel, ...)
end
end


--- Ugly hack to fix a mp problem from giants, where the job class can not be found.
function CpAIJob.getJobTypeIndex(aiJobTypeManager, superFunc, job)
local ret = superFunc(aiJobTypeManager, job)
Expand Down
106 changes: 105 additions & 1 deletion scripts/ai/jobs/CpAIJobSiloLoader.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
--- AI Job for silo loader like the ropa maus or wheel loaders.
---@class CpAIJobSiloLoader : CpAIJob
---@field heapPlot HeapPlot
---@field trailerAreaPlot HeapPlot
---@field heapNode number
CpAIJobSiloLoader = {
name = "SILO_LOADER_CP",
Expand All @@ -9,15 +10,26 @@ CpAIJobSiloLoader = {
}
local AIJobCombineUnloaderCp_mt = Class(CpAIJobSiloLoader, CpAIJob)

--- Trailer unload marker length, -TRAILER_SEARCH_LENGTH/2 to TRAILER_SEARCH_LENGTH/2
CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH = 25
--- Trailer unload marker width, -TRAILER_SEARCH_WIDTH/2 to TRAILER_SEARCH_WIDTH/2
CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH = 20
--- Max distance the trailer unload spot can be from the silo/heap.
CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO = 180

function CpAIJobSiloLoader.new(isServer, customMt)
local self = CpAIJob.new(isServer, customMt or AIJobCombineUnloaderCp_mt)

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

self.trailerAreaPlot = HeapPlot(g_currentMission.inGameMenu.ingameMap)


self.heapNode = CpUtil.createNode("siloNode", 0, 0, 0, nil)
self.heap = nil
self.hasValidPosition = false
self.debugChannel = CpDebug.DBG_SILO
return self
end

Expand Down Expand Up @@ -101,6 +113,7 @@ end
--- Called when parameters change, scan field
function CpAIJobSiloLoader:validate(farmId)
self.heapPlot:setVisible(false)
self.trailerAreaPlot:setVisible(false)
self.heap = nil
self.bunkerSilo = nil
self.unloadStation = nil
Expand Down Expand Up @@ -134,7 +147,7 @@ function CpAIJobSiloLoader:validate(farmId)
if not AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) then
if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then
--- Validates the unload trigger setup
local found, unloadTrigger, unloadStation = self:getUnloadTriggerAt(self.cpJobParameters.unloadPosition)
local found, unloadTrigger, unloadStation, validDistanceToSilo = self:getUnloadTriggerAt(self.cpJobParameters.unloadPosition)
if found then
self.unloadStation = unloadStation
self.unloadTrigger = unloadTrigger
Expand All @@ -156,11 +169,81 @@ function CpAIJobSiloLoader:validate(farmId)
if unloadPosition.x == nil or unloadPosition.angle == nil then
return false, g_i18n:getText("CP_error_no_unload_trigger_found")
end
if not validDistanceToSilo then
return false, g_i18n:getText("CP_error_unload_target_to_far_away_from_silo")
end
else
local found, area, validDistanceToSilo = CpAIJobSiloLoader.getTrailerUnloadArea(
self.cpJobParameters.unloadPosition, self.bunkerSilo or self.heap)
if found then
self.trailerAreaPlot:setVisible(true)
self.trailerAreaPlot:setArea(area)
end
if not validDistanceToSilo then
return false, g_i18n:getText("CP_error_unload_target_to_far_away_from_silo")
end
end
end
return isValid, errorMessage
end

--- Gets the area to search for trailers
--- and optional check if the trailer area is close enough to the silo
---@param position CpAIParameterPositionAngle
---@param silo CpSilo|nil
---@return boolean found?
---@return table area
---@return boolean distance to silo is valid
function CpAIJobSiloLoader.getTrailerUnloadArea(position, silo)
local x, z = position:getPosition()
local dirX, dirZ = position:getDirection()
if x == nil or dirX == nil then
return false, {}, false
end
--- Rotation matrix to rotate Z directions to x directions
local dirX2 = dirX * math.cos(math.pi/2) - dirZ * math.sin(math.pi/2)
local dirZ2 = dirX * math.sin(math.pi/2) + dirZ * math.cos(math.pi/2)
--- Creates a rectangle for the trailer unload area
local area = {
{
x = x + dirX * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirX2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2,
z = z + dirZ * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirZ2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2
},
{
x = x + dirX * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 - dirX2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2,
z = z + dirZ * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 - dirZ2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2
},
{
x = x - dirX * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 - dirX2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2,
z = z - dirZ * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 - dirZ2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2
},
{
x = x - dirX * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirX2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2,
z = z - dirZ * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirZ2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2
},
{
x = x + dirX * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirX2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2,
z = z + dirZ * CpAIJobSiloLoader.TRAILER_SEARCH_LENGTH/2 + dirZ2 * CpAIJobSiloLoader.TRAILER_SEARCH_WIDTH/2
},
}
if silo then
--- Checks if the distance between the front or back of the bunker silo/heap
--- to the trailer unload area marker is which the max limit.
local fx, fz = silo:getFrontCenter()
local bx, bz = silo:getBackCenter()
local dist1 = MathUtil.vector2Length(x-fx, z-fz)
local dist2 = MathUtil.vector2Length(x-bx, z-bz)
CpUtil.debugFormat(CpDebug.DBG_SILO, "Trailer marker is %.1fm/%.1fm away from the silo",
math.min(dist1, dist2), CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO)
if dist1 > CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO and
dist2 > CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO then
--- Trailer unload area is to far away from the silo
return true, area, false
end
end
return true, area, true
end

--- Gets the bunker silo or heap at the loading position in that order.
---@param loadPosition CpAIParameterPositionAngle
---@param node number
Expand All @@ -185,10 +268,15 @@ function CpAIJobSiloLoader:getBunkerSiloOrHeap(loadPosition, node)
end

--- Gets the unload trigger at the unload position.
--- Checks for the correct fill type
--- between the silo and the unload target.
--- Also checks if the unloading target
--- is close enough to the silo.
---@param unloadPosition CpAIParameterPositionAngle
---@return boolean found?
---@return table|nil Trigger
---@return table|nil unloadStation
---@return boolean|nil distance is close enough to the bunker silo/heap
function CpAIJobSiloLoader:getUnloadTriggerAt(unloadPosition)
local x, z = unloadPosition:getPosition()
local dirX, dirZ = unloadPosition:getDirection()
Expand Down Expand Up @@ -217,6 +305,19 @@ function CpAIJobSiloLoader:getUnloadTriggerAt(unloadPosition)
end
end
end
local fx, fz = silo:getFrontCenter()
local bx, bz = silo:getBackCenter()
--- Checks the distance of the unloading station to the bunker silo/heap
local dist1 = MathUtil.vector2Length(x-fx, z-fz)
local dist2 = MathUtil.vector2Length(x-bx, z-bz)
self:debug("Unloading trigger: %s is %.1fm/%.1fm away from the silo",
CpUtil.getName(station), math.min(dist1, dist2),
self.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO)
if dist1 < CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO or
dist2 < CpAIJobSiloLoader.MAX_UNLOAD_TARGET_DISTANCE_FROM_SILO then
--- Unloading point is close enough to the bunker silo.
return found, trigger, station, true
end
return found, trigger, station
end

Expand All @@ -230,6 +331,9 @@ function CpAIJobSiloLoader:drawSilos(map)
table.insert(fillTypes, silo:getFillType())
end
g_triggerManager:drawDischargeableTriggers(map, self.unloadTrigger, fillTypes)
else
--- Drawing trailer area
self.trailerAreaPlot:draw(map)
end
end

Expand Down
Loading

0 comments on commit 260ac47

Please sign in to comment.