From da0ae42ea940192eff2f9474a783037cb6811512 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 5 Feb 2021 10:37:56 +0100 Subject: [PATCH] diff changes/fixes mode 9/10 and BunkerSiloManager - reworked shield height control - fixes reverse unloading mode 6 in bunker silo (#6748) - code improvements BunkerSiloManager - improves unloading mode 9 at trailers - fixes mode 9 heap callstacks --- AIDriver.lua | 5 +- BunkersiloManager.lua | 678 +++++++++++++++++++++++--------- CpManager.lua | 49 +++ FieldworkAIDriver.lua | 7 +- LevelCompactAIDriver.lua | 459 +++++++++++---------- ShovelModeAIDriver.lua | 336 ++++++++++------ TriggerShovelModeAIDriver.lua | 2 +- UnloadableFieldworkAIDriver.lua | 43 +- 8 files changed, 1014 insertions(+), 565 deletions(-) diff --git a/AIDriver.lua b/AIDriver.lua index 9ae0f42ac..c5d9f295d 100644 --- a/AIDriver.lua +++ b/AIDriver.lua @@ -1418,9 +1418,10 @@ end function AIDriver:setOffsetInBGASilo() if self.bunkerSiloManager == nil then - local silo = BunkerSiloManagerUtil.getTargetBunkerSiloByPointOnCourse(self.course,self.ppc:getCurrentWaypointIx()+3) + local ix = self.ppc:getCurrentWaypointIx()+3 + local silo = BunkerSiloManagerUtil.getTargetBunkerSiloAtWaypoint(self.vehicle,self.course,ix) if silo then - self.bunkerSiloManager = BunkerSiloManager(self.vehicle, silo,3) + self.bunkerSiloManager = BunkerSiloManager(self.vehicle, silo,3,nil,BunkerSiloManager.MODE.UNLOADING) end end if self.bunkerSiloManager ~= nil then diff --git a/BunkersiloManager.lua b/BunkersiloManager.lua index 6d6f8a013..a199f5e37 100644 --- a/BunkersiloManager.lua +++ b/BunkersiloManager.lua @@ -1,6 +1,15 @@ ---@class BunkerSiloManager BunkerSiloManager = CpObject() +BunkerSiloManager.debugChannel = 10 + +BunkerSiloManager.MODE = {} +BunkerSiloManager.MODE.COMPACTING = 0 --LevelCompactAIDriver compacting without shield +BunkerSiloManager.MODE.SHIELD = 1 --LevelCompactAIDriver leveling/filUup with shield +BunkerSiloManager.MODE.SHOVEL = 2 --ShovelModeAIDriver +BunkerSiloManager.MODE.UNLOADING = 3 --unloading in bunker with GrainTransport-,UnloadableFieldwork-,CombineUnloadAIDriver + + --for reference look up : "https://gdn.giants-software.com/documentation_scripting_fs19.php?version=script&category=26&class=244" --mostly : "BunkerSilo:load(id, xmlFile, key)" --[[ @@ -29,52 +38,332 @@ widthNode --> | | <-- startNode (sx,_,sz) ---@param vehicle vehicle ---@param Silo BunkerSilo or simulated HeapSilo ----@param float workwidth +---@param float work width of the relevant tool ---@param node targetNode is either the front/back node of the implement used to check if the bestTarget was passed +---@param int driver mode (shovel,unloading,shield,compacting) ---@param boolean is the silo a heap ? -function BunkerSiloManager:init(vehicle, Silo, width, targetNode,isHeap) - print("BunkerSiloManager: init()") - self.siloMap = self:createBunkerSiloMap(vehicle, Silo, width,isHeap) - self.silo = Silo +function BunkerSiloManager:init(vehicle, silo, workWidth, targetNode,driverMode, isHeap) + self.silo = silo self.vehicle = vehicle self.targetNode = targetNode + self.isHeap = isHeap + self.driverMode = driverMode + self.siloMap = self:createBunkerSiloMap(workWidth) +end + +---Gets the driver mode +---@return int driver mode (shovel,unloading,shield,compacting) +function BunkerSiloManager:getDriverMode() + return self.driverMode end function BunkerSiloManager:getSiloMap() return self.siloMap end +---Get silo part by line and column index +---@param int line index +---@param int column index +---@return table silo part +function BunkerSiloManager:getSiloPart(line,column) + return self.siloMap[line][column] +end + +---Get silo part drive positions by line and column index, +---which have an driving offset near the bunker walls +---@param int line index +---@param int column index +---@return float cx/cz drive position +function BunkerSiloManager:getSiloPartPosition(line,column) + local siloPart = self:getSiloPart(line,column) + return siloPart.cx,siloPart.cz +end + +---Get silo part center positions by line and column index +---@param int line index +---@param int column index +---@return float bx/bz center position of part +function BunkerSiloManager:getSiloPartCenterPosition(line,column) + local siloPart = self:getSiloPart(line,column) + return siloPart.bx,siloPart.bz +end + +---Get silo part start/width/height positions by line and column index +---@param int line index +---@param int column index +---@return float sx/sz start position of part +---@return float wx/wz width position of part +---@return float hx/hz height position of part +function BunkerSiloManager:getSiloPartStartWidthHeightPositions(line,column) + local siloPart = self:getSiloPart(line,column) + return siloPart.sx,siloPart.sz,siloPart.wx,siloPart.wz,siloPart.hx,siloPart.hz +end + +---Gets silo part fillLevel +---@param int line index +---@param int column index +---@return float silo part fillLevel +function BunkerSiloManager:getSiloPartFillLevel(line,column) + local fillLevel = 0 + local fillType = self:getSiloPartFillType(line,column) + if fillType and fillType ~= 0 then + local sx,sz,wx,wz,hx,hz = self:getSiloPartStartWidthHeightPositions(line,column) + fillLevel = DensityMapHeightUtil.getFillLevelAtArea(fillType,sx,sz,wx,wz,hx,hz) + end + return fillLevel +end + +---Gets silo line total fillLevel +---@param int line index +---@return float silo line fillLevel +function BunkerSiloManager:getSiloPartLineFillLevel(line) + local totalFillLevel = 0 + local numColumns = self:getNumberOfColumns() + for column=1,numColumns do + totalFillLevel = totalFillLevel + self:getSiloPartFillLevel(line,column) + end + return totalFillLevel +end + +---Gets silo column total fillLevel +---@param int column index +---@return float silo column fillLevel +function BunkerSiloManager:getSiloPartColumnFillLevel(column) + local totalFillLevel = 0 + local numLines = self:getNumberOfLines() + for line=1,numLines do + totalFillLevel = totalFillLevel + self:getSiloPartFillLevel(line,column) + end + return totalFillLevel +end + +---Gets silo column with the most fillLevel +---@param int optional line index +---@return int silo column with most fillLevel +function BunkerSiloManager:getSiloPartColumnWithMostFillLevel(line) + local lastFillLevel = 0 + local lastColumn = 1 + local numColumns = self:getNumberOfColumns() + for column=1,numColumns do + local fillLevel = 0 + if line then + fillLevel = self:getSiloPartFillLevel(line,column) + else + fillLevel = self:getSiloPartColumnFillLevel(column) + end + if fillLevel > lastFillLevel then + lastFillLevel = fillLevel + lastColumn = column + end + end + return lastColumn +end + +---Gets silo column with the least fillLevel +---@param int optional line index +---@return int silo column with least fillLevel +function BunkerSiloManager:getSiloPartColumnWithLeastFillLevel(line) + local lastFillLevel = math.huge + local lastColumn = 1 + local numColumns = self:getNumberOfColumns() + for column=1,numColumns do + local fillLevel = 0 + if line then + fillLevel = self:getSiloPartFillLevel(line,column) + else + fillLevel = self:getSiloPartColumnFillLevel(column) + end + if fillLevel < lastFillLevel then + lastFillLevel = fillLevel + lastColumn = column + end + end + return lastColumn +end + +---Gets silo line with the most fillLevel +---@param int optional column index +---@return int silo line with most fillLevel +function BunkerSiloManager:getSiloPartLineWithMostFillLevel(column) + local lastFillLevel = 0 + local lastLine = 1 + local numLines = self:getNumberOfLines() + for line=1,numLines do + local fillLevel = 0 + if line then + fillLevel = self:getSiloPartFillLevel(line,column) + else + fillLevel = self:getSiloPartLineFillLevel(line) + end + if fillLevel > lastFillLevel then + lastFillLevel = fillLevel + lastLine = line + end + end + return lastLine +end + +---Gets silo line with the least fillLevel +---@param int optional column index +---@return int silo line with least fillLevel +function BunkerSiloManager:getSiloPartLineWithLeastFillLevel(column) + local lastFillLevel = math.huge + local lastLine = 1 + local numLines = self:getNumberOfLines() + for line=1,numLines do + local fillLevel = 0 + if line then + fillLevel = self:getSiloPartFillLevel(line,column) + else + fillLevel = self:getSiloPartLineFillLevel(line) + end + if fillLevel < lastFillLevel then + lastFillLevel = fillLevel + lastLine = line + end + end + return lastLine +end + + +---Gets the first silo part line, which has a fillLevel >0 +---@return int first silo line which has a fillLevel >0 +function BunkerSiloManager:getFirstSiloPartLineWithFillLevel() + local numLines = self:getNumberOfLines() + for line=1,numLines do + local fillLevel = self:getSiloPartLineFillLevel(line) + if fillLevel > 0 then + return line + end + end + return 1 +end + +---Gets the first silo part line with fillLevel > 0 for a column +---@param int column index +---@return int first silo line with fillLevel > 0 for a column +function BunkerSiloManager:getFirstSiloPartLineWithFillLevelForColumn(column) + local numLines = self:getNumberOfLines() + for line=1,numLines do + local fillLevel = self:getSiloPartFillLevel(line,column) + if fillLevel > 0 then + return line + end + end + return 1 +end + +---Gets the first silo part column with fillLevel > 0 for a line +---@param int line index +---@return int first silo column with fillLevel > 0 for a line +function BunkerSiloManager:getFirstSiloPartColumnWithFillLevelForLine(line) + local numColumns = self:getNumberOfColumns() + for column=1,numColumns do + local fillLevel = self:getSiloPartFillLevel(line,column) + if fillLevel > 0 then + return column + end + end + return 1 +end + +---Gets silo part fillType +---@param int line index +---@param int column index +---@return int silo part fillType index +function BunkerSiloManager:getSiloPartFillType(line,column) + local sx,sz,wx,wz,hx,hz = self:getSiloPartStartWidthHeightPositions(line,column) + local wy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wx, 1, wz); + local hy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, hx, 1, hz) + return DensityMapHeightUtil.getFillTypeAtLine(wx, wy, wz, hx, hy, hz, 5) +end + +---Get silo part area +---@param int line index +---@param int column index +---@return float silo part area +function BunkerSiloManager:getSiloPartArea(line,column) + local siloPart = self:getSiloPart(line,column) + return siloPart.area +end + +---Get silo part line area +---@param int line index +---@return float silo part line area +function BunkerSiloManager:getSiloPartLineArea(line) + local area = 0 + local numColumns = self:getNumberOfColumns() + for column = 1, numColumns do + area = area + self:getSiloPartArea(line,column) + end + return area +end + +---Get silo part column area +---@param int column index +---@return float silo part column area +function BunkerSiloManager:getSiloPartColumnArea(column) + local area = 0 + local numLines = self:getNumberOfLines() + for line = 1, numLines do + area = area + self:getSiloPartArea(line,column) + end + return area +end + +---Get number of columns +---@return int number of columns +function BunkerSiloManager:getNumberOfColumns() + return #self.siloMap[1] +end + +---Get number of lines +---@return int number of lines +function BunkerSiloManager:getNumberOfLines() + return #self.siloMap +end + +---Get number of lines and columns +---@return int number of lines +---@return int number of columns +function BunkerSiloManager:getNumberOfLinesAndColumns() + return self:getNumberOfLines(),self:getNumberOfColumns() +end + function BunkerSiloManager:getSilo() return self.silo end +function BunkerSiloManager:isHeapSiloMap() + return self.isHeap +end + ---creating the relevant siloMap ----@param vehicle vehicle ----@param Silo BunkerSilo or simulated HeapSilo ----@param float workwidth ----@param boolean is the silo a heap ? -function BunkerSiloManager:createBunkerSiloMap(vehicle, Silo, width,isHeap) +---@param float work width of the relevant tool +function BunkerSiloManager:createBunkerSiloMap(width) + + local bunkerSiloArea = self.silo.bunkerSiloArea --only for Heaps as this createBunkerSiloMap() also applies to it .. - local sx,sz = Silo.bunkerSiloArea.sx,Silo.bunkerSiloArea.sz; --start BunkerNode - local wx,wz = Silo.bunkerSiloArea.wx,Silo.bunkerSiloArea.wz; --width BunkerNode "x cordinate" - local hx,hz = Silo.bunkerSiloArea.hx,Silo.bunkerSiloArea.hz; --height/"depth" BunkerNode "z cordinate" + local sx,sz = bunkerSiloArea.sx,bunkerSiloArea.sz; --start BunkerNode + local wx,wz = bunkerSiloArea.wx,bunkerSiloArea.wz; --width BunkerNode "x cordinate" + local hx,hz = bunkerSiloArea.hx,bunkerSiloArea.hz; --height/"depth" BunkerNode "z cordinate" local bunkerWidth = courseplay:distance(sx,sz, wx, wz) local bunkerLength = courseplay:distance(sx,sz, hx, hz) local sy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx, 1, sz); -- check the distance from our vehicle either we are comming from the front or back of the silo - local startDistance = courseplay:distanceToPoint(vehicle, sx, sy, sz) - local endDistance = courseplay:distanceToPoint(vehicle, hx, sy, hz) + local startDistance = courseplay:distanceToPoint(self.vehicle, sx, sy, sz) + local endDistance = courseplay:distanceToPoint(self.vehicle, hx, sy, hz) --correct data for bunkerSilos --shorten the BunkerArea by 1.0 , as the silo size from Giants tends to be bigger the the actual fillArea - if Silo.bunkerSiloArea.start then - sx, _, sz = localToWorld(Silo.bunkerSiloArea.start,-0.5,0,0) --start BunkerNode - wx, _, wz = localToWorld(Silo.bunkerSiloArea.width,0.5,0,0) --width BunkerNode "x cordinate" - hx, _, hz = localToWorld(Silo.bunkerSiloArea.height,-0.5,0,1) --height/"depth" BunkerNode "z cordinate" - bunkerWidth = calcDistanceFrom(Silo.bunkerSiloArea.start,Silo.bunkerSiloArea.width)-1 - bunkerLength = calcDistanceFrom(Silo.bunkerSiloArea.start,Silo.bunkerSiloArea.height)-1 + if bunkerSiloArea.start then + sx, _, sz = localToWorld(bunkerSiloArea.start,-0.5,0,0) --start BunkerNode + wx, _, wz = localToWorld(bunkerSiloArea.width,0.5,0,0) --width BunkerNode "x cordinate" + hx, _, hz = localToWorld(bunkerSiloArea.height,-0.5,0,1) --height/"depth" BunkerNode "z cordinate" + bunkerWidth = calcDistanceFrom(bunkerSiloArea.start,bunkerSiloArea.width)-1 + bunkerLength = calcDistanceFrom(bunkerSiloArea.start,bunkerSiloArea.height)-1 end @@ -82,14 +371,11 @@ function BunkerSiloManager:createBunkerSiloMap(vehicle, Silo, width,isHeap) local heightDirX,heightDirY,heightDirZ,heightDistance = courseplay:getWorldDirection(sx,sy,sz, hx,sy,hz); local widthCount = 0 - courseplay.debugVehicle(10, vehicle, 'Bunker width %.1f, working width %.1f (passed in)', bunkerWidth, width) + self:debug('Bunker width %.1f, working width %.1f (passed in)', bunkerWidth, width) widthCount =math.ceil(bunkerWidth/width) - --check if this one is still needed ? - if vehicle.cp.driver.getIsModeLeveling then - if (vehicle.cp.driver:getIsModeLeveling() or vehicle.cp.driver:getIsModeFillUp()) and courseplay:isEven(widthCount) then - widthCount = widthCount+1 - end + if self:getDriverMode() == self.MODE.SHIELD and courseplay:isEven(widthCount) then + widthCount = widthCount+1 end local heightCount = math.ceil(bunkerLength/ width) @@ -129,8 +415,8 @@ function BunkerSiloManager:createBunkerSiloMap(vehicle, Silo, width,isHeap) local bx = sx + (widthLengthX/2) + (heightLengthX/2) local bz = sz + (widthLengthZ/2) + (heightLengthZ/2) local offset = 0 - if isHeap then - --no idea ?? + + if self:isHeapSiloMap() then offset = unitWidth/2 else if widthIndex == 1 then @@ -143,7 +429,7 @@ function BunkerSiloManager:createBunkerSiloMap(vehicle, Silo, width,isHeap) end -- something with direction ?? local cx,cz = sx +(widthDirX*offset)+(heightLengthX/2),sz +(widthDirZ*offset)+ (heightLengthZ/2) - if vehicle.cp.mode == courseplay.MODE_SHOVEL_FILL_AND_EMPTY and heightIndex == heightCount then + if self:getDriverMode() == self.MODE.SHOVEL and heightIndex == heightCount then cx,cz = sx +(widthDirX*offset)+(heightLengthX),sz +(widthDirZ*offset)+ (heightLengthZ) end local unitArea = unitWidth*unitHeigth @@ -174,13 +460,13 @@ function BunkerSiloManager:createBunkerSiloMap(vehicle, Silo, width,isHeap) sz = map[heightIndex][1].hz end if lastValidfillType > 0 then - courseplay:debug(('%s: Bunkersilo filled with %s(%i) will be devided in %d lines and %d columns'):format(nameNum(vehicle),g_fillTypeManager.indexToName[lastValidfillType], lastValidfillType, heightCount, widthCount), 10); + self:debug('Bunker silo filled with %s(%i) will be divided in %d lines and %d column',g_fillTypeManager.indexToName[lastValidfillType], lastValidfillType, heightCount, widthCount) else - courseplay:debug(('%s: empty Bunkersilo will be devided in %d lines and %d columns'):format(nameNum(vehicle), heightCount, widthCount), 10); + courseplay.infoVehicle(self.vehicle,'Empty bunker silo will be divided in %d lines and %d columns',heightCount,widthCount) end - --invert table as we are comming from the back into the silo + --invert table as we are coming from the back into the silo if endDistance < startDistance then - courseplay:debug(('%s: Bunkersilo will be approached from the back -> turn map'):format(nameNum(vehicle)), 10); + self:debug('Bunker silo will be approached from the back, so inverted the silo map') local newMap = {} local lineCounter = #map for lineIndex=1,lineCounter do @@ -198,24 +484,45 @@ function BunkerSiloManager:createBunkerSiloMap(vehicle, Silo, width,isHeap) return map end ----get the best column to fill +---Check if the bunker silo map setup valid ? +---@return boolean isSiloEmpty ? +function BunkerSiloManager:isSiloMapValid() + return self.siloMap and not self:isSiloEmpty() or false +end + +---Check if a siloMap was created and it's not empty +---@return boolean isSiloEmpty ? +function BunkerSiloManager:isSiloEmpty() + return self:getTotalFillLevel() <= 0 +end + +---Gets the total fillLevel of the silo +---@return float totalFillLevel ? +function BunkerSiloManager:getTotalFillLevel() + local totalFillLevel = 0 + local numLines,numColumns = self:getNumberOfLinesAndColumns() + for line = 1, numLines do + for column = 1, numColumns do + totalFillLevel = totalFillLevel + self:getSiloPartFillLevel(line,column) + end + end + self:debug("totalFillLevel: %.2f",totalFillLevel) + return totalFillLevel +end + + +---Gets column with least fillLevel, without the column directly near the bunker walls ---@return int best column to fill function BunkerSiloManager:getBestColumnToFill() + local numLines,numColumns = self:getNumberOfLinesAndColumns() local leastFillLevel = math.huge local leastColumnIndex = 0 - for columnIndex=2,#self.siloMap[1]-1 do - local currentFillLevel = 0 - for lineIndex=1,#self.siloMap do - local fillUnit = self.siloMap[lineIndex][columnIndex] - currentFillLevel = currentFillLevel + fillUnit.fillLevel - --print(string.format("check:line %s, column %s fillLevel:%s",tostring(lineIndex),tostring(columnIndex),tostring(fillUnit.fillLevel))) - end - --print("column:"..tostring(columnIndex).." :"..tostring(currentFillLevel)) - if currentFillLevel distance then leastDistance = distance @@ -259,21 +566,42 @@ function BunkerSiloManager:setOffsetsPerWayPoint(course,bestColumn,ix) return foundFirst end +---Gets the closest bunker silo map line form a given column to a waypoint ix +---@param course course of the driver +---@param int waypoint ix to get the distance from +---@param int target column of the bunker silo map +---@return float dx,dy,dz closest silo map part position +function BunkerSiloManager:getClosestSiloPartPositionToWaypoint(course,ix,targetColumn) + local closestLine,dx,dz = 0,0,0 + local leastDistance = math.huge + for lineIndex=1,#self.siloMap do + local x,z = self:getSiloPartPosition(lineIndex,targetColumn) + local distance = course:getDistanceBetweenPointAndWaypoint(x,z, ix) + if leastDistance > distance then + leastDistance = distance + closestLine = lineIndex + dx,dz = x,z + end + end + local dy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, dx, 1, dz); + return dx,dy,dz +end + + ---have we reached the end ? ---@param siloMapPart bestTarget targeted part ---@return boolean end reached ? function BunkerSiloManager:isAtEnd(bestTarget) + local numLines = self:getNumberOfLines() if not self.siloMap or not bestTarget then return false end - - local targetUnit = self.siloMap[bestTarget.line][bestTarget.column] - local cx ,cz = targetUnit.cx, targetUnit.cz + local cx,cz = self:getSiloPartPosition(bestTarget.line,bestTarget.column) local cy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, cx, 1, cz); local x,y,z = getWorldTranslation(self.targetNode) local distance2Target = courseplay:distance(x,z, cx, cz) --distance from shovel to target if distance2Target < 1 then - if bestTarget.line == #self.siloMap then + if bestTarget.line == numLines then return true end end @@ -281,52 +609,15 @@ function BunkerSiloManager:isAtEnd(bestTarget) end --- get the bestTarget, firstLine of the bestTarget work with ----@param siloMapPart bestTarget targeted part ---@return bestTarget, firstLine of the bestTarget -function BunkerSiloManager:getBestTargetFillUnitFillUp(bestTarget) - --print(string.format("courseplay:getActualTarget(vehicle) called by %s",tostring(courseplay.utils:getFnCallPath(3)))) - local firstLine = 0 - if self.siloMap ~= nil then - local stopSearching = false - local mostFillLevelAtLine = 0 - local mostFillLevelIndex = 2 - local fillingTarget = {} - - -- find column with most fillLevel and figure out whether it is empty - for lineIndex, line in pairs(self.siloMap) do - if stopSearching then - break - end - mostFillLevelAtLine = 0 - for column, fillUnit in pairs(line) do - if mostFillLevelAtLine < fillUnit.fillLevel then - mostFillLevelAtLine = fillUnit.fillLevel - mostFillLevelIndex = column - end - if column == #line and mostFillLevelAtLine > 0 then - fillingTarget = { - line = lineIndex; - column = mostFillLevelIndex; - empty = false; - } - stopSearching = true - break - end - end - end - if mostFillLevelAtLine == 0 then - fillingTarget = { - line = 1; - column = 1; - empty = true; - } - end - - bestTarget = fillingTarget - firstLine = bestTarget.line - end - - return bestTarget, firstLine +function BunkerSiloManager:getBestTargetFillUnitFillUp() + local line = self:getFirstSiloPartLineWithFillLevel() + local column = self:getSiloPartColumnWithMostFillLevel(line) + local bestTarget = { + line = line, + column = column + } + return bestTarget, line end @@ -340,36 +631,35 @@ end --- updating the current silo target part ---@param siloMapPart bestTarget targeted part function BunkerSiloManager:updateTarget(bestTarget) - local targetUnit = self.siloMap[bestTarget.line][bestTarget.column] - local cx ,cz = targetUnit.cx, targetUnit.cz + local numLines = self:getNumberOfLines() + local cx,cz = self:getSiloPartPosition(bestTarget.line,bestTarget.column) local cy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, cx, 1, cz); local x,y,z = getWorldTranslation(self.targetNode) local distance2Target = courseplay:distance(x,z, cx, cz) --distance from shovel to target if math.abs(distance2Target) < 1 then - bestTarget.line = math.min(bestTarget.line + 1, #self.siloMap) + bestTarget.line = math.min(bestTarget.line + 1, numLines) end end ---drawing the routing of the driver ----@param siloMapPart bestTarget targeted part ----@param tempTarget for driving out of the silo -function BunkerSiloManager:debugRouting(bestTarget,tempTarget) +---@param table bestTarget targeted part +---@param table for driving out of the silo +---@param float target shield height of mode 10 driver +function BunkerSiloManager:debugRouting(bestTarget,tempTarget,targetHeight) if self.siloMap ~= nil and bestTarget ~= nil then - local fillUnit = self.siloMap[bestTarget.line][bestTarget.column] - --print(string.format("fillUnit %s; self.cp.actualTarget.line %s; self.cp.actualTarget.column %s",tostring(fillUnit),tostring(self.cp.actualTarget.line),tostring(self.cp.actualTarget.column))) - local sx,sz = fillUnit.sx,fillUnit.sz - local wx,wz = fillUnit.wx,fillUnit.wz - local bx,bz = fillUnit.bx,fillUnit.bz - local hx,hz = fillUnit.hx +(fillUnit.wx-fillUnit.sx) ,fillUnit.hz +(fillUnit.wz-fillUnit.sz) + local sx,sz,wx,wz,hx,hz = self:getSiloPartStartWidthHeightPositions(bestTarget.line,bestTarget.column) + local bx,bz = self:getSiloPartCenterPosition(bestTarget.line,bestTarget.column) + local cx,cz = self:getSiloPartPosition(bestTarget.line,bestTarget.column) + local whx,whz = hx +(wx-sx) ,hz +(wz-sz) local _,tractorHeight,_ = getWorldTranslation(self.vehicle.cp.directionNode) local y = tractorHeight + 1.5; cpDebug:drawLine(sx, y, sz, 1, 0, 0, wx, y, wz); - cpDebug:drawLine(wx, y, wz, 1, 0, 0, hx, y, hz); - cpDebug:drawLine(fillUnit.hx, y, fillUnit.hz, 1, 0, 0, sx, y, sz); - cpDebug:drawLine(fillUnit.cx, y, fillUnit.cz, 1, 0, 1, bx, y, bz); - cpDebug:drawPoint(fillUnit.cx, y, fillUnit.cz, 1, 1 , 1); + cpDebug:drawLine(wx, y, wz, 1, 0, 0, whx, y, whz); + cpDebug:drawLine(hx, y, hz, 1, 0, 0, sx, y, sz); + cpDebug:drawLine(cx, y, cz, 1, 0, 1, bx, y, bz); + cpDebug:drawPoint(cx, y, cz, 1, 1 , 1); local bunker = self.silo if bunker ~= nil then @@ -384,11 +674,17 @@ function BunkerSiloManager:debugRouting(bestTarget,tempTarget) end if tempTarget ~= nil then local tx,tz = tempTarget.cx,tempTarget.cz - local fillUnit = self.siloMap[bestTarget.line][bestTarget.column] - local sx,sz = fillUnit.sx,fillUnit.sz cpDebug:drawLine(tx, y, tz, 1, 0, 1, sx, y, sz); cpDebug:drawPoint(tx, y, tz, 1, 1 , 1); end + if targetHeight then + local numLines = self:getNumberOfLines() + local x,z = self:getSiloPartCenterPosition(1,bestTarget.column) + local nx,nz = self:getSiloPartCenterPosition(numLines,bestTarget.column) + local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x,1,z) + local height = terrainHeight + targetHeight + cpDebug:drawLine(x, height, z, 1, 1, 1, nx, height, nz); + end end end @@ -419,60 +715,53 @@ function BunkerSiloManager:drawMap() end end +---Vehicle debug function +function BunkerSiloManager:debug(...) + courseplay.debugVehicle(self.debugChannel, self.vehicle, ...) +end BunkerSiloManagerUtil = {} +BunkerSiloManagerUtil.debugChannel = 10 ----get the closest bunkerSilo or heap +---Vehicle debug function +---@param vehicle +function BunkerSiloManagerUtil.debug(vehicle,...) + courseplay.debugVehicle(BunkerSiloManagerUtil.debugChannel, vehicle, ...) +end + +---Checks for bunkerSilos between two points ---@param vehicle vehicle of the driver ----@param int forces search around waypointIndex=forcedPoint ----@param boolean is using Heaps allowed, for example mode 9 ----@return targetSilo either the found BunkerSilo or --- a simulated silo by BunkerSiloManagerUtil.getHeapsMinMaxCoords() for Heaps ----@return boolean have we found a heap ? -function BunkerSiloManagerUtil.getTargetBunkerSilo(vehicle,forcedPoint,checkForHeapsActive) - local pointIndex = 0 - if forcedPoint then - pointIndex = forcedPoint; - else - pointIndex = vehicle.cp.driver.shovelFillStartPoint+2 - end - local x,z = vehicle.Waypoints[pointIndex].cx,vehicle.Waypoints[pointIndex].cz - local tx,tz = x,z + 0.50 - local p1x,p1z,p2x,p2z,p1y,p2y = 0,0,0,0,0,0 +---@param int x,z fist point +---@param int tx,tz second point +---return BunkerSilo +function BunkerSiloManagerUtil.getBunkerSilo(vehicle,x,z,tx,tz) if g_currentMission.bunkerSilos ~= nil then for _, bunker in pairs(g_currentMission.bunkerSilos) do local x1,z1 = bunker.bunkerSiloArea.sx,bunker.bunkerSiloArea.sz local x2,z2 = bunker.bunkerSiloArea.wx,bunker.bunkerSiloArea.wz local x3,z3 = bunker.bunkerSiloArea.hx,bunker.bunkerSiloArea.hz - bunker.type = "silo" if MathUtil.hasRectangleLineIntersection2D(x1,z1,x2-x1,z2-z1,x3-x1,z3-z1,x,z,tx-x,tz-z) then + BunkerSiloManagerUtil.debug(vehicle,"Silo was found: %s",nameNum(bunker)) return bunker end end end - --it's not a bunkersSilo, try to find a heap if it is allowed - if checkForHeapsActive then - return BunkerSiloManagerUtil.getHeapCoords(vehicle),true - end end - ----check for heaps and simulate a bunkerSiloMap for the found heap +---Checks for heaps between two points ---@param vehicle vehicle of the driver +---@param int x,z first point +---@param int nx,nz second point ---return targetSilo a simulated bunkerSilo version of the heap -function BunkerSiloManagerUtil.getHeapCoords(vehicle) - local p1x,p1z,p2x,p2z,p1y,p2y = 0,0,0,0,0,0 - - p1x,p1z = vehicle.Waypoints[vehicle.cp.driver.shovelFillStartPoint].cx,vehicle.Waypoints[vehicle.cp.driver.shovelFillStartPoint].cz; - p2x,p2z = vehicle.Waypoints[vehicle.cp.driver.shovelFillEndPoint].cx,vehicle.Waypoints[vehicle.cp.driver.shovelFillEndPoint].cz; +function BunkerSiloManagerUtil.getHeapCoords(vehicle,x,z,nx,nz) + local p1x,p1z,p2x,p2z,p1y,p2y = x,z,nx,nz,0,0 p1y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, p1x, 1, p1z); p2y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, p2x, 1, p2z); local heapFillType = DensityMapHeightUtil.getFillTypeAtLine(p1x, p1y, p1z, p2x, p2y, p2z, 5) - - if not heapFillType or heapFillType == FillType.UNKNOWN then + if heapFillType == nil or heapFillType == FillType.UNKNOWN then return end - courseplay:debug(string.format("%s: heap with %s found",nameNum(vehicle),tostring(heapFillType)),10) + --create temp node local point = createTransformGroup("cpTempHeapFindingPoint"); link(g_currentMission.terrainRootNode, point); @@ -484,10 +773,6 @@ function BunkerSiloManagerUtil.getHeapCoords(vehicle) local yRot = MathUtil.getYRotationFromDirection(dx, dz); setRotation(point, 0, yRot, 0); - --debug line vor search area to be sure, the point is set correctly - vehicle.cp.tempMOde9PointX,vehicle.cp.tempMOde9PointY,vehicle.cp.tempMOde9PointZ = getWorldTranslation(point) - vehicle.cp.tempMOde9PointX2,vehicle.cp.tempMOde9PointY2,vehicle.cp.tempMOde9PointZ2 = localToWorld(point,0,0,distance*2) - -- move the line to find out the size of the heap --find maxX @@ -502,7 +787,7 @@ function BunkerSiloManagerUtil.getHeapCoords(vehicle) --print(string.format("fillType:%s distance: %.1f",tostring(fillType),i)) if fillType ~= heapFillType then maxX = i-stepSize - courseplay:debug("maxX= "..tostring(maxX),10) + BunkerSiloManagerUtil.debug(vehicle,"maxX = %.2f",maxX) break end end @@ -517,7 +802,7 @@ function BunkerSiloManagerUtil.getHeapCoords(vehicle) --print(string.format("fillType:%s distance: %.1f",tostring(fillType),i)) if fillType ~= heapFillType then minX = i-stepSize - courseplay:debug("minX= "..tostring(minX),10) + BunkerSiloManagerUtil.debug(vehicle,"minX = %.2f",minX) break end end @@ -533,12 +818,12 @@ function BunkerSiloManagerUtil.getHeapCoords(vehicle) if fillType == heapFillType then foundHeap = true minZ = i-stepSize - courseplay:debug("minZ= "..tostring(minZ),10) + BunkerSiloManagerUtil.debug(vehicle,"minZ = %.2f",minZ) end else if fillType ~= heapFillType then maxZ = i-stepSize+1 - courseplay:debug("maxZ= "..tostring(maxZ),10) + BunkerSiloManagerUtil.debug(vehicle,"maxZ = %.2f",maxZ) break end end @@ -550,36 +835,75 @@ function BunkerSiloManagerUtil.getHeapCoords(vehicle) bunker.bunkerSiloArea.sx,_,bunker.bunkerSiloArea.sz = localToWorld(point,maxX,0,minZ); bunker.bunkerSiloArea.wx,_,bunker.bunkerSiloArea.wz = localToWorld(point,-minX,0,minZ) bunker.bunkerSiloArea.hx,_,bunker.bunkerSiloArea.hz = localToWorld(point,maxX,0,maxZ) - bunker.type = "heap" + local sx,sz = bunker.bunkerSiloArea.sx,bunker.bunkerSiloArea.sz + local wx,wz = bunker.bunkerSiloArea.wx,bunker.bunkerSiloArea.wz + local hx,hz = bunker.bunkerSiloArea.hx,bunker.bunkerSiloArea.hz + + local fillLevel = DensityMapHeightUtil.getFillLevelAtArea(heapFillType, sx, sz, wx, wz, hx, hz) + BunkerSiloManagerUtil.debug(vehicle,"Heap found with %s(%d) and fillLevel: %.2f",g_fillTypeManager:getFillTypeByIndex(heapFillType).title,heapFillType,fillLevel) - -- Clean up the temporary node. unlink(point); delete(point); - - + return bunker end ----get the best column to fill +---Checks if there is a bunker silo in between two waypoints, +---if a bunker was found the return it +---@param vehicle vehicle of the driver +---@param table course of the driver +---@param int first waypoint ix +---@param int last waypoint ix +---@param boolean looking for heaps allowed ? +---@return table bunker silo or simulated heap silo +---@return boolean was a heap found ? +function BunkerSiloManagerUtil.getTargetBunkerSiloBetweenWaypoints(vehicle,course,firstWpIx,lastWpIx,checkForHeapsActive) + local x,_,z = course:getWaypointPosition(firstWpIx) + local nx,_,nz = course:getWaypointPosition(lastWpIx) + + local silo = BunkerSiloManagerUtil.getBunkerSilo(vehicle,x,z,nx,nz) + if silo then + return silo + end + --it's not a bunkersSilo, try to find a heap if it is allowed + if checkForHeapsActive then + return BunkerSiloManagerUtil.getHeapCoords(vehicle,x,z,nx,nz),true + end +end + +---Checks if the waypoint is in a bunker silo, +---if a bunker was found the return it +---@param vehicle vehicle of the driver +---@param table course of the driver +---@param int waypoint ix to look for a silo +---@return table bunker silo +function BunkerSiloManagerUtil.getTargetBunkerSiloAtWaypoint(vehicle,course,wpIx) + local x,_,z = course:getWaypointPosition(wpIx) + local nx,nz = x,z + 0.50 + return BunkerSiloManagerUtil.getBunkerSilo(vehicle,x,z,nx,nz) +end + +---Gets the first waypoint in the silo and the last waypoint +---@param vehicle vehicle of the driver ---@param course course of the driver ----@param int waypointIndex to find the next closest bunkersilo or default is 1 ----@return int best column to fill -function BunkerSiloManagerUtil.getTargetBunkerSiloByPointOnCourse(course,forcedPoint) - local pointIndex = forcedPoint or 1 ; - local x,_,z = course:getWaypointPosition(pointIndex) - local tx,tz = x,z + 0.50 - local p1x,p1z,p2x,p2z,p1y,p2y = 0,0,0,0,0,0 - if g_currentMission.bunkerSilos ~= nil then - for _, bunker in pairs(g_currentMission.bunkerSilos) do - local x1,z1 = bunker.bunkerSiloArea.sx,bunker.bunkerSiloArea.sz - local x2,z2 = bunker.bunkerSiloArea.wx,bunker.bunkerSiloArea.wz - local x3,z3 = bunker.bunkerSiloArea.hx,bunker.bunkerSiloArea.hz - bunker.type = "silo" - if MathUtil.hasRectangleLineIntersection2D(x1,z1,x2-x1,z2-z1,x3-x1,z3-z1,x,z,tx-x,tz-z) then - return bunker +---@param int first waypoint to start checking from +---@return int first waypointIx in silo +---@return int last waypointIx in silo +function BunkerSiloManagerUtil.getFirstAndLastWaypointIxInSilo(vehicle,course,startIx) + local firstIx, lastIx + for ix=startIx,course:getNumberOfWaypoints() do + local x,_,z = course:getWaypointPosition(ix) + local nextIx = math.min(ix,course:getNumberOfWaypoints()) + local nx,_,nz = course:getWaypointPosition(nextIx) + if BunkerSiloManagerUtil.getBunkerSilo(vehicle,x,z,nx,nz) then + if not firstIx then + firstIx = ix end + lastIx = ix + elseif foundFirst then + break end end + return firstIx,lastIx end - diff --git a/CpManager.lua b/CpManager.lua index 152c046f8..5a6d607ae 100644 --- a/CpManager.lua +++ b/CpManager.lua @@ -123,6 +123,9 @@ function CpManager:loadMap(name) addConsoleCommand( 'cpToggleDevhelperDebug', 'Toggle development helper visual debug info', 'toggleDevhelperDebug', self ) addConsoleCommand( 'cpShowCombineUnloadManagerStatus', 'Show combine unload manager status', 'showCombineUnloadManagerStatus', self ) addConsoleCommand( 'cpReadVehicleConfigurations', 'Read custom vehicle configurations', 'loadFromXml', g_vehicleConfigurations) + + addConsoleCommand( 'cpCreateVehicleDebugSparseHook', 'Create a debug Sparse Hook', 'createVehicleVariableDebugSparseHook', self) + -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- TRIGGERS @@ -301,6 +304,7 @@ function CpManager:update(dt) courseplay.fields.filter:setValueCompareParams("greater", 0) -- more than 0, so it is a field end g_devHelper:update() + self:printVariableDebugSparseHook() end; @@ -643,6 +647,51 @@ function CpManager:traceOnForTable(t, tableName) end end +---Creates a debug loop of n-iterations and a delay of m-ticks +---@param string variable name/path to print +---@param function function to print the variable name +---@param int optional number of total iterations, default is 5 +---@param int optional delay of update ticks in between, default is 100 +function CpManager:createVariableDebugSparseHook(variableName,printVariableFunc,numIterations,delayBetweenIterations) + if self.debugSparseHookVariable == nil then + self.debugSparseHookVariable = { + variableName = variableName, + printVariableFunc = printVariableFunc, + numIterations = numIterations or 5, + delayBetweenIterations = delayBetweenIterations or 100 + } + end +end + +---Creates a debug loop of n-iterations and a delay of m-ticks for vehicle variables +---@param string variable name/path to print +---@param int optional number of total iterations, default is 5 +---@param int optional delay of update ticks in between, default is 100 +function CpManager:createVehicleVariableDebugSparseHook(variableName,numIterations,delayBetweenIterations) + if g_currentMission.controlledVehicle then + self:createVariableDebugSparseHook(variableName,self.printVehicleVariable,numIterations,delayBetweenIterations) + else + courseplay.info("controlledVehicle not found!") + end +end + +---Prints the debug sparse hook variable +function CpManager:printVariableDebugSparseHook() + if self.debugSparseHookVariable then + local delayBetweenIterations = self.debugSparseHookVariable.delayBetweenIterations + if g_updateLoopIndex % delayBetweenIterations == 0 then + local variableName = self.debugSparseHookVariable.variableName + local numIterations = self.debugSparseHookVariable.numIterations + local printVariableFunc = self.debugSparseHookVariable.printVariableFunc + printVariableFunc(self,variableName,1) + numIterations = numIterations - 1 + if numIterations < 1 then + self.debugSparseHookVariable = nil + end + end + end +end + --- get a reference pointing to the global variable 'variableName' -- can handle multiple levels (but not arrays, yet) like foo.bar function CpManager:getVariable(variableName) diff --git a/FieldworkAIDriver.lua b/FieldworkAIDriver.lua index afee1cc03..6e89add57 100644 --- a/FieldworkAIDriver.lua +++ b/FieldworkAIDriver.lua @@ -365,7 +365,10 @@ function FieldworkAIDriver:drive(dt) return end elseif self.state == self.states.ON_UNLOAD_OR_REFILL_COURSE then - self:driveUnloadOrRefill(dt) + local giveUpControl = self:driveUnloadOrRefill(dt) + if giveUpControl then + return + end elseif self.state == self.states.RETURNING_TO_FIRST_POINT then self:setSpeed(self:getFieldSpeed()) self.triggerHandler.fillableObject = nil @@ -495,7 +498,7 @@ function FieldworkAIDriver:stopAndRefuel() end ---@return boolean true if unload took over the driving -function FieldworkAIDriver:driveUnloadOrRefill() +function FieldworkAIDriver:driveUnloadOrRefill(dt) if self.course:isTemporary() then -- use the courseplay speed limit until we get to the actual unload course fields (on alignment/temporary) self:setSpeed(self.vehicle.cp.speeds.field) diff --git a/LevelCompactAIDriver.lua b/LevelCompactAIDriver.lua index 51d3ca076..c2e3b3e94 100644 --- a/LevelCompactAIDriver.lua +++ b/LevelCompactAIDriver.lua @@ -90,7 +90,6 @@ function LevelCompactAIDriver:drive(dt) end if self.levelState == self.states.DRIVE_TO_PARKING then - self:moveShield('up',dt) self.ppc:update() AIDriver.driveCourse(self, dt) elseif self.levelState == self.states.WAITING_FOR_FREE_WAY then @@ -126,6 +125,7 @@ function LevelCompactAIDriver:drive(dt) self:driveSiloCompact(dt) end self:updateInfoText() + self:updateShieldHeight(dt) end ---search for unloaders nearby @@ -209,26 +209,19 @@ function LevelCompactAIDriver:checkShield() local leveler = AIDriverUtil.getImplementWithSpecialization(self.vehicle, Leveler) if leveler then - self:debugSparse("leveler found: %s",nameNum(leveler)) + self:debug("leveler found: %s",nameNum(leveler)) if self:getIsModeFillUp() or self:getIsModeLeveling() then - --record alphaList if not existing - if self.alphaList == nil then - self:setIsAlphaListrecording() - end - if self:getIsAlphaListrecording() then - self:recordAlphaList() - else - return true - end + return true else courseplay:setInfoText(self.vehicle, 'COURSEPLAY_WRONG_TOOL'); - self:debugSparse("fail no working combo found!") + self:debug("fail no working combo found!") end else if self:getIsModeCompact() then return true else - self:debugSparse("fail no working combo found!") + courseplay:setInfoText(self.vehicle, 'COURSEPLAY_WRONG_TOOL'); + self:debug("fail no working combo found!") end end end @@ -278,11 +271,7 @@ function LevelCompactAIDriver:driveSiloLevel(dt) if self.bestTarget == nil then self.bestTarget, self.firstLine, self.targetHeight = self:getBestTargetFillUnitLeveling(self.lastDrivenColumn) end - renderText(0.2,0.395,0.02,"self:drivePush(dt)") - - self:drivePush(dt) - self:moveShield('down',dt,self:getDiffHeightforHeight(self.targetHeight)) - + self:drivePush(dt) if self:isAtEnd() --or self:hasShieldEmpty() or self:isStuck() @@ -298,7 +287,6 @@ function LevelCompactAIDriver:driveSiloLevel(dt) elseif self.fillUpState == self.states.PULLBACK then - self:moveShield('up',dt) if self:isStuck() then self.fillUpState = self.states.PUSH end @@ -311,14 +299,13 @@ end function LevelCompactAIDriver:driveSiloFillUp(dt) -- self:drawMap() + self.targetHeight = 0 if self.fillUpState == self.states.PUSH then --initialize first target point if self.bestTarget == nil then self.bestTarget, self.firstLine = self:getBestTargetFillUnitFillUp(self.lastDrivenColumn) - end + end self:drivePush(dt) - self:moveShield('down',dt,0) - --self:moveShield('down',dt,self:getDiffHeightforHeight(0)) if self:lastLineFillLevelChanged() or self:isStuck() --or self:hasShieldEmpty() @@ -332,7 +319,6 @@ function LevelCompactAIDriver:driveSiloFillUp(dt) end end elseif self.fillUpState == self.states.PULLBACK then - self:moveShield('up',dt) if self:drivePull(dt) then self.fillUpState = self.states.PUSH self:deleteBestTargetLeveling() @@ -374,16 +360,18 @@ function LevelCompactAIDriver:drivePull(dt) local fwd = true local refSpeed = math.min(20,self.vehicle.cp.settings.bunkerSpeed:get()) local allowedToDrive = true - local cx,cy,cz = self.course:getWaypointPosition(self.course:getNumberOfWaypoints()) - local lx, lz = AIVehicleUtil.getDriveDirection(self.vehicle.cp.directionNode, cx,cy,cz); + local gx,gy,gz = self.course:waypointLocalToWorld(1,0,0,15) + local lx, lz = AIVehicleUtil.getDriveDirection(self.vehicle.cp.directionNode, gx,gy,gz); self:driveInDirection(dt,lx,lz,fwd,refSpeed,allowedToDrive) --end if I moved over the last way point + self:debugRouting(gx,gz) if lz < 0 then pullDone = true end if self.hasFoundUnloaders then self:changeLevelState(self.states.DRIVE_TO_PARKING) self:deleteBestTarget() + self:raiseImplements() return false end -- self:drawMap() @@ -394,17 +382,10 @@ end ---currently we just drive 10 m ahead and then start normaly drive the buker course function LevelCompactAIDriver:driveToPreStartPosition(dt) local refSpeed = math.min(20,self.vehicle.cp.settings.bunkerSpeed:get()) - self:moveShield('up',dt) - if self.tempTarget == nil then - local gx,gy,gz = localToWorld(self.vehicle.rootNode,0,0,10) - self.tempTarget = {} - self.tempTarget.x = gx - self.tempTarget.y = gy - self.tempTarget.z = gz - end - local lx, lz = AIVehicleUtil.getDriveDirection(self.vehicle.cp.directionNode, self.tempTarget.x,self.tempTarget.y,self.tempTarget.z); - local distX,_,distZ = localToWorld(self.vehicle.rootNode,0,0,0) - if math.abs(distZ-self.tempTarget.z) > 1 then + local gx,gy,gz = self.course:waypointLocalToWorld(1,0,0,15) + local lx, lz = AIVehicleUtil.getDriveDirection(self.vehicle.cp.directionNode, gx,gy,gz); + self:debugRouting(gx,gz) + if lz > 0 then self:driveInDirection(dt,lx,lz,true,refSpeed,true) else self:selectMode() @@ -427,19 +408,8 @@ end function LevelCompactAIDriver:lastLineFillLevelChanged() - local vehicle = self.vehicle - local siloMap = self.bunkerSiloManager:getSiloMap() - local newSx = siloMap[#siloMap][1].sx - local newSz = siloMap[#siloMap][1].sz - local newWx = siloMap[#siloMap][#siloMap[#siloMap]].wx - local newWz = siloMap[#siloMap][#siloMap[#siloMap]].wz - local newHx = siloMap[#siloMap][1].hx - local newHz = siloMap[#siloMap][1].hz - local wY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, newWx, 1, newWz); - local hY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, newHx, 1, newHz); - - local fillType = DensityMapHeightUtil.getFillTypeAtLine(newWx, wY, newWz, newHx, hY, newHz, 5) - local newFillLevel = DensityMapHeightUtil.getFillLevelAtArea(fillType, newSx, newSz, newWx, newWz, newHx, newHz ) + local numLines = self.bunkerSiloManager:getNumberOfLines() + local newFillLevel = self.bunkerSiloManager:getSiloPartLineFillLevel(numLines) if self.savedLastLineFillLevel == nil then self.savedLastLineFillLevel = newFillLevel @@ -492,13 +462,19 @@ function LevelCompactAIDriver:hasShieldEmpty() end end +---Is the shield full ? +---@return boolean shield is full +function LevelCompactAIDriver:isShieldFull() + local shield = self.leveler + return shield and shield:getFillUnitFillLevel(1)/shield:getFillUnitCapacity(1) > 0.98 or false +end + function LevelCompactAIDriver:updateTarget() return self.bunkerSiloManager:updateTarget(self.bestTarget) end function LevelCompactAIDriver:isAtEnd() - if not self.bunkerSiloManager then return false end - return self.bunkerSiloManager:isAtEnd(self.bestTarget) + return self.bunkerSiloManager and self.bunkerSiloManager:isAtEnd(self.bestTarget) or false end function LevelCompactAIDriver:deleteBestTarget() @@ -582,15 +558,16 @@ end function LevelCompactAIDriver:checkSilo() if self.bunkerSiloManager == nil then - local silo = BunkerSiloManagerUtil.getTargetBunkerSilo(self.vehicle,1) + local silo = BunkerSiloManagerUtil.getTargetBunkerSiloAtWaypoint(self.vehicle,self.course,1) if silo then - self.bunkerSiloManager = BunkerSiloManager(self.vehicle,silo,self:getWorkWidth(),self:getValidBackImplement()) + local targetBunkerSiloMode = self:hasShield() and BunkerSiloManager.MODE.SHIELD or BunkerSiloManager.MODE.COMPACTING + self:debug("silo was found") + self.bunkerSiloManager = BunkerSiloManager(self.vehicle,silo,self:getWorkWidth(),self:getValidBackImplement(),targetBunkerSiloMode) + return true + else + courseplay:setInfoText(self.vehicle, courseplay:loc('COURSEPLAY_MODE10_NOSILO')); end - - end - if not self.bunkerSiloManager then - courseplay:setInfoText(self.vehicle, courseplay:loc('COURSEPLAY_MODE10_NOSILO')); - else + else ---silo was already found return true end end @@ -623,104 +600,6 @@ function LevelCompactAIDriver:raiseImplements() self.vehicle:raiseStateChange(Vehicle.STATE_CHANGE_AI_END_LINE) end - -function LevelCompactAIDriver:moveShield(moveDir,dt,fixHeight) - local leveler = self.leveler - local moveFinished = false - if leveler and leveler.spec_attacherJointControl ~= nil then - local spec = leveler.spec_attacherJointControl - local jointDesc = spec.jointDesc - if moveDir == "down" then - - --move attacherJoint down - if spec.heightController.moveAlpha ~= jointDesc.lowerAlpha then - spec.heightTargetAlpha = jointDesc.lowerAlpha - else - local newAlpha = self:getClosestAlpha(fixHeight) - leveler:controlAttacherJoint(spec.controls[2],newAlpha) - moveFinished = true - end - - elseif moveDir == "up" then - if spec.heightController.moveAlpha ~= spec.jointDesc.upperAlpha then - spec.heightTargetAlpha = jointDesc.upperAlpha - if not fixHeight then - leveler:controlAttacherJoint(spec.controls[2], spec.controls[2].moveAlpha + 0.1) - end - else - moveFinished = true - end - end - end; - return moveFinished -end - -function LevelCompactAIDriver:getClosestAlpha(height) - local closestIndex = 99 - local closestValue = 99 - for indexHeight,_ in pairs (self.alphaList) do - --print("try "..tostring(indexHeight)) - local diff = math.abs(height-indexHeight) - if closestValue > diff then - --print(string.format("%s is closer- set as closest",tostring(closestValue))) - closestIndex = indexHeight - closestValue = diff - end - end - return self.alphaList[closestIndex] -end - -function LevelCompactAIDriver:getIsAlphaListrecording() - return self.isAlphaListrecording; -end - -function LevelCompactAIDriver:resetIsAlphaListrecording() - self.isAlphaListrecording = nil -end -function LevelCompactAIDriver:setIsAlphaListrecording() - self.isAlphaListrecording = true - self.alphaList ={} -end -function LevelCompactAIDriver:getDiffHeightforHeight(targetHeight) - local blade = self.leveler - local bladeX,bladeY,bladeZ = getWorldTranslation(self:getLevelerNode(blade)) - local bladeTerrain = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, bladeX,bladeY,bladeZ); - local _,_,offSetZ = worldToLocal(self.vehicle.rootNode,bladeX,bladeY,bladeZ) - local _,projectedTractorY,_ = localToWorld(self.vehicle.rootNode,0,0,offSetZ) - - return targetHeight- (projectedTractorY-bladeTerrain) -end - - -function LevelCompactAIDriver:recordAlphaList() - local blade = self.leveler - local spec = blade.spec_attacherJointControl - local jointDesc = spec.jointDesc - local bladeX,bladeY,bladeZ = getWorldTranslation(self:getLevelerNode(blade)) - local bladeTerrain = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, bladeX,bladeY,bladeZ); - local _,_,offSetZ = worldToLocal(self.vehicle.rootNode,bladeX,bladeY,bladeZ) - local _,projectedTractorY,_ = localToWorld(self.vehicle.rootNode,0,0,offSetZ) - local tractorToGround = courseplay:round(projectedTractorY-bladeTerrain,3) - local bladeToGound = courseplay:round(bladeY-bladeTerrain,3) - - if spec.heightController.moveAlpha ~= jointDesc.lowerAlpha then - spec.heightTargetAlpha = jointDesc.lowerAlpha - blade:controlAttacherJoint(spec.controls[2], spec.controls[2].moveAlpha + 0.1) - else - blade:controlAttacherJoint(spec.controls[2], spec.controls[2].moveAlpha - 0.005) - - --record the related alphas to the alpha list - local alphaEntry = courseplay:round(bladeToGound-tractorToGround,3) - if self.alphaList[alphaEntry] ~= nil then - self:debug("resetIsAlphaListrecording") - self:resetIsAlphaListrecording() - else - self:debug(string.format("self.alphaList[%s] = %s",tostring(alphaEntry),tostring(spec.controls[2].moveAlpha))) - self.alphaList[alphaEntry] = spec.controls[2].moveAlpha - end - end -end - function LevelCompactAIDriver:getLevelerNode(blade) for _, levelerNode in pairs (blade.spec_leveler.nodes) do if levelerNode.node ~= nil then @@ -749,120 +628,93 @@ function LevelCompactAIDriver:printMap() end end --- TODO: create a BunkerSiloMap class ... --- Find the first row in the map where this column is not empty -function LevelCompactAIDriver:findFirstNonEmptyRow(map, column) - for i, row in ipairs(map) do - if row[column].fillLevel > 0 then - return i - end - end - return #map -end - function LevelCompactAIDriver:getBestTargetFillUnitCompacting(lastDrivenColumn) + local numColumns = self.bunkerSiloManager:getNumberOfColumns() local newBestTarget = {} - local firstLine = 1 - local siloMap = self.bunkerSiloManager:getSiloMap() - if siloMap ~= nil then - local newColumn = lastDrivenColumn and lastDrivenColumn + 1 or 1 - if newColumn > #siloMap[1] then - newColumn = 1 - end - local newBestTarget= { - line = 1, - column = newColumn, - empty = false - } - return newBestTarget, firstLine + local newColumn = lastDrivenColumn and lastDrivenColumn + 1 or 1 + if newColumn > numColumns then + newColumn = 1 end + local newBestTarget= { + line = 1, + column = newColumn, + empty = false + } + return newBestTarget, 1 end --- get the bestTarget, firstLine of the bestTarget work with ---@param int lastDrivenColumn of the silo ---@return bestTarget, firstLine of the bestTarget function LevelCompactAIDriver:getBestTargetFillUnitFillUp(lastDrivenColumn) - local siloMap = self.bunkerSiloManager:getSiloMap() - + local numColumns = self.bunkerSiloManager:getNumberOfColumns() local newColumn = lastDrivenColumn and lastDrivenColumn + 1 or 1 - if newColumn > #siloMap[1] then + if newColumn > numColumns then newColumn = 1 end - local firstLine = 1 + local firstLineWithFillLevel = self.bunkerSiloManager:getFirstSiloPartLineWithFillLevelForColumn(newColumn) local bestTarget = { - line = 1; + line = firstLineWithFillLevel; column = newColumn; empty = true; } - -- find column with most fillLevel and figure out whether it is empty - for lineIndex, line in pairs(siloMap) do - local fillUnit = siloMap[lineIndex][newColumn] - if fillUnit.fillLevel > 0 then - bestTarget = { - line = lineIndex; - column = newColumn; - empty = false; - } - firstLine = bestTarget.line - break - end - end - return bestTarget, firstLine + return bestTarget, firstLineWithFillLevel end function LevelCompactAIDriver:getBestTargetFillUnitLeveling(lastDrivenColumn) + local numColumns = self.bunkerSiloManager:getNumberOfColumns() local siloMap = self.bunkerSiloManager:getSiloMap() local firstLine = 1 local targetHeight = 0.5 local vehicle = self.vehicle local newApproach = lastDrivenColumn == nil local newBestTarget = {} - if siloMap ~= nil then - local newColumn = math.ceil(#siloMap[1]/2) - if newApproach then - newBestTarget, firstLine = self.bunkerSiloManager:getBestTargetFillUnitFillUp(self.bestTarget) - self:debug('Best leveling target at line %d, column %d, height %d, first line %d (fist approach)', - newBestTarget.line, newBestTarget.column, targetHeight, firstLine) - return newBestTarget, firstLine, targetHeight - else - newColumn = lastDrivenColumn + 1; - if newColumn > #siloMap[1] then - newColumn = 1; - end - firstLine = self:findFirstNonEmptyRow(siloMap, newColumn) - newBestTarget= { - line = firstLine; - column = newColumn; - empty = false; - } + + local newColumn = math.ceil(numColumns/2) + if newApproach then + newBestTarget, firstLine = self.bunkerSiloManager:getBestTargetFillUnitFillUp() + self:debug('Best leveling target at line %d, column %d, height %d, first line %d (fist approach)', + newBestTarget.line, newBestTarget.column, targetHeight, firstLine) + return newBestTarget, firstLine, targetHeight + else + newColumn = lastDrivenColumn + 1; + if newColumn > numColumns then + newColumn = 1; end - targetHeight = self:getColumnsTargetHeight(newColumn) - end + firstLine = self.bunkerSiloManager:getFirstSiloPartLineWithFillLevelForColumn(newColumn) + newBestTarget= { + line = firstLine; + column = newColumn; + empty = false; + } + end + targetHeight = self:getColumnsTargetHeight(newColumn) self:debug('Best leveling target at line %d, column %d, height %d, first line %d', newBestTarget.line, newBestTarget.column, targetHeight, firstLine) return newBestTarget, firstLine, targetHeight end function LevelCompactAIDriver:getColumnsTargetHeight(newColumn) - local totalArea = 0 - local totalFillLevel = 0 - local siloMap = self.bunkerSiloManager:getSiloMap() - for i=1,#siloMap do - --calculate the area without first and last line - if i~= 1 and i~= #siloMap then - totalArea = totalArea + siloMap[i][newColumn].area - end - totalFillLevel = totalFillLevel + siloMap[i][newColumn].fillLevel - end - local newHeight = math.max(0.6,(totalFillLevel/1000)/totalArea) - self:debug("getTargetHeight: totalFillLevel:%s; totalArea:%s Height%s",tostring(totalFillLevel),tostring(totalArea),tostring(newHeight)) + local totalArea = self.bunkerSiloManager:getSiloPartColumnArea(newColumn) + local totalFillLevel = self.bunkerSiloManager:getSiloPartColumnFillLevel(newColumn) + + local newHeight = (totalFillLevel/1000)/totalArea + self:debug("getColumnsTargetHeight: totalFillLevel:%.2f; totalArea:%.2f Height%.2f",totalFillLevel,totalArea,newHeight) return newHeight - end -function LevelCompactAIDriver:debugRouting() - if self:isDebugActive() and self.bunkerSiloManager then - self.bunkerSiloManager:debugRouting(self.bestTarget) +---Debug of AIVehicleUtil.driveInDirection() pathFinding +---and also target silo unit and targetHeight of the shield +---@param float (optional) gx/gz temporary goal node +function LevelCompactAIDriver:debugRouting(gx,gz) + if self:isDebugActive() then + if self.bunkerSiloManager then + self.bunkerSiloManager:debugRouting(self.bestTarget,nil,self.targetHeight) + end + if gx and gz then + local x,y,z = getWorldTranslation(self.vehicle.cp.directionNode) + cpDebug:drawLine(x,y+3,z,0,0,0,gx,y+3,gz) + end end end @@ -933,3 +785,140 @@ function LevelCompactAIDriver:renderText(y,text,xOffset) return y-0.02 end +---If a leveler is attached, then we don't need the normal joint control +function LevelCompactAIDriver:isShieldJointControlDisabled() + return self.leveler ~= nil +end + +---Is the driver actively pushing into the silo in mode: leveling,fillUp ? +---@return boolean is pushing into silo, so allow lowering of shield +function LevelCompactAIDriver:isShieldLoweringAllowed() + return self.fillUpState == self.states.PUSH and (self.levelState == self.states.DRIVE_SILOFILLUP or self.levelState == self.states.DRIVE_SILOLEVEL) +end + +---Update shield height and rotation +function LevelCompactAIDriver:updateShieldHeight(dt) + if not self:isShieldJointControlDisabled() then + return + end + local shield = self.leveler + local spec = shield.spec_attacherJointControl + if shield and spec then + local jointDesc = spec.jointDesc + local objectAttacherJoint = shield.spec_attachable.attacherJoint + if self:isShieldLoweringAllowed() then + local levelerNode = self:getLevelerNode(shield) + local x,y,z = getWorldTranslation(levelerNode) + local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x,y,z) + ---target height of leveling, fill up is 0 by default + local targetHeight = self:getTargetShieldHeight() + + --safety check to make sure shieldHeightOffset ~= nil + if self.shieldHeightOffset == nil then + self.shieldHeightOffset = 0 + end + + self:updateShieldHeightOffset() + --get the height difference that needs to be adjusted form the shield leveler node to the ground + local heightDiff = terrainHeight+self.shieldHeightOffset+targetHeight-y + + --[[ In the long term it should be safer to calculate the new alpha directly, + instead of adjusting the alpha by a constant. + This is currently not working as the shield then tends to toggle between going up and down repeatedly. + + ---Reference: AttacherJoints:calculateAttacherJointMoveUpperLowerAlpha(jointDesc, object) + local dx, dy, dz = localToLocal(jointDesc.jointTransform, jointDesc.rootNode, 0, 0, 0) + local delta = jointDesc.lowerDistanceToGround - dy + local ax,ay,az = localToLocal(jointDesc.jointTransform,levelerNode,0,heightDiff,0) + local hx, hy, hz = localToLocal(jointDesc.jointTransform, jointDesc.rootNode, ax, ay, az) + local lowerDistanceToGround = hy + delta + + --calculate the target alpha + local alpha = MathUtil.clamp((lowerDistanceToGround - jointDesc.upperDistanceToGround) / (jointDesc.lowerDistanceToGround - jointDesc.upperDistanceToGround), 0, 1) + self:debug("lastCurAlpha: %.2f, nextAlpha: %.2f, heightDiff: %.2f",spec.lastHeightAlpha,alpha,heightDiff) + self:debug("terrainHeight: %.2f,shieldHeight: %.2f, shieldHeightOffset: %.2f, targetHeight: %.2f",terrainHeight,y,self.shieldHeightOffset,targetHeight) + + ]]-- + self:debug("heightDiff: %.2f, shieldHeightOffset: %.2f, targetHeight: %.2f",heightDiff,self.shieldHeightOffset,targetHeight) + local curAlpha = spec.heightController.moveAlpha + --For now we are only adjusting the shield height by a constant + --heightDiff > -0.04 means we are under the target height, for example in fillUp modi below the ground offset by 0.04 + if heightDiff > -0.04 then + spec.heightTargetAlpha = curAlpha - 0.05 + --heightDiff < -0.12 means we are above the target height by 0.12, which also is used to minimize going up and down constantly + elseif heightDiff < -0.12 then + spec.heightTargetAlpha = curAlpha + 0.05 + else + --shield is in valid height scope, so we stop all movement + spec.heightTargetAlpha =-1 + end + --TODO: maybe change the shield tilt angle relative to the shield height alpha + + --rotate shield to standing on ground position, should roughly be 90 degree to ground by default + --tilt the shield relative to the additional shield height offset + --added a factor of 2 to make sure the shield is getting tilted enough + local targetAngle = math.min(spec.maxTiltAngle*self.shieldHeightOffset*2,spec.maxTiltAngle) + self:controlShieldTilt(dt,jointDesc,spec.maxTiltAngle,targetAngle) + else + self.shieldHeightOffset = 0 + spec.heightTargetAlpha = jointDesc.upperAlpha + -- --move shield to upperPosition and rotate it up + self:controlShieldTilt(dt,jointDesc,spec.maxTiltAngle,spec.maxTiltAngle) + end + end +end + +---Controls the tilt of the shield, as giants doesn't have implement a function for tilting the shield smoothly +---@param float dt +---@param table jointDesc of the vehicle +---@param float max tilt angle +---@param float target tilt angle +function LevelCompactAIDriver:controlShieldTilt(dt,jointDesc,maxTiltAngle,targetAngle) + local curAngle = jointDesc.upperRotationOffset-jointDesc.upperRotationOffsetBackup + local diff = curAngle - targetAngle + 0.0001 + local moveTime = diff / maxTiltAngle * jointDesc.moveTime + local moveStep = dt / moveTime * diff + if diff > 0 then + moveStep = -moveStep + end + local newAngle = targetAngle + moveStep/10 + jointDesc.upperRotationOffset = jointDesc.upperRotationOffsetBackup - newAngle + jointDesc.lowerRotationOffset = jointDesc.lowerRotationOffsetBackup - newAngle +end + +---If the driver is slower than 2 km/h, then move the shield slowly up (increase self.shieldHeightOffset) +function LevelCompactAIDriver:updateShieldHeightOffset() + local lastSpeed = self.vehicle.lastSpeedReal + local minSpeed = 2 + local maxSpeed = math.max(4,math.floor(self.vehicle.cp.settings.bunkerSpeed:get()*0.7)) + if lastSpeed < minSpeed then + if self:isShieldFull() then + self.shieldHeightOffset = self.shieldHeightOffset + 0.05 + end + elseif lastSpeed > maxSpeed then + self.shieldHeightOffset = math.max(self.shieldHeightOffset - 0.05,0) + end +end + +---Get the target height for the shield +---@return float targetHeight +function LevelCompactAIDriver:getTargetShieldHeight() + return self.targetHeight or 0 +end + +---Disables player control of shield while driver is driving +function LevelCompactAIDriver.actionEventAttacherJointControl(self,superFunc, actionName, inputValue, callbackState, isAnalog) + local rootVehicle = self:getRootVehicle() + if courseplay:isAIDriverActive(rootVehicle) then + if rootVehicle.cp.driver.isShieldJointControlDisabled and rootVehicle.cp.driver:isShieldJointControlDisabled() then + return + end + end + superFunc(self,actionName, inputValue, callbackState, isAnalog) +end +AttacherJointControl.actionEventAttacherJointControl = Utils.overwrittenFunction(AttacherJointControl.actionEventAttacherJointControl,LevelCompactAIDriver.actionEventAttacherJointControl) + +---Is a shield attached ? +function LevelCompactAIDriver:hasShield() + return self.leveler ~= nil +end \ No newline at end of file diff --git a/ShovelModeAIDriver.lua b/ShovelModeAIDriver.lua index ddcfe2492..ba05d8408 100644 --- a/ShovelModeAIDriver.lua +++ b/ShovelModeAIDriver.lua @@ -61,6 +61,11 @@ ShovelModeAIDriver.myStates = { STATE_GO_BACK_FROM_EMPTYPOINT = {}, STATE_WORK_FINISHED = {} } +ShovelModeAIDriver.SHOVEL_POSITIONS = {} +ShovelModeAIDriver.SHOVEL_POSITIONS.LOADING = 1 +ShovelModeAIDriver.SHOVEL_POSITIONS.TRANSPORT = 2 +ShovelModeAIDriver.SHOVEL_POSITIONS.PRE_UNLOADING = 3 +ShovelModeAIDriver.SHOVEL_POSITIONS.UNLOADING = 4 --- Constructor @@ -98,30 +103,11 @@ function ShovelModeAIDriver:start() end --finding my working points local vehicle = self.vehicle - self.shovelFillStartPoint = nil - self.shovelFillEndPoint = nil - self.shovelEmptyPoint = nil - local numWaitPoints = 0 - self.bestTarget = nil + self:validateWaitpoints() + self:resetSiloData() self.bunkerSiloManager = nil self.unloadingObjectRaycastActive = false - for i,wp in pairs(vehicle.Waypoints) do - if wp.wait then - numWaitPoints = numWaitPoints + 1; - vehicle.cp.waitPoints[numWaitPoints] = i; - end; - - if numWaitPoints == 1 and self.shovelFillStartPoint == nil then - self.shovelFillStartPoint = i; - end; - if numWaitPoints == 2 and self.shovelFillEndPoint == nil then - self.shovelFillEndPoint = i; - end; - if numWaitPoints == 3 and self.shovelEmptyPoint == nil then - self.shovelEmptyPoint = i; - end; - end; - + self.trailerCallback = nil local vAI = vehicle:getAttachedImplements() for i,_ in pairs(vAI) do local object = vAI[i].object @@ -147,13 +133,35 @@ end function ShovelModeAIDriver:shouldStopAtEndOfCourse() return false end +---Checks and sets all valid Waitpoints +function ShovelModeAIDriver:validateWaitpoints() + self.shovelFillStartPoint = nil + self.shovelFillEndPoint = nil + self.shovelEmptyPoint = nil + local numWaitPoints = 0 + for i,wp in pairs(self.vehicle.Waypoints) do + if wp.wait then + numWaitPoints = numWaitPoints + 1; + end; + + if numWaitPoints == 1 and self.shovelFillStartPoint == nil then + self.shovelFillStartPoint = i; + end; + if numWaitPoints == 2 and self.shovelFillEndPoint == nil then + self.shovelFillEndPoint = i; + end; + if numWaitPoints == 3 and self.shovelEmptyPoint == nil then + self.shovelEmptyPoint = i; + end; + end; +end --debug info function ShovelModeAIDriver:onDraw() if self:isDebugActive() and self.shovel then local y = 0.5 y = self:renderText(y,"state: "..tostring(self.shovelState.name),0.4) - y = self:renderText(y,"hasbunkerSiloManager: "..tostring(self.bunkerSiloManager ~= nil),0.4) + y = self:renderText(y,"hasBunkerSiloManager: "..tostring(self.bunkerSiloManager ~= nil),0.4) y = self:renderText(y,"hasBestTarget: "..tostring(self.bestTarget ~= nil),0.4) y = self:renderText(y,"isShovelFull: "..tostring(self:getIsShovelFull() == true),0.4) y = self:renderText(y,"isShovelEmpty: "..tostring(self:getIsShovelEmpty() == true),0.4) @@ -178,22 +186,38 @@ function ShovelModeAIDriver:drive(dt) --get the relevant bunkerSilo/Heap data if self.shovelState == self.states.STATE_CHECKSILO then self:hold() - if self:setShovelToPositionFinshed(2,dt) then - --initialize first target point - if self.bunkerSiloManager == nil then - local silo,isHeap = BunkerSiloManagerUtil.getTargetBunkerSilo(self.vehicle,nil,true) + if self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.LOADING,dt) then + --if bunkerSiloManager is nil, then search for a silo/heap + if self.bunkerSiloManager == nil then + local silo,isHeap = self:getTargetBunkerSilo() + --silo/heap was found if silo then - self.bunkerSiloManager = BunkerSiloManager(self.vehicle, silo, self:getWorkWidth(),self.shovel.rootNode,isHeap) + self.bunkerSiloManager = BunkerSiloManager(self.vehicle, silo, self:getWorkWidth(),self.shovel.rootNode,BunkerSiloManager.MODE.SHOVEL,isHeap) + else + self:debug("no silo was found") + -- courseplay:setInfoText(self.vehicle, courseplay:loc('COURSEPLAY_MODE10_NOSILO')) + self:setShovelState(self.states.STATE_WORK_FINISHED) end end - if self.bunkerSiloManager and self.bestTarget == nil then - self.bestTarget, self.firstLine = self.bunkerSiloManager:getBestTargetFillUnitFillUp(self.bestTarget) + ---if bunkerSiloManager and siloMap are valid then search for best target + if self.bunkerSiloManager and self.bunkerSiloManager:isSiloMapValid() then + self.bestTarget, self.firstLine = self.bunkerSiloManager:getBestTargetFillUnitFillUp() + --best target was found => STATE_GOINTO_SILO + if self.bestTarget then + self:setShovelState(self.states.STATE_GOINTO_SILO) + else + self.bunkerSiloManager = nil + self:resetBGASiloTables() + self:debug("could not find best target") + self:setShovelState(self.states.STATE_WORK_FINISHED) + end + else + self.bunkerSiloManager = nil + self:resetBGASiloTables() + self:debug("silo map setup is not valid") + self:setShovelState(self.states.STATE_WORK_FINISHED) end end - self:drawMap() - if self.bunkerSiloManager and self.bestTarget then - self:setShovelState(self.states.STATE_GOINTO_SILO) - end --driving into the bunkerSilo elseif self.shovelState == self.states.STATE_GOINTO_SILO then self.refSpeed = self.vehicle.cp.speeds.field @@ -209,14 +233,16 @@ function ShovelModeAIDriver:drive(dt) if self:getIsShovelFull() or self.bunkerSiloManager:isAtEnd(self.bestTarget) or self:isStuck() then --driving back out of the bunkerSilo if self:getTargetIsOnBunkerWallColumn() then + ---create a temporary course, if the last target was near a bunker wall, + ---this one is only allowed if the silo map has at least two lines self.tempTarget = self:getTargetToStraightOut() self:setShovelState(self.states.STATE_REVERSE_STRAIGHT_OUT_OF_SILO) else - local _,_,Zoffset = self.course:getWaypointLocalPosition(self.vehicle.cp.directionNode, self.shovelFillStartPoint) - local newPoint = self.course:getNextRevWaypointIxFromVehiclePosition(self.ppc:getCurrentWaypointIx(), self.vehicle.cp.directionNode,-Zoffset) + local directionNode = self:getDirectionNode() + local _,_,Zoffset = self.course:getWaypointLocalPosition(directionNode, self.shovelFillStartPoint) + local newPoint = self.course:getNextRevWaypointIxFromVehiclePosition(self.ppc:getCurrentWaypointIx(), directionNode,-Zoffset) self.ppc:initialize(newPoint) self:setShovelState(self.states.STATE_REVERSE_OUT_OF_SILO) - self.bestTarget = nil end end @@ -224,22 +250,23 @@ function ShovelModeAIDriver:drive(dt) --driving back out of the bunkerSilo elseif self.shovelState == self.states.STATE_REVERSE_STRAIGHT_OUT_OF_SILO then self.refSpeed = self.vehicle.cp.speeds.reverse - if not self:setShovelToPositionFinshed(3,dt) then + if not self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.TRANSPORT,dt) then self:hold() end self:drawMap() if self:getIsReversedOutOfSilo() then - local _,_,Zoffset = self.course:getWaypointLocalPosition(self.vehicle.cp.directionNode, self.shovelFillStartPoint) - local newPoint = self.course:getNextRevWaypointIxFromVehiclePosition(self.ppc:getCurrentWaypointIx(), self.vehicle.cp.directionNode,-Zoffset) + local directionNode = self:getDirectionNode() + local _,_,Zoffset = self.course:getWaypointLocalPosition(directionNode, self.shovelFillStartPoint) + local newPoint = self.course:getNextRevWaypointIxFromVehiclePosition(self.ppc:getCurrentWaypointIx(), directionNode,-Zoffset) self.ppc:initialize(newPoint) self:setShovelState(self.states.STATE_TRANSPORT) - self.bestTarget = nil end --drive to temp target if self.tempTarget then local cx,cz = self.tempTarget.cx,self.tempTarget.cz local cy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, cx, 1, cz); - local lx, lz = AIVehicleUtil.getDriveDirection(self.vehicle.cp.directionNode, cx,cy,cz); + local directionNode = self:getDirectionNode() + local lx, lz = AIVehicleUtil.getDriveDirection(directionNode, cx,cy,cz); lx,lz = -lx,-lz; self:driveInDirection(dt,lx,lz,false,self:getSpeed(),true) self:debugRouting() @@ -248,7 +275,7 @@ function ShovelModeAIDriver:drive(dt) --driving back out of the bunkerSilo elseif self.shovelState == self.states.STATE_REVERSE_OUT_OF_SILO then self.refSpeed = self.vehicle.cp.speeds.reverse - if not self:setShovelToPositionFinshed(3,dt) then + if not self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.TRANSPORT,dt) then self:hold() end if not self.course:isReverseAt(self.ppc:getCurrentWaypointIx()) then @@ -265,7 +292,7 @@ function ShovelModeAIDriver:drive(dt) self:disableTrafficConflictDetection() end --backup for starting somewhere in between - if not self:setShovelToPositionFinshed(3,dt) then + if not self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.TRANSPORT,dt) then self:hold() end -- close to the unload waitpoint, so set pre unload shovel position and do a raycast for unload triggers, trailers @@ -311,6 +338,13 @@ function ShovelModeAIDriver:updateTick(dt) end end +---Reset all relevant silo data +function ShovelModeAIDriver:resetSiloData() + self.bestTarget = nil + self.tempTarget = nil + self.firstLine = nil +end + function ShovelModeAIDriver:isStuck() if self:doesNotMove() then if self.vehicle.cp.timers.slipping == nil or self.vehicle.cp.timers.slipping == 0 then @@ -340,7 +374,7 @@ function ShovelModeAIDriver:driveWaitForTarget(dt) if self.course:getDistanceBetweenVehicleAndWaypoint(self.vehicle, self.shovelEmptyPoint) < 10 then self:hold() end - if self:setShovelToPositionFinshed(4,dt) then + if self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.PRE_UNLOADING,dt) then --search for UnloadStation(UnloadTrigger) or correct Trailer ahead, else wait self.unloadingObjectRaycastActive = true end @@ -352,30 +386,35 @@ function ShovelModeAIDriver:driveStartUnload(dt) self.unloadingObjectRaycastActive = false self.refSpeed = self:getDriveStartUnloadRefSpeed() local currentDischargeNode = self.shovel:getCurrentDischargeNode() - -- if shovel is empty we can return direct back from the trigger + -- if shovel is empty we can drive directly back from the trigger if self:getIsShovelEmpty() then - self:setShovelState(self.states.STATE_WAIT_FOR_UNLOADREADY); - end - -- if we can discharge at unload trigger or trailer has enough free space - if self.shovel:getCanDischargeToObject(currentDischargeNode) and currentDischargeNode.dischargeObject and self:hasEnoughSpaceInObject(currentDischargeNode) then - if self:setShovelToPositionFinshed(5,dt) then - self:setShovelState(self.states.STATE_WAIT_FOR_UNLOADREADY); - end; + --wait until shovel position 3 is reached + self:sendUnloaderBackFromEmptyPoint() self:hold() - -- if there is no more free space move shovel back to pre unload position - elseif currentDischargeNode.dischargeObject or currentDischargeNode.dischargeFailedReason == Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY then - self:setShovelToPositionFinshed(4,dt) + return false + end + -- discharge node can unload and dischargeObject was found + if self.shovel:getCanDischargeToObject(currentDischargeNode) and currentDischargeNode.dischargeObject then + --enough free space in object found + if self:hasEnoughSpaceInObject(currentDischargeNode) then + if self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.UNLOADING,dt) then + self:setShovelState(self.states.STATE_WAIT_FOR_UNLOADREADY) + end + --not enough free space + else + self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.PRE_UNLOADING,dt) + end self:hold() - --drive in straight line to waitPoint/UnloadStation(UnloadTrigger) - elseif not self:getIsShovelEmpty() then + --trigger not yet reached + else if self.course:getDistanceToNextWaypoint(self.shovelEmptyPoint) <2 then -- the last 2m we drive straight to the unload trigger - notAllowedToDrive = true local gx, _, gz = self.course:getWaypointLocalPosition(self:getDirectionNode(),self.shovelEmptyPoint) self:driveVehicleToLocalPosition(dt, true, true, gx, gz, self.refSpeed) + return true end end - return notAllowedToDrive + return false end -- handle unloading at trigger @@ -384,10 +423,8 @@ function ShovelModeAIDriver:driveWaitForUnloadReady(dt) local dischargeNode = self.shovel:getCurrentDischargeNode() -- drive back to the course if self:getIsShovelEmpty() then - if self:setShovelToPositionFinshed(4,dt) then - local newPoint = self.course:getNextRevWaypointIxFromVehiclePosition(self.ppc:getCurrentWaypointIx(), self.vehicle.cp.directionNode, 3 ) - self.ppc:initialize(newPoint) - self:setShovelState(self.states.STATE_GO_BACK_FROM_EMPTYPOINT); + if self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.PRE_UNLOADING,dt) then + self:sendUnloaderBackFromEmptyPoint() end --stop unloading at unload trigger if there is no more free space elseif not self.shovel:getCanDischargeToObject(dischargeNode) or self:almostFullObject(dischargeNode) then @@ -396,27 +433,64 @@ function ShovelModeAIDriver:driveWaitForUnloadReady(dt) end ------trailer --- drive to the unload trigger/ trailer +---drive to the unload trigger/ trailer function ShovelModeAIDriver:driveStartUnloadTrailer(dt) self.unloadingObjectRaycastActive = false self.refSpeed = self:getDriveStartUnloadRefSpeed() local dischargeNode = self.shovel:getCurrentDischargeNode() - -- if we can discharge at trailer - if self.shovel:getCanDischargeToObject(dischargeNode) and dischargeNode.dischargeObject then - if self:setShovelToPositionFinshed(5,dt) then - self:setShovelState(self.states.STATE_WAIT_FOR_UNLOADREADY_TRAILER); - end; - self:hold() - --drive in straight line to waitPoint/trailer - elseif not self:getIsShovelEmpty() then - if self.course:getDistanceToNextWaypoint(self.shovelEmptyPoint) <2 then - -- the last 2m we drive straight to the unload trailer - notAllowedToDrive = true - local gx, _, gz = self.course:getWaypointLocalPosition(self:getDirectionNode(),self.shovelEmptyPoint) - self:driveVehicleToLocalPosition(dt, true, true, gx, gz, self.refSpeed) + + if self:getIsShovelEmpty() then + self:sendUnloaderBackFromEmptyPoint() + return + end + if self.trailerCallback then + ---drive to exactFillRootNode and stop if the attacherNode of the shovel is roughly near it + if self.course:getDistanceToNextWaypoint(self.shovelEmptyPoint) <5 then + local exactFillRootNode = self.trailerCallback.exactFillRootNode + local trailer = self.trailerCallback.trailer + local inputAttacherJoints = self.shovel:getInputAttacherJoints() + local relevantAttacherJointNode = inputAttacherJoints[1].node + ---offset between shovel attacherNode and dischargeNode + local _,_,offsetZ = localToLocal(relevantAttacherJointNode,dischargeNode.node,0,0,0) + ---get the distance between exactFillRootNode and dischargeNode in z direction of the dischargeNode + local nx,ny,nz = localToLocal(exactFillRootNode,dischargeNode.node,0,0,offsetZ) + ---get the world coordinates + local ax,ay,az = localToWorld(dischargeNode.node,0,0,offsetZ) + local dx,dy,dz = localToWorld(exactFillRootNode,0,0,0) + if self:isDebugActive() then + cpDebug:drawLine(dx,dy,dz,1,0,1,ax,ay,az) + DebugUtil.drawDebugNode(relevantAttacherJointNode, 'relevantAttacherJointNode') + DebugUtil.drawDebugNode(exactFillRootNode, 'exactFillRootNode') + DebugUtil.drawDebugNode(dischargeNode.node, 'dischargeNode.node') + end + self:debug("distanceToTrailer(nz): %.2f,offsetShovelToAttacherNode(offsetZ): %.2f",nz,offsetZ) + if nz > 0 then + ---shovel attacherNode is not near enough to the exactFillRootNode + ---get the direction of the dischargeNode to the exactFillRootNode(dx,dy,dz) + local lx, lz = AIVehicleUtil.getDriveDirection(dischargeNode.node, dx,dy,dz); + local MIN_SPEED = 4 + local speed = MathUtil.clamp(nz,MIN_SPEED,self.refSpeed) + self:driveInDirection(dt,lx,lz,true,speed,true) + -- the last 2m we drive straight to the unload trailer + + return true + else + ---position is reached, so start unloading if possible down below + self:debug("self.trailerCallback=>nil") + self.trailerCallback = nil + self:setSpeed(0) + end + end + else + -- if we can discharge at trailer + if self.shovel:getCanDischargeToObject(dischargeNode) and dischargeNode.dischargeObject then + if self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.UNLOADING,dt) then + self:setShovelState(self.states.STATE_WAIT_FOR_UNLOADREADY_TRAILER) + end end + self:hold() end - return notAllowedToDrive + return false end -- handle unloading @@ -425,21 +499,26 @@ function ShovelModeAIDriver:driveWaitForUnloadReadyTrailer(dt) local dischargeNode = self.shovel:getCurrentDischargeNode() --drive back to the course if self:getIsShovelEmpty() or not self.shovel:getCanDischargeToObject(dischargeNode) or dischargeNode.dischargeFailedReason == Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY then - if self:setShovelToPositionFinshed(4,dt) then - local newPoint = self.course:getNextRevWaypointIxFromVehiclePosition(self.ppc:getCurrentWaypointIx(), self.vehicle.cp.directionNode, 3 ) - self.ppc:initialize(newPoint) - self:setShovelState(self.states.STATE_GO_BACK_FROM_EMPTYPOINT); - end + self:sendUnloaderBackFromEmptyPoint() end end ------ +function ShovelModeAIDriver:sendUnloaderBackFromEmptyPoint() + if self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.PRE_UNLOADING,dt) then + local directionNode = self:getDirectionNode() + local newPoint = self.course:getNextRevWaypointIxFromVehiclePosition(self.ppc:getCurrentWaypointIx(), directionNode, 3 ) + self.ppc:initialize(newPoint) + self:setShovelState(self.states.STATE_GO_BACK_FROM_EMPTYPOINT) + end +end + -- reverse back to the course function ShovelModeAIDriver:driveGoBackFromEmptyPoint(dt) self.refSpeed = self.vehicle.cp.speeds.reverse if not self.course:isReverseAt(self.ppc:getCurrentWaypointIx()) then - if not self:setShovelToPositionFinshed(3,dt) then + if not self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.TRANSPORT,dt) then --self:hold() else self:setShovelState(self.states.STATE_TRANSPORT) @@ -456,9 +535,9 @@ function ShovelModeAIDriver:getDriveStartUnloadRefSpeed() return self.vehicle.cp.speeds.turn end ---check for enough free space to start unloading +---Check if the current dischargeObject has enough free space ---@param dischargeNode dischargeNode of the shovel ----@return boolean has enough free space +---@return boolean object has enough free space, freeSpace > self:getMinNeededFreeCapacity() function ShovelModeAIDriver:hasEnoughSpaceInObject(dischargeNode) local fillType = self.shovel:getDischargeFillType(dischargeNode) local object = dischargeNode.dischargeObject @@ -472,9 +551,10 @@ function ShovelModeAIDriver:hasEnoughSpaceInObject(dischargeNode) end end ---check if no more free space here +---Check if the current dischargeObject is almost full ? ---@param dischargeNode dischargeNode of the shovel ----@return boolean not enough free space left +---@return boolean object is almost full, free capacity < 300*1/self.shovel:getDischargeNodeEmptyFactor() +--- the factor is used to compensate for shovels that unload to fast function ShovelModeAIDriver:almostFullObject(dischargeNode) local fillType = self.shovel:getDischargeFillType(dischargeNode) local object = dischargeNode.dischargeObject @@ -484,17 +564,12 @@ function ShovelModeAIDriver:almostFullObject(dischargeNode) end function ShovelModeAIDriver:driveIntoSilo(dt) - if self.bunkerSiloManager == nil then - return - end local vehicle = self.vehicle local fwd = true; local allowedToDrive = true - local cx ,cy,cz = 0,0,0 --get coords of the target point - local targetUnit = self.bunkerSiloManager.siloMap[self.bestTarget.line][self.bestTarget.column] - cx ,cz = targetUnit.cx, targetUnit.cz - cy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, cx, 1, cz); + local cx,cz = self.bunkerSiloManager:getSiloPartPosition(self.bestTarget.line,self.bestTarget.column) + local cy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, cx, 1, cz); --check whether its time to change the target point self.bunkerSiloManager:updateTarget(self.bestTarget) @@ -508,7 +583,8 @@ function ShovelModeAIDriver:driveIntoSilo(dt) end --drive - local lx, lz = AIVehicleUtil.getDriveDirection(self.vehicle.cp.directionNode, cx,cy,cz); + local directionNode = self:getDirectionNode() + local lx, lz = AIVehicleUtil.getDriveDirection(directionNode, cx,cy,cz); self:debugRouting() self:driveInDirection(dt,lx,lz,fwd,self:getSpeed(),allowedToDrive) end @@ -528,10 +604,10 @@ function ShovelModeAIDriver:getCanGoWithStreetSpeed() end -- check and set the needed shovel position ----@param int next shovel position offset by +1 because of old code +---@param int next shovel position ---@return boolean has reached shovel position ? function ShovelModeAIDriver:setShovelToPositionFinshed(stage,dt) - return self.vehicle.cp.settings.frontloaderToolPositions:updatePositions(dt,stage-1) + return self.vehicle.cp.settings.frontloaderToolPositions:updatePositions(dt,stage) end function ShovelModeAIDriver:getIsShovelFull() @@ -543,8 +619,9 @@ function ShovelModeAIDriver:getIsShovelEmpty() end --- get the minimum required free space ----@return boolean min needed free space +---Get the minimum required free space to start unloading, +---so we are not constantly starting to unload and the stop again directly +---@return float minimum required free space, shovel min(capacity/5,fillLevel) function ShovelModeAIDriver:getMinNeededFreeCapacity() return math.min(self.shovel:getFillUnitCapacity(1)/5,self.shovel:getFillUnitFillLevel(1)) end @@ -613,8 +690,20 @@ function ShovelModeAIDriver:searchForUnloadingObjectRaycastCallback(transformId, self:debug("allowedToFillByShovel") if supportedFillType then --valid trailer/ fillableObject found + + --check if the vehicle is stopped + local rootVehicle = object:getRootVehicle() + if not AIDriverUtil.isStopped(rootVehicle) then + return + end + --- + local exactFillRootNode = object:getFillUnitExactFillRootNode(fillUnitIndex) or object.rootNode self:debug("supportedFillType") self:debug("Trailer found!") + self.trailerCallback = { + trailer = object, + exactFillRootNode = exactFillRootNode + } self:setShovelState(self.states.STATE_START_UNLOAD_TRAILER) return else @@ -665,8 +754,8 @@ function ShovelModeAIDriver:onWaypointPassed(ix) end end --- are all the shovel positions correctly set ? ----@return boolean are shovel positions okay +---Check if all shovel positions are set correctly +---@return boolean are all shovel positions valid ? function ShovelModeAIDriver:checkShovelPositionsValid() local validToolPositions = self.vehicle.cp.settings.frontloaderToolPositions:hasValidToolPositions() if not validToolPositions then @@ -675,8 +764,8 @@ function ShovelModeAIDriver:checkShovelPositionsValid() return validToolPositions end --- are all 3 needed waitpoint correctly setup ? ----@return boolean has necessary waitpoints +---Check if all wait points are set correctly +---@return boolean are all wait points valid ? function ShovelModeAIDriver:checkWaypointsValid() if self.shovelFillStartPoint == nil or self.shovelFillEndPoint == nil or self.shovelEmptyPoint == nil then courseplay:setInfoText(self.vehicle, 'COURSEPLAY_NO_VALID_COURSE'); @@ -688,7 +777,7 @@ end function ShovelModeAIDriver:checkLastWaypoint() if self.ppc:reachedLastWaypoint() then self.ppc:initialize(1) - self.bunkerSiloManager = nil + self:resetSiloData() end end @@ -696,11 +785,13 @@ function ShovelModeAIDriver:updateLastMoveCommandTime() self:resetLastMoveCommandTime() end +---old code function ShovelModeAIDriver:findNextRevWaypoint(currentPoint) local vehicle = self.vehicle; - local _,ty,_ = getWorldTranslation(vehicle.cp.directionNode); + local directionNode = self:getDirectionNode() + local _,ty,_ = getWorldTranslation(directionNode); for i= currentPoint, self.vehicle.cp.numWaypoints do - local _,_,z = worldToLocal(vehicle.cp.directionNode, vehicle.Waypoints[i].cx , ty , vehicle.Waypoints[i].cz); + local _,_,z = worldToLocal(directionNode, vehicle.Waypoints[i].cx , ty , vehicle.Waypoints[i].cz); if z < -3 and vehicle.Waypoints[i].rev then return i end; @@ -725,9 +816,17 @@ function ShovelModeAIDriver:setShovelState(state, extraText) end end; +---TODO: Do we want to use this function with heaps ? +---Was the best target directly near a bunker wall +---@return best target is directly near a bunker wall function ShovelModeAIDriver:getTargetIsOnBunkerWallColumn() - local vehicle = self.vehicle - return self.bestTarget.column == 1 or self.bestTarget.column == #self.bunkerSiloManager.siloMap[#self.bunkerSiloManager.siloMap] + local numLines,numColumns = self.bunkerSiloManager:getNumberOfLinesAndColumns() + --only allow a straight out reverse course, + --if there are at least two lines , which is not always the case with heaps + if numLines < 2 then + return false + end + return self.bestTarget.column == 1 or self.bestTarget.column == numColumns end --not used ? @@ -749,9 +848,8 @@ function ShovelModeAIDriver:getClosestPointToStartFill() return closestPoint; end function ShovelModeAIDriver:getTargetToStraightOut() - local vehicle = self.vehicle - local sX,sZ = self.bunkerSiloManager.siloMap[2][self.bestTarget.column].cx,self.bunkerSiloManager.siloMap[2][self.bestTarget.column].cz - local tX,tZ = self.bunkerSiloManager.siloMap[1][self.bestTarget.column].cx,self.bunkerSiloManager.siloMap[1][self.bestTarget.column].cz + local sX,sZ = self.bunkerSiloManager:getSiloPartPosition(2,self.bestTarget.column) + local tX,tZ = self.bunkerSiloManager:getSiloPartPosition(1,self.bestTarget.column) local dx,_,dz = courseplay:getWorldDirection(sX, 0, sZ, tX, 0, tZ) local tempTarget = { cx = sX+(dx*30); @@ -762,8 +860,9 @@ function ShovelModeAIDriver:getTargetToStraightOut() end function ShovelModeAIDriver:getIsReversedOutOfSilo() - local x,z = self.bunkerSiloManager.siloMap[1][self.bestTarget.column].cx,self.bunkerSiloManager.siloMap[1][self.bestTarget.column].cz - local px,py,pz = worldToLocal(self.vehicle.cp.directionNode,x,0,z) + local x,z = self.bunkerSiloManager:getSiloPartPosition(self.bestTarget.line,self.bestTarget.column) + local directionNode = self:getDirectionNode() + local px,py,pz = worldToLocal(directionNode,x,0,z) return pz > 4 end @@ -817,3 +916,10 @@ end function ShovelModeAIDriver:isProximitySpeedControlEnabled() return self.shovelState.properties.enableProximitySpeedControl end + +---Checks for bunker silo or heaps in between shovelFillStartPoint and shovelFillEndPoint +---@return table bunker silo or simulated heap silo +---@return boolean is the found silo a heap ? +function ShovelModeAIDriver:getTargetBunkerSilo() + return BunkerSiloManagerUtil.getTargetBunkerSiloBetweenWaypoints(self.vehicle,self.course,self.shovelFillStartPoint,self.shovelFillEndPoint,true) +end \ No newline at end of file diff --git a/TriggerShovelModeAIDriver.lua b/TriggerShovelModeAIDriver.lua index a2c7b9cea..092126418 100644 --- a/TriggerShovelModeAIDriver.lua +++ b/TriggerShovelModeAIDriver.lua @@ -89,7 +89,7 @@ function TriggerShovelModeAIDriver:drive(dt) end end --backup for starting somewhere in between - if not self:setShovelToPositionFinshed(3,dt) then + if not self:setShovelToPositionFinshed(self.SHOVEL_POSITIONS.TRANSPORT,dt) then self:hold() end -- close to the unload waitpoint, so set pre unload shovel position and do a raycast for unload triggers, trailers diff --git a/UnloadableFieldworkAIDriver.lua b/UnloadableFieldworkAIDriver.lua index e070a3228..0437058db 100644 --- a/UnloadableFieldworkAIDriver.lua +++ b/UnloadableFieldworkAIDriver.lua @@ -100,43 +100,20 @@ end ---@return boolean true if unload took over the driving function UnloadableFieldworkAIDriver:driveUnloadOrRefill(dt) self:updateOffset() - local allowedToDrive = true - local isNearUnloadPoint, unloadPointIx = self.course:hasUnloadPointWithinDistance(self.ppc:getCurrentWaypointIx(),20) - -- by default, drive street/recorded speed. - self:setSpeed(self:getRecordedSpeed()) - if not self.ppc:isReversing() then - -- 'cause reverse does the raycasting for us - self:searchForTipTriggers() - end - local takeOverSteering = FieldworkAIDriver.driveUnloadOrRefill(self) - if self.vehicle.cp.totalFillLevel > 0 then - if self:hasTipTrigger() then - -- unload at tip trigger - self:setSpeed(self.vehicle.cp.speeds.approach) - allowedToDrive, takeOverSteering = self:dischargeAtTipTrigger(dt) - courseplay:setInfoText(self.vehicle,"COURSEPLAY_TIPTRIGGER_REACHED"); - self:setSpeed(self.vehicle.cp.speeds.turn) - end - end self.triggerHandler:enableFillTypeUnloading() self.triggerHandler:enableFillTypeUnloadingBunkerSilo() - -- tractor reaches unloadPoint - if isNearUnloadPoint then - self:setSpeed(self.vehicle.cp.speeds.approach) - courseplay:setInfoText(self.vehicle, "COURSEPLAY_TIPTRIGGER_REACHED"); - allowedToDrive, takeOverSteering = self:dischargeAtUnloadPoint(dt,unloadPointIx) - end - - if not allowedToDrive then - self:setSpeed(0) - end - -- done tipping? - if self:hasTipTrigger() and self.vehicle.cp.totalFillLevel <= 10 then - courseplay:resetTipTrigger(self.vehicle, true); + -- TODO: refactor that whole unload process, it was just copied from the legacy CP code + self:searchForTipTriggers() + local allowedToDrive, giveUpControl = self:onUnLoadCourse(true, dt) + if not allowedToDrive then + self:hold() + end + if giveUpControl then + return true end - - return takeOverSteering + FieldworkAIDriver.driveUnloadOrRefill(self,dt) + return false end --- Interface for AutoDrive