Skip to content

Commit

Permalink
Merge pull request #144 from Courseplay/71-ballen-wickler
Browse files Browse the repository at this point in the history
fix: find bales in missions
  • Loading branch information
Tensuko authored Dec 27, 2024
2 parents a1cfc33 + 76aee43 commit 7afa211
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 52 deletions.
6 changes: 4 additions & 2 deletions config/VehicleConfigurations.xml
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ You can define the following custom settings:
movingToolIx = "1"
workingWidth ="10.2"
/>

<!--\vehicles\kuhn-->
<Vehicle name="sw4014.xml"
toolOffsetX = "-2.5"
Expand Down Expand Up @@ -377,7 +376,10 @@ You can define the following custom settings:
turnRadius = "5"
useVehicleSizeForMarkers = "true"
/>

<!--\vehicles\goeweil-->
<Vehicle name="g5020.xml"
baleCollectorOffset = "1.8"
/>
<!--\vehicles\samsonAgro-->
<Vehicle name="us235.xml"
workingWidth = "24"
Expand Down
4 changes: 3 additions & 1 deletion scripts/ai/BaleToCollect.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ end
---@param baleWrapper table bale wrapper, if exists
---@param baleLoader table bale loader, if exists
---@param baleWrapType number bale wrap type for the bale loader
---@return boolean, boolean isValid,
function BaleToCollect.isValidBale(object, baleWrapper, baleLoader, baleWrapType)
-- nodeId is sometimes 0, causing issues for the BaleToCollect constructor
if object.isa and object:isa(Bale) and object.nodeId and entityExists(object.nodeId) then
if baleWrapper then
-- if there is a bale wrapper, the bale must be wrappable
return baleWrapper:getIsBaleWrappable(object)
local wrappable, sizeMatch = baleWrapper:getIsBaleWrappable(object)
return wrappable
elseif baleLoader and baleLoader.getBaleTypeByBale then
local baleType = baleLoader:getBaleTypeByBale(object)
local spec = baleLoader.spec_baleLoader
Expand Down
86 changes: 45 additions & 41 deletions scripts/ai/strategies/AIDriveStrategyFindBales.lua
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ end
--- Wait for the giants bale loader to finish grabbing the bale.
function AIDriveStrategyFindBales:isReadyToLoadNextBale()
local isGrabbingBale = false
for i, controller in pairs(self.controllers) do
if controller.isGrabbingBale then
for i, controller in pairs(self.controllers) do
if controller.isGrabbingBale then
isGrabbingBale = isGrabbingBale or controller:isGrabbingBale()
end
end
Expand All @@ -119,8 +119,8 @@ end
--- Have any bales been loaded?
function AIDriveStrategyFindBales:hasBalesLoaded()
local hasBales = false
for i, controller in pairs(self.controllers) do
if controller.hasBales then
for i, controller in pairs(self.controllers) do
if controller.hasBales then
hasBales = hasBales or controller:hasBales()
end
end
Expand All @@ -130,8 +130,8 @@ end
--- Can all bale loaders be folded?
function AIDriveStrategyFindBales:isReadyToFoldImplements()
local canBeFolded = true
for i, controller in pairs(self.controllers) do
if controller.canBeFolded then
for i, controller in pairs(self.controllers) do
if controller.canBeFolded then
canBeFolded = canBeFolded and controller:canBeFolded()
end
end
Expand All @@ -140,8 +140,8 @@ end

function AIDriveStrategyFindBales:areBaleLoadersFull()
local allBaleLoadersFilled = self.baleLoader ~= nil
for i, controller in pairs(self.controllers) do
if controller.isFull then
for i, controller in pairs(self.controllers) do
if controller.isFull then
allBaleLoadersFilled = allBaleLoadersFilled and controller:isFull()
end
end
Expand All @@ -152,15 +152,15 @@ function AIDriveStrategyFindBales:getBalesToIgnore()
local objectsToIgnore = {}
if self.lastBale then
return { self.lastBale }
elseif self.baleLoaderController then
elseif self.baleLoaderController then
return self.baleLoaderController:getBalesToIgnore()
else
for i, controller in pairs(self.controllers) do
if controller.getBalesToIgnore then
for i, bale in pairs(controller:getBalesToIgnore()) do
for i, controller in pairs(self.controllers) do
if controller.getBalesToIgnore then
for i, bale in pairs(controller:getBalesToIgnore()) do
table.insert(objectsToIgnore, bale)
end

end
end
end
Expand All @@ -187,7 +187,7 @@ function AIDriveStrategyFindBales:setFieldPolygon(fieldPolygon)
self.fieldPolygon = fieldPolygon
end

--- Bale wrap type for the bale loader.
--- Bale wrap type for the bale loader.
function AIDriveStrategyFindBales:setAIVehicle(vehicle, jobParameters)
AIDriveStrategyCourse.setAIVehicle(self, vehicle, jobParameters)
self.baleWrapType = jobParameters.baleWrapType:getValue()
Expand Down Expand Up @@ -224,12 +224,13 @@ end
function AIDriveStrategyFindBales:findBales()
local balesFound, baleWithWrongWrapType = {}, false
for _, object in pairs(g_baleToCollectManager:getBales()) do
local isValid, wrongWrapType = BaleToCollect.isValidBale(object,
local isValid, wrongWrapType = BaleToCollect.isValidBale(object,
self.baleWrapper, self.baleLoader, self.baleWrapType)
if isValid and g_baleToCollectManager:isValidBale(object) then
local bale = BaleToCollect(object)
-- if the bale has a mountObject it is already on the loader so ignore it
if not object.mountObject and object:getOwnerFarmId() == self.vehicle:getOwnerFarmId() and
if not object.mountObject and
g_currentMission.accessHandler:canFarmAccess(self.vehicle:getOwnerFarmId(), object) and
self:isBaleOnField(bale) then
-- bales may .have multiple nodes, using the object.id deduplicates the list
balesFound[object.id] = bale
Expand All @@ -253,7 +254,7 @@ end
---@return number|nil distance to the closest bale
---@return number|nil index of the bale
function AIDriveStrategyFindBales:findClosestBale(bales, balesToIgnore)
if not bales then
if not bales then
return
end
local closestBale, minDistance, ix = nil, math.huge, 1
Expand Down Expand Up @@ -327,44 +328,44 @@ function AIDriveStrategyFindBales:getBaleTarget(bale)
return State3D(xb, -zb, CpMathUtil.angleFromGame(yRot))
end

--- Sets the driver as finished, so either a path
--- Sets the driver as finished, so either a path
--- to the start marker as a park position can be used
--- or the driver stops directly.
function AIDriveStrategyFindBales:setFinished()
if not self:isReadyToFoldImplements() then
-- Waiting until the folding has finished..
self:debugSparse("Waiting until an animation has finish, so the driver can be released ..")
return
end
end
self.vehicle:prepareForAIDriving()
if not self.vehicle:getIsAIReadyToDrive() then
if not self.vehicle:getIsAIReadyToDrive() then
-- Waiting until the folding has finished..
self:debugSparse("Waiting until an animation has finish, so the driver can be released ..")
return
end
if self.invertedStartPositionMarkerNode then
if self.invertedStartPositionMarkerNode then
self:debug("A valid start position is found, so the driver tries to finish at the inverted goal node")
self:startPathfindingToStartMarker()
else
self:finishJob()
end
end

--- Finishes the job with the correct stop reason, as
--- Finishes the job with the correct stop reason, as
--- the correct reason is needed for a possible AD takeover.
function AIDriveStrategyFindBales:finishJob()
if self:areBaleLoadersFull() then
if self:areBaleLoadersFull() then
self:debug('All the bale loaders are full, so stopping the job.')
self.vehicle:stopCurrentAIJob(AIMessageErrorIsFull.new())
elseif self:hasBalesLoaded() then
if self.baleLoaderController and self.baleLoaderController:isChangingBaleSize() then
elseif self:hasBalesLoaded() then
if self.baleLoaderController and self.baleLoaderController:isChangingBaleSize() then
self:debug('There really are no more bales on the field, so stopping the job')
self.vehicle:stopCurrentAIJob(AIMessageSuccessFinishedJob.new())
else
else
self:debug('No more bales found on the field, so stopping the job and sending the loader to unload the bales.')
self.vehicle:stopCurrentAIJob(AIMessageErrorIsFull.new())
end
elseif self.baleLoader and self.wrongWrapTypeFound then
elseif self.baleLoader and self.wrongWrapTypeFound then
self:debug('Only bales with a wrong wrap type are left on the field.')
self.vehicle:stopCurrentAIJob(AIMessageErrorWrongBaleWrapType.new())
else
Expand All @@ -382,8 +383,8 @@ end
---@param success boolean
---@param course Course|nil
---@param goalNodeInvalid boolean|nil
function AIDriveStrategyFindBales:onPathfindingFinished(controller,
success, course, goalNodeInvalid)
function AIDriveStrategyFindBales:onPathfindingFinished(controller,
success, course, goalNodeInvalid)
if self.state == self.states.DRIVING_TO_NEXT_BALE then
if success then
self.balesTried = {}
Expand Down Expand Up @@ -411,12 +412,12 @@ function AIDriveStrategyFindBales:onPathfindingFinished(controller,
end
elseif self.state == self.states.DRIVING_TO_START_MARKER then
if success then
--- Append a straight alignment segment
--- Append a straight alignment segment
local x, _, z = course:getWaypointPosition(course:getNumberOfWaypoints())
local dx, _, dz = localToWorld(self.invertedStartPositionMarkerNode, self.invertedGoalPositionOffset, 0, 0)

course:append(Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz,
0, 0, 0, 3, false))
0, 0, 0, 3, false))
self:startCourse(course, 1)
else
self:finishJob()
Expand Down Expand Up @@ -445,16 +446,19 @@ function AIDriveStrategyFindBales:getPathfinderBaleTargetAsGoalNode(bale)
local offset = Vector(0, safeDistanceFromBale + configuredOffset)
goal:add(offset:rotate(goal.t))
self:debug('Start pathfinding to next bale (%d), safe distance from bale %.1f, half vehicle width %.1f, configured offset %s',
bale:getId(), safeDistanceFromBale, halfVehicleWidth,
configuredOffset and string.format('%.1f', configuredOffset) or 'n/a')
bale:getId(), safeDistanceFromBale, halfVehicleWidth,
configuredOffset and string.format('%.1f', configuredOffset) or 'n/a')
return goal
end

---@param bale BaleToCollect
function AIDriveStrategyFindBales:startPathfindingToBale(bale)
self.state = self.states.DRIVING_TO_NEXT_BALE
g_baleToCollectManager:lockBale(bale:getBaleObject(), self)
local context = PathfinderContext(self.vehicle):objectsToIgnore(self:getBalesToIgnore())
local objectsToIgnore = self:getBalesToIgnore()
-- ignore the target bale, we actually want to hit it, there really is no reason to trigger collisions with it
table.insert(objectsToIgnore, bale:getBaleObject())
local context = PathfinderContext(self.vehicle):objectsToIgnore(objectsToIgnore)
context:allowReverse(false):maxFruitPercent(self.settings.avoidFruit:getValue() and 10 or math.huge)
table.insert(self.balesTried, bale)
self.pathfinderController:registerListeners(self, self.onPathfindingFinished, nil,
Expand Down Expand Up @@ -533,13 +537,13 @@ function AIDriveStrategyFindBales:getDriveData(dt, vX, vY, vZ)
self:updateLowFrequencyImplementControllers()
self:updateLowFrequencyPathfinder()
if self.state == self.states.INITIAL then
if self:getCanContinueWork() then
if self:getCanContinueWork() then
self.state = self.states.SEARCHING_FOR_NEXT_BALE
else
--- Waiting until the unfolding has finished.
if self.bales == nil then
if self.bales == nil then
--- Makes sure the hud bale counter already gets updated
self.bales = self:findBales()
self.bales = self:findBales()
end
self:setMaxSpeed(0)
end
Expand Down Expand Up @@ -590,15 +594,15 @@ function AIDriveStrategyFindBales:approachBale()
if not self:isReadyToLoadNextBale() then
self:debug('Start picking up bale')
self.state = self.states.WORKING_ON_BALE
self.numBalesLeftOver = math.max(self.numBalesLeftOver-1, 0)
self.numBalesLeftOver = math.max(self.numBalesLeftOver - 1, 0)
end
end
if self.baleWrapper then
self.baleWrapperController:handleBaleWrapper()
if self.baleWrapperController:isWorking() then
self:debug('Start wrapping bale')
self.state = self.states.WORKING_ON_BALE
self.numBalesLeftOver = math.max(self.numBalesLeftOver-1, 0)
self.numBalesLeftOver = math.max(self.numBalesLeftOver - 1, 0)
end
end
end
Expand Down Expand Up @@ -648,15 +652,15 @@ function AIDriveStrategyFindBales:update(dt)
end
end
if self.state ~= self.states.DRIVING_TO_START_MARKER and
self.state ~= self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD then
self.state ~= self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD then
if self:areBaleLoadersFull() then
self.state = self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD
end
end
--- Ignores the loaded auto loader bales.
--- TODO: Maybe add a delay here?
local loadedBales = self:getBalesToIgnore()
for _, bale in pairs(loadedBales) do
for _, bale in pairs(loadedBales) do
--- Makes sure these loaded bales from an autoload trailer,
--- can't be selected as a target by another bale loader.
g_baleToCollectManager:temporarilyLeaseBale(bale)
Expand Down
16 changes: 8 additions & 8 deletions scripts/reloadAI.bat
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
set outfile=..\reload.xml
echo ^<code^> > %outfile%
echo ^<![CDATA[ >> %outfile%
type ai\AIDriveStrategyCourse.lua >> %outfile%
type ai\AIDriveStrategyFieldWorkCourse.lua >> %outfile%
type ai\AIDriveStrategyCombineCourse.lua >> %outfile%
type ai\AIDriveStrategyPlowCourse.lua >> %outfile%
type ai\AIDriveStrategyDriveToFieldWorkStart.lua >> %outfile%
type ai\AIDriveStrategyVineFieldWorkCourse.lua >> %outfile%
type ai\AIDriveStrategyFindBales.lua >> %outfile%
type ai\AIDriveStrategyUnloadCombine.lua >> %outfile%
type ai\strategies\AIDriveStrategyCourse.lua >> %outfile%
type ai\strategies\AIDriveStrategyFieldWorkCourse.lua >> %outfile%
type ai\strategies\AIDriveStrategyCombineCourse.lua >> %outfile%
type ai\strategies\AIDriveStrategyPlowCourse.lua >> %outfile%
type ai\strategies\AIDriveStrategyDriveToFieldWorkStart.lua >> %outfile%
type ai\strategies\AIDriveStrategyVineFieldWorkCourse.lua >> %outfile%
type ai\strategies\AIDriveStrategyFindBales.lua >> %outfile%
type ai\strategies\AIDriveStrategyUnloadCombine.lua >> %outfile%
echo ]]^> >> %outfile%
echo ^</code^> >> %outfile%

0 comments on commit 7afa211

Please sign in to comment.