diff --git a/BunkersiloManager.lua b/BunkersiloManager.lua index 2ac698b30..7e989f824 100644 --- a/BunkersiloManager.lua +++ b/BunkersiloManager.lua @@ -30,14 +30,22 @@ widthNode --> | | <-- startNode (sx,_,sz) ---@param vehicle vehicle ---@param Silo BunkerSilo or simulated HeapSilo ---@param float workwidth ----@param implement relevant workTool +---@param node targetNode is either the front/back node of the implement used to check if the bestTarget was passed ---@param boolean is the silo a heap ? -function BunkerSiloManager:init(vehicle, Silo, width, object,isHeap) +function BunkerSiloManager:init(vehicle, Silo, width, targetNode,isHeap) print("BunkerSiloManager: init()") self.siloMap = self:createBunkerSiloMap(vehicle, Silo, width,isHeap) self.silo = Silo self.vehicle = vehicle - self.object = object + self.targetNode = targetNode +end + +function BunkerSiloManager:getSiloMap() + return self.siloMap +end + +function BunkerSiloManager:getSilo() + return self.silo end ---creating the relevant siloMap @@ -262,7 +270,7 @@ function BunkerSiloManager:isAtEnd(bestTarget) local targetUnit = self.siloMap[bestTarget.line][bestTarget.column] local cx ,cz = targetUnit.cx, targetUnit.cz local cy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, cx, 1, cz); - local x,y,z = getWorldTranslation(self.object.rootNode) + 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 @@ -297,10 +305,10 @@ function BunkerSiloManager:getBestTargetFillUnitFillUp(bestTarget) end if column == #line and mostFillLevelAtLine > 0 then fillingTarget = { - line = lineIndex; - column = mostFillLevelIndex; - empty = false; - } + line = lineIndex; + column = mostFillLevelIndex; + empty = false; + } stopSearching = true break end @@ -308,10 +316,10 @@ function BunkerSiloManager:getBestTargetFillUnitFillUp(bestTarget) end if mostFillLevelAtLine == 0 then fillingTarget = { - line = 1; - column = 1; - empty = true; - } + line = 1; + column = 1; + empty = true; + } end bestTarget = fillingTarget @@ -335,7 +343,7 @@ function BunkerSiloManager:updateTarget(bestTarget) local targetUnit = self.siloMap[bestTarget.line][bestTarget.column] local cx ,cz = targetUnit.cx, targetUnit.cz local cy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, cx, 1, cz); - local x,y,z = getWorldTranslation(self.object.rootNode) + 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) diff --git a/FillableFieldworkAIDriver.lua b/FillableFieldworkAIDriver.lua index c0ff13fe9..5fbe1b2dd 100644 --- a/FillableFieldworkAIDriver.lua +++ b/FillableFieldworkAIDriver.lua @@ -67,7 +67,7 @@ function FillableFieldworkAIDriver:driveUnloadOrRefill() else self:clearInfoText('NO_SELECTED_FILLTYPE') end - local isNearWaitPoint, waitPointIx = self.course:hasWaitPointWithinDistance(self.ppc:getRelevantWaypointIx(), 10) + local isNearWaitPoint, waitPointIx = self.course:hasWaitPointWithinDistance(self.ppc:getRelevantWaypointIx(), 25) --this one is used to disable loading at the unloading stations, --might be better to disable the triggerID for loading self:enableFillTypeLoading(isNearWaitPoint) @@ -75,6 +75,7 @@ function FillableFieldworkAIDriver:driveUnloadOrRefill() -- use the courseplay speed limit until we get to the actual unload corse fields (on alignment/temporary) self:setSpeed(self.vehicle.cp.speeds.field) elseif self.refillState == self.states.TO_BE_REFILLED and isNearWaitPoint then + -- should be reworked and be similar to mode 1 loading at start local distanceToWait = self.course:getDistanceBetweenVehicleAndWaypoint(self.vehicle, waitPointIx) self:setSpeed(MathUtil.clamp(distanceToWait,self.vehicle.cp.speeds.crawl,self:getRecordedSpeed())) if distanceToWait < 1 then diff --git a/LevelCompactAIDriver.lua b/LevelCompactAIDriver.lua index e37168c8e..51d3ca076 100644 --- a/LevelCompactAIDriver.lua +++ b/LevelCompactAIDriver.lua @@ -159,18 +159,15 @@ function LevelCompactAIDriver:foundUnloaderInRadius(r,setWaiting) end if setWaiting and isOkayToStop then --- vehicle.cp.driver.triggerHandler:setWaitingForUnloadReady() vehicle.cp.driver:hold() vehicle.cp.driver:setInfoText("WAITING_FOR_LEVELCOMPACTAIDRIVER") vehicle.cp.driver:disableTrafficConflictDetection() else vehicle.cp.driver:clearInfoText("WAITING_FOR_LEVELCOMPACTAIDRIVER") vehicle.cp.driver:enableTrafficConflictDetection() --- vehicle.cp.driver.triggerHandler:resetWaitingForUnloadReady() end self:debugSparse("found cp driver : %s",nameNum(vehicle)) return isOkayToStop - -- self.unloaderAIDrivers[#self.unloaderAIDrivers+1] = vehicle elseif autodriveSpec and autodriveSpec.HoldDriving and vehicle.ad.stateModule and vehicle.ad.stateModule:isActive() then --autodrive if setWaiting then @@ -247,6 +244,7 @@ function LevelCompactAIDriver:selectMode() elseif self:getIsModeCompact()then self:debug("self:isModeCompact()") self:changeLevelState(self.states.DRIVE_SILOCOMPACT) + self:lowerImplements() end self.fillUpState = self.states.PUSH end @@ -260,21 +258,16 @@ function LevelCompactAIDriver:driveSiloCompact(dt) end self:drivePush(dt) - self:lowerImplements() if self:isAtEnd() then self.fillUpState = self.states.PULLBACK + self:raiseImplements() end elseif self.fillUpState == self.states.PULLBACK then if self:drivePull(dt) then self.fillUpState = self.states.PUSH + self:lowerImplements() self:deleteBestTargetLeveling() - self:raiseImplements() - if self.hasFoundUnloaders then - self:debug("has unloader so move to waitpoint") - self:changeLevelState(self.states.DRIVE_TO_PARKING) - self:deleteBestTarget() - end end end end @@ -291,7 +284,7 @@ function LevelCompactAIDriver:driveSiloLevel(dt) self:moveShield('down',dt,self:getDiffHeightforHeight(self.targetHeight)) if self:isAtEnd() - or self:hasShieldEmpty() + --or self:hasShieldEmpty() or self:isStuck() then if self.hasFoundUnloaders then @@ -305,7 +298,6 @@ function LevelCompactAIDriver:driveSiloLevel(dt) elseif self.fillUpState == self.states.PULLBACK then - renderText(0.2,0.365,0.02,"self:drivePull(dt)") self:moveShield('up',dt) if self:isStuck() then self.fillUpState = self.states.PUSH @@ -322,14 +314,14 @@ function LevelCompactAIDriver:driveSiloFillUp(dt) if self.fillUpState == self.states.PUSH then --initialize first target point if self.bestTarget == nil then - self.bestTarget, self.firstLine = self.bunkerSiloManager:getBestTargetFillUnitFillUp(self.bestTarget) + self.bestTarget, self.firstLine = self:getBestTargetFillUnitFillUp(self.lastDrivenColumn) 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() + --or self:hasShieldEmpty() then if self.hasFoundUnloaders then self:changeLevelState(self.states.DRIVE_TO_PARKING) @@ -341,9 +333,9 @@ function LevelCompactAIDriver:driveSiloFillUp(dt) end elseif self.fillUpState == self.states.PULLBACK then self:moveShield('up',dt) - if self:drivePull(dt) or self:getHasMovedToFrontLine(dt) then + if self:drivePull(dt) then self.fillUpState = self.states.PUSH - self:deleteBestTarget() + self:deleteBestTargetLeveling() end end end @@ -363,9 +355,9 @@ function LevelCompactAIDriver:drivePush(dt) self:updateTarget() --speed if self:isNearEnd() then - refSpeed = math.min(10,vehicle.cp.speeds.bunkerSilo) + refSpeed = math.min(10,vehicle.cp.settings.bunkerSpeed:get()) else - refSpeed = math.min(20,vehicle.cp.speeds.bunkerSilo) + refSpeed = math.min(20,vehicle.cp.settings.bunkerSpeed:get()) end --drive local lx, lz = AIVehicleUtil.getDriveDirection(self.vehicle.cp.directionNode, cx,cy,cz); @@ -380,7 +372,7 @@ end function LevelCompactAIDriver:drivePull(dt) local pullDone = false local fwd = true - local refSpeed = math.min(20,self.vehicle.cp.speeds.bunkerSilo) + 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); @@ -389,6 +381,11 @@ function LevelCompactAIDriver:drivePull(dt) if lz < 0 then pullDone = true end + if self.hasFoundUnloaders then + self:changeLevelState(self.states.DRIVE_TO_PARKING) + self:deleteBestTarget() + return false + end -- self:drawMap() return pullDone end @@ -396,7 +393,7 @@ end ---make sure we start with enough distance to the first bunkersilo target, so we don't drive into the silo wall ---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.speeds.bunkerSilo) + 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) @@ -416,9 +413,9 @@ end function LevelCompactAIDriver:getHasMovedToFrontLine(dt) local startUnit = self.bunkerSiloManager.siloMap[self.firstLine][1] - local _,ty,_ = getWorldTranslation(self.vehicle.cp.directionNode); - local _,_,z = worldToLocal(self.vehicle.cp.directionNode, startUnit.cx , ty , startUnit.cz); - if z < -15 then + local _,ty,_ = getWorldTranslation(self:getLevelerNode(self.leveler)); + local _,_,z = worldToLocal(self:getLevelerNode(self.leveler), startUnit.cx , ty , startUnit.cz); + if math.abs(z) < 1 then return true; end return false; @@ -431,12 +428,13 @@ end function LevelCompactAIDriver:lastLineFillLevelChanged() local vehicle = self.vehicle - local newSx = self.bunkerSiloManager.siloMap[#self.bunkerSiloManager.siloMap][1].sx - local newSz = self.bunkerSiloManager.siloMap[#self.bunkerSiloManager.siloMap][1].sz - local newWx = self.bunkerSiloManager.siloMap[#self.bunkerSiloManager.siloMap][#self.bunkerSiloManager.siloMap[#self.bunkerSiloManager.siloMap]].wx - local newWz = self.bunkerSiloManager.siloMap[#self.bunkerSiloManager.siloMap][#self.bunkerSiloManager.siloMap[#self.bunkerSiloManager.siloMap]].wz - local newHx = self.bunkerSiloManager.siloMap[#self.bunkerSiloManager.siloMap][1].hx - local newHz = self.bunkerSiloManager.siloMap[#self.bunkerSiloManager.siloMap][1].hz + 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); @@ -598,18 +596,28 @@ function LevelCompactAIDriver:checkSilo() end function LevelCompactAIDriver:lowerImplements() + self.vehicle:raiseAIEvent("onAIStart", "onAIImplementStart") + self.vehicle:requestActionEventUpdate() for _, implement in pairs(self.vehicle:getAttachedImplements()) do if implement.object.aiImplementStartLine then implement.object:aiImplementStartLine() + -- if implement.object.getCanBeTurnedOn and implement.object:getCanBeTurnedOn() then + -- implement.object:setIsTurnedOn(true) + -- end end end self.vehicle:raiseStateChange(Vehicle.STATE_CHANGE_AI_START_LINE) end function LevelCompactAIDriver:raiseImplements() + self.vehicle:raiseAIEvent("onAIEnd", "onAIImplementEnd") + self.vehicle:requestActionEventUpdate() for _, implement in pairs(self.vehicle:getAttachedImplements()) do if implement.object.aiImplementEndLine then implement.object:aiImplementEndLine() + -- if implement.object.getCanBeTurnedOn then + -- implement.object:setIsTurnedOn(false) + -- end end end self.vehicle:raiseStateChange(Vehicle.STATE_CHANGE_AI_END_LINE) @@ -619,7 +627,7 @@ end function LevelCompactAIDriver:moveShield(moveDir,dt,fixHeight) local leveler = self.leveler local moveFinished = false - if leveler.spec_attacherJointControl ~= nil then + if leveler and leveler.spec_attacherJointControl ~= nil then local spec = leveler.spec_attacherJointControl local jointDesc = spec.jointDesc if moveDir == "down" then @@ -755,9 +763,10 @@ end function LevelCompactAIDriver:getBestTargetFillUnitCompacting(lastDrivenColumn) local newBestTarget = {} local firstLine = 1 - if self.bunkerSiloManager.siloMap ~= nil then + local siloMap = self.bunkerSiloManager:getSiloMap() + if siloMap ~= nil then local newColumn = lastDrivenColumn and lastDrivenColumn + 1 or 1 - if newColumn > #self.bunkerSiloManager.siloMap[1] then + if newColumn > #siloMap[1] then newColumn = 1 end local newBestTarget= { @@ -769,14 +778,47 @@ function LevelCompactAIDriver:getBestTargetFillUnitCompacting(lastDrivenColumn) end 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 newColumn = lastDrivenColumn and lastDrivenColumn + 1 or 1 + if newColumn > #siloMap[1] then + newColumn = 1 + end + local firstLine = 1 + local bestTarget = { + line = 1; + 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 +end + function LevelCompactAIDriver:getBestTargetFillUnitLeveling(lastDrivenColumn) + local siloMap = self.bunkerSiloManager:getSiloMap() local firstLine = 1 local targetHeight = 0.5 local vehicle = self.vehicle local newApproach = lastDrivenColumn == nil local newBestTarget = {} - if self.bunkerSiloManager.siloMap ~= nil then - local newColumn = math.ceil(#self.bunkerSiloManager.siloMap[1]/2) + 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)', @@ -784,15 +826,15 @@ function LevelCompactAIDriver:getBestTargetFillUnitLeveling(lastDrivenColumn) return newBestTarget, firstLine, targetHeight else newColumn = lastDrivenColumn + 1; - if newColumn > #self.bunkerSiloManager.siloMap[1] then + if newColumn > #siloMap[1] then newColumn = 1; end - firstLine = self:findFirstNonEmptyRow(self.bunkerSiloManager.siloMap, newColumn) + firstLine = self:findFirstNonEmptyRow(siloMap, newColumn) newBestTarget= { - line = firstLine; - column = newColumn; - empty = false; - } + line = firstLine; + column = newColumn; + empty = false; + } end targetHeight = self:getColumnsTargetHeight(newColumn) end @@ -804,12 +846,13 @@ end function LevelCompactAIDriver:getColumnsTargetHeight(newColumn) local totalArea = 0 local totalFillLevel = 0 - for i=1,#self.bunkerSiloManager.siloMap do + local siloMap = self.bunkerSiloManager:getSiloMap() + for i=1,#siloMap do --calculate the area without first and last line - if i~= 1 and i~= #self.bunkerSiloManager.siloMap then - totalArea = totalArea + self.bunkerSiloManager.siloMap[i][newColumn].area + if i~= 1 and i~= #siloMap then + totalArea = totalArea + siloMap[i][newColumn].area end - totalFillLevel = totalFillLevel + self.bunkerSiloManager.siloMap[i][newColumn].fillLevel + 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)) @@ -819,7 +862,7 @@ end function LevelCompactAIDriver:debugRouting() if self:isDebugActive() and self.bunkerSiloManager then - self.bunkerSiloManager:debugRouting(self.bestTarget,self.tempTarget) + self.bunkerSiloManager:debugRouting(self.bestTarget) end end @@ -858,9 +901,11 @@ function LevelCompactAIDriver:getWorkWidth() return math.max(self.workWidth,self.vehicle.cp.workWidth) end +---if we have a leveler return the leveler.rootNode, else return the backMakerNode +---@return node self.leveler.rootNode or backMakerNode function LevelCompactAIDriver:getValidBackImplement() - local backImplement = AIDriverUtil.getLastAttachedImplement(self.vehicle) - return self.leveler or backImplement + local backMarkerNode = self:getBackMarkerNode(self.vehicle) + return self.leveler and self.leveler.rootNode or backMarkerNode end function LevelCompactAIDriver:isDebugActive() diff --git a/ShovelModeAIDriver.lua b/ShovelModeAIDriver.lua index f5725fb5a..4686b038c 100644 --- a/ShovelModeAIDriver.lua +++ b/ShovelModeAIDriver.lua @@ -56,6 +56,8 @@ ShovelModeAIDriver.myStates = { STATE_WAIT_FOR_TARGET = {}, STATE_START_UNLOAD = {}, STATE_WAIT_FOR_UNLOADREADY = {}, + STATE_START_UNLOAD_TRAILER = {}, + STATE_WAIT_FOR_UNLOADREADY_TRAILER = {}, STATE_GO_BACK_FROM_EMPTYPOINT = {}, STATE_WORK_FINISHED = {} } @@ -69,7 +71,6 @@ function ShovelModeAIDriver:init(vehicle) --self.mode = courseplay.MODE_SHOVEL_FILL_AND_EMPTY self.shovelState = self.states.STATE_TRANSPORT self.refSpeed = 15 - self.foundTrailer = nil end function ShovelModeAIDriver.create(vehicle) @@ -101,11 +102,10 @@ function ShovelModeAIDriver:start() self.shovelFillStartPoint = nil self.shovelFillEndPoint = nil self.shovelEmptyPoint = nil - self.mode9SavedLastFillLevel = 0; local numWaitPoints = 0 - self.targetSilo = nil self.bestTarget = nil - self.bunkerSilo = nil + self.bunkerSiloManager = nil + self.unloadingObjectRaycastActive = false for i,wp in pairs(vehicle.Waypoints) do if wp.wait then numWaitPoints = numWaitPoints + 1; @@ -180,7 +180,7 @@ function ShovelModeAIDriver:drive(dt) if self.bunkerSiloManager == nil then local silo,isHeap = BunkerSiloManagerUtil.getTargetBunkerSilo(self.vehicle,nil,true) if silo then - self.bunkerSiloManager = BunkerSiloManager(self.vehicle, silo, self:getWorkWidth(),self.shovel,isHeap) + self.bunkerSiloManager = BunkerSiloManager(self.vehicle, silo, self:getWorkWidth(),self.shovel.rootNode,isHeap) end end if self.bunkerSiloManager and self.bestTarget == nil then @@ -188,7 +188,7 @@ function ShovelModeAIDriver:drive(dt) end end self:drawMap() - if self.bestTarget then + if self.bunkerSiloManager and self.bestTarget then self:setShovelState(self.states.STATE_GOINTO_SILO) end --driving into the bunkerSilo @@ -268,12 +268,24 @@ function ShovelModeAIDriver:drive(dt) -- close to the unload waitpoint, so set pre unload shovel position and do a raycast for unload triggers, trailers elseif self.shovelState == self.states.STATE_WAIT_FOR_TARGET then self:driveWaitForTarget(dt) - -- drive to the unload trigger/ trailer + -- drive to the unload at trigger elseif self.shovelState == self.states.STATE_START_UNLOAD then notAllowedToDrive = self:driveStartUnload(dt) - -- handle unloading + if notAllowedToDrive then + return + end + -- handle unloading at trigger elseif self.shovelState == self.states.STATE_WAIT_FOR_UNLOADREADY then self:driveWaitForUnloadReady(dt) + -- drive to the unload at trailer + elseif self.shovelState == self.states.STATE_START_UNLOAD_TRAILER then + notAllowedToDrive = self:driveStartUnloadTrailer(dt) + if notAllowedToDrive then + return + end + -- handle unloading at trailer + elseif self.shovelState == self.states.STATE_WAIT_FOR_UNLOADREADY_TRAILER then + self:driveWaitForUnloadReadyTrailer(dt) -- reverse back to the course elseif self.shovelState == self.states.STATE_GO_BACK_FROM_EMPTYPOINT then self:driveGoBackFromEmptyPoint(dt) @@ -283,13 +295,19 @@ function ShovelModeAIDriver:drive(dt) end self:updateInfoText() self.ppc:update() - if not notAllowedToDrive then - AIDriver.drive(self, dt) - end + AIDriver.drive(self, dt) self:resetSpeed() self:checkLastWaypoint() end +--using updateTick for raycasts performance, as updateTick represents the physic updates +function ShovelModeAIDriver:updateTick(dt) + AIDriver.updateTick(self,dt) + if self:isUnloadingObjectRaycastActive() then + self:searchForUnloadingObjectRaycast() + end +end + function ShovelModeAIDriver:isStuck() if self:doesNotMove() then if self.vehicle.cp.timers.slipping == nil or self.vehicle.cp.timers.slipping == 0 then @@ -321,11 +339,14 @@ function ShovelModeAIDriver:driveWaitForTarget(dt) end if self:setShovelToPositionFinshed(4,dt) then --search for UnloadStation(UnloadTrigger) or correct Trailer ahead, else wait - self:searchForUnloadingObjectRaycast() + self.unloadingObjectRaycastActive = true end end --- drive to the unload trigger/ trailer + +---trigger +-- drive to the unload trigger 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 @@ -333,7 +354,7 @@ function ShovelModeAIDriver:driveStartUnload(dt) 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) or self.foundTrailer) then + 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; @@ -342,10 +363,10 @@ function ShovelModeAIDriver:driveStartUnload(dt) elseif currentDischargeNode.dischargeObject or currentDischargeNode.dischargeFailedReason == Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY then self:setShovelToPositionFinshed(4,dt) self:hold() - --drive in straight line to waitPoint is UnloadStation(UnloadTrigger) or correct Trailer was found + --drive in straight line to waitPoint/UnloadStation(UnloadTrigger) elseif not self:getIsShovelEmpty() then if self.course:getDistanceToNextWaypoint(self.shovelEmptyPoint) <2 then - -- the last 2m we drive straight to the unload trigger/ trailer + -- 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) @@ -353,22 +374,64 @@ function ShovelModeAIDriver:driveStartUnload(dt) end return notAllowedToDrive end --- handle unloading + +-- handle unloading at trigger function ShovelModeAIDriver:driveWaitForUnloadReady(dt) self:hold() local dischargeNode = self.shovel:getCurrentDischargeNode() -- drive back to the course - if self:getIsShovelEmpty() or not self.shovel:getCanDischargeToObject(dischargeNode) and self.foundTrailer then + 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); end --stop unloading at unload trigger if there is no more free space - elseif (not self.shovel:getCanDischargeToObject(dischargeNode) or self:almostFullObject(dischargeNode)) and not self.foundTrailer then + elseif not self.shovel:getCanDischargeToObject(dischargeNode) or self:almostFullObject(dischargeNode) then self:setShovelState(self.states.STATE_START_UNLOAD); end end + +------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) + end + end + return notAllowedToDrive +end + +-- handle unloading +function ShovelModeAIDriver:driveWaitForUnloadReadyTrailer(dt) + self:hold() + 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 + end +end + +------ + -- reverse back to the course function ShovelModeAIDriver:driveGoBackFromEmptyPoint(dt) self.refSpeed = self.vehicle.cp.speeds.reverse @@ -379,7 +442,6 @@ function ShovelModeAIDriver:driveGoBackFromEmptyPoint(dt) self:setShovelState(self.states.STATE_TRANSPORT) end end - self.foundTrailer=nil end --bunker silo/ heap is empty function ShovelModeAIDriver:driveWorkFinished(dt) @@ -419,6 +481,9 @@ 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 @@ -497,6 +562,10 @@ function ShovelModeAIDriver:iAmBeforeEmptyPoint() return self.ppc:getCurrentWaypointIx() < self.shovelEmptyPoint end +function ShovelModeAIDriver:isUnloadingObjectRaycastActive() + return self.unloadingObjectRaycastActive +end + -- raycast for unloading trigger or trailer at the shovelEmptyPoint function ShovelModeAIDriver:searchForUnloadingObjectRaycast() local ix = self.shovelEmptyPoint @@ -543,8 +612,7 @@ function ShovelModeAIDriver:searchForUnloadingObjectRaycastCallback(transformId, --valid trailer/ fillableObject found self:debug("supportedFillType") self:debug("Trailer found!") - self:setShovelState(self.states.STATE_START_UNLOAD) - self.foundTrailer = true + self:setShovelState(self.states.STATE_START_UNLOAD_TRAILER) return else self:debug("not supportedFillType") diff --git a/TriggerShovelModeAIDriver.lua b/TriggerShovelModeAIDriver.lua index a79520a5a..f9ad70a29 100644 --- a/TriggerShovelModeAIDriver.lua +++ b/TriggerShovelModeAIDriver.lua @@ -93,12 +93,18 @@ function TriggerShovelModeAIDriver:drive(dt) elseif self.shovelState == self.states.STATE_WAIT_FOR_TARGET then self:driveWaitForTarget(dt) self.triggerHandler:disableFillTypeLoading() - -- drive to the unload trigger/ trailer + -- drive to the unload trigger elseif self.shovelState == self.states.STATE_START_UNLOAD then notAllowedToDrive = self:driveStartUnload(dt) - -- handle unloading + -- handle unloading at trigger elseif self.shovelState == self.states.STATE_WAIT_FOR_UNLOADREADY then self:driveWaitForUnloadReady(dt) + -- drive to the unload at trailer + elseif self.shovelState == self.states.STATE_START_UNLOAD_TRAILER then + notAllowedToDrive = self:driveStartUnloadTrailer(dt) + -- handle unloading at trailer + elseif self.shovelState == self.states.STATE_WAIT_FOR_UNLOADREADY_TRAILER then + self:driveWaitForUnloadReadyTrailer(dt) -- reverse back to the course elseif self.shovelState == self.states.STATE_GO_BACK_FROM_EMPTYPOINT then self:driveGoBackFromEmptyPoint(dt) diff --git a/base.lua b/base.lua index 719f5b92e..aca47af52 100644 --- a/base.lua +++ b/base.lua @@ -480,7 +480,7 @@ function courseplay:onLoad(savegame) self.cp.courseGeneratorSettings:addSetting(CenterModeSetting, self) self.cp.courseGeneratorSettings:addSetting(NumberOfRowsPerLandSetting, self) self.cp.courseGeneratorSettings:addSetting(HeadlandOverlapPercent, self) - + self.cp.courseGeneratorSettings:addSetting(ShowSeedCalculatorSetting, self) courseplay.signs:updateWaypointSigns(self); courseplay:setAIDriver(self, self.cp.mode) @@ -503,7 +503,12 @@ function courseplay:onLeaveVehicle() end function courseplay:onEnterVehicle() - courseEditor:reset() + --if the vehicle is attached to another vehicle, disable cp + if not courseplay.isEnabled(self) then + return + end + + courseEditor:reset() if self.cp.mouseCursorActive then courseplay:setMouseCursor(self, true); end; @@ -517,7 +522,12 @@ function courseplay:onEnterVehicle() end function courseplay:onDraw() - courseEditor:draw(self, self.cp.directionNode) + --if the vehicle is attached to another vehicle, disable cp + if not courseplay.isEnabled(self) then + return + end + + courseEditor:draw(self, self.cp.directionNode) courseplay:showAIMarkers(self) courseplay:showTemporaryMarkers(self) @@ -759,7 +769,10 @@ function courseplay:drawWaypointsLines(vehicle) end; function courseplay:onUpdate(dt) - + --if the vehicle is attached to another vehicle, disable cp + if not courseplay.isEnabled(self) then + return + end if self.cp.infoText ~= nil then self.cp.infoText = nil end @@ -831,7 +844,10 @@ end; function courseplay:onUpdateTick(dt) --print("base:courseplay:updateTick(dt)") - + --if the vehicle is attached to another vehicle, disable cp + if not courseplay.isEnabled(self) then + return + end if not self.cp.fieldEdge.selectedField.buttonsCreated and courseplay.fields.numAvailableFields > 0 then courseplay:createFieldEdgeButtons(self); end; @@ -1495,5 +1511,11 @@ function courseplay:onStopCpAIDriver() self:setIsCourseplayDriving(false) end +---vehicle is not attached to another one and vehicle has CourseplaySpec +function courseplay.isEnabled(vehicle) + local vehicle = vehicle + return vehicle and vehicle.hasCourseplaySpec and not (vehicle.spec_attachable and vehicle.spec_attachable.attacherVehicle) +end + -- do not remove this comment -- vim: set noexpandtab: diff --git a/gui/CourseGeneratorScreen.lua b/gui/CourseGeneratorScreen.lua index ef54e69a5..f9286a312 100644 --- a/gui/CourseGeneratorScreen.lua +++ b/gui/CourseGeneratorScreen.lua @@ -520,6 +520,9 @@ function CourseGeneratorScreen:draw() self.coursePlot:setSize(self.ingameMap.size[1], self.ingameMap.size[2]) self.coursePlot:draw() end + if self.vehicle.cp.courseGeneratorSettings.showSeedCalculator:is(true) then + self:drawSeedCalculator(self.ingameMap.absPosition[ 1 ],self.ingameMap.absPosition[2]+0.025) + end end function CourseGeneratorScreen:onOpenCenterMode( element, parameter ) @@ -548,6 +551,72 @@ function CourseGeneratorScreen:onClickNumberOfRowsPerLand(state) end end +function CourseGeneratorScreen:onOpenShowSeedCalculator( element, parameter ) + local setting = self.vehicle.cp.courseGeneratorSettings.showSeedCalculator + setting:setGuiElement(element) + element:setTexts(setting:getGuiElementTexts()) + element:setState(setting:getGuiElementState()) +end + +function CourseGeneratorScreen:onClickShowSeedCalculator(state) + local setting = self.vehicle.cp.courseGeneratorSettings.showSeedCalculator + if setting:getGuiElement() then + setting:setToIx(setting:getGuiElement():getState()) + end +end + +---a very basic and simple seed calculator in the course generator +function CourseGeneratorScreen:drawSeedCalculator(xPos,yPos) + -- do have a valid field selected ? + local currentFieldNumber = self.vehicle.cp.fieldEdge.selectedField.fieldNum + if currentFieldNumber ~= 0 then + local fieldAreaHa = courseplay.fields.fieldData[currentFieldNumber].areaHa + local fieldAreaSqm = courseplay.fields.fieldData[currentFieldNumber].areaSqm + setTextBold(true) + local textFontSize = 0.02 + local shadowOffset = textFontSize * 0.03 + --shadow color + local rShadow,gShadow,bShadow,aShadow = 1, 0, 0, 0.8 + --text color + local r,g,b,a = 1, 0.2, 0, 1 + -- draw shadow + setTextColor(rShadow,gShadow,bShadow,aShadow) + renderText(self.ingameMap.absPosition[ 1 ]+shadowOffset,self.ingameMap.absPosition[ 2 ]-shadowOffset,textFontSize,string.format("Field size: %.2f Ha",fieldAreaHa)) + -- draw field size at the bottom + setTextColor(r,g,b,a) + renderText(self.ingameMap.absPosition[ 1 ],self.ingameMap.absPosition[ 2 ],textFontSize,string.format("Field size: %.2f Ha",fieldAreaHa)) + -- draw all the sprayTypes and fruitType + for _,sprayType in pairs(g_sprayTypeManager:getSprayTypes()) do + local litersPerSecond = sprayType.litersPerSecond + -- calculate totalLiters in liters per hour, not sure why 36000 is needed instead of 3600 + local totalLiters = litersPerSecond*fieldAreaHa* 36000 + local name = sprayType.fillType.title + -- draw shadow + setTextColor(rShadow,gShadow,bShadow,aShadow) + renderText(xPos+shadowOffset,yPos-shadowOffset,textFontSize,string.format("%s : %d %s",name,math.ceil(totalLiters),g_i18n:getText("unit_liter"))) + --draw text + setTextColor(r,g,b,a) + renderText(xPos,yPos,textFontSize,string.format("%s : %d %s",name,math.ceil(totalLiters),g_i18n:getText("unit_liter"))) + yPos = yPos+0.025 + end + for _,fruitType in pairs(g_fruitTypeManager:getFruitTypes()) do + if fruitType.allowsSeeding then + local seedUsagePerSqm = fruitType.seedUsagePerSqm + local totalSeedUsage = seedUsagePerSqm*fieldAreaSqm + local name = fruitType.fillType.title + -- draw shadow + setTextColor(rShadow,gShadow,bShadow,aShadow) + renderText(xPos+shadowOffset,yPos-shadowOffset,textFontSize,string.format("%s : %d %s",name,math.ceil(totalSeedUsage),g_i18n:getText("unit_liter"))) + -- draw text + setTextColor(r,g,b,a) + renderText(xPos,yPos,textFontSize,string.format("%s : %d %s",name,math.ceil(totalSeedUsage),g_i18n:getText("unit_liter"))) + yPos = yPos+0.025 + end + end + end +end + + function CourseGeneratorScreen:isOverElement( x, y, element ) if x < element.absPosition[ 1 ] or x > element.absPosition[ 1 ] + element.size[ 1 ] or y < element.absPosition[ 2 ] or y > element.absPosition[ 2 ] + element.size[ 2 ] then diff --git a/gui/CourseGeneratorScreen.xml b/gui/CourseGeneratorScreen.xml index b248b42f9..0c58957c1 100644 --- a/gui/CourseGeneratorScreen.xml +++ b/gui/CourseGeneratorScreen.xml @@ -133,24 +133,30 @@ - - - - - + + + + + + + + + + + + + - + - - CoursePlay SIX diff --git a/settings.lua b/settings.lua index 8def19802..41e775779 100644 --- a/settings.lua +++ b/settings.lua @@ -1999,6 +1999,13 @@ function HeadlandOverlapPercent:init(vehicle) self:set(7) end +---@class ShowSeedCalculatorSetting : BooleanSetting +ShowSeedCalculatorSetting = CpObject(BooleanSetting) +function ShowSeedCalculatorSetting:init(vehicle) + BooleanSetting.init(self, 'showSeedCalculator', 'COURSEPLAY_SEEDUSAGECALCULATOR','COURSEPLAY_SEEDUSAGECALCULATOR', vehicle) + self:set(false) +end + --- Course generator settings (read from the XML, may be added to the UI later when needed): --- --- Minimum radius in meters where a lane change on the headland is allowed. This is to ensure that diff --git a/translations/translation_br.xml b/translations/translation_br.xml index 3a016ccd4..97b5c1218 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -337,7 +337,7 @@ - + diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 506da4b7b..2802be606 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -337,7 +337,7 @@ - + diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index 554530538..d53d2c8a5 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -337,7 +337,7 @@ - + diff --git a/translations/translation_de.xml b/translations/translation_de.xml index d28413377..482ea9a85 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -337,7 +337,7 @@ - + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 4f5bb4205..6e09d7a9b 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -338,7 +338,7 @@ - + diff --git a/translations/translation_es.xml b/translations/translation_es.xml index 3cec83d8a..74be31235 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -338,7 +338,7 @@ - + diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index fb6c6c0d9..9223ce07c 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -337,7 +337,7 @@ - + diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index f9ca2f41a..177d6f7fd 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -333,7 +333,7 @@ - + diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 8892fa5ec..d979d131c 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -337,7 +337,7 @@ - + diff --git a/translations/translation_jp.xml b/translations/translation_jp.xml index eb8b55b08..b966b2ed2 100644 --- a/translations/translation_jp.xml +++ b/translations/translation_jp.xml @@ -337,7 +337,7 @@ - + diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 78108093c..9eff5a06c 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -333,7 +333,7 @@ - + diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index f16ec86fb..3e9068942 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -337,7 +337,7 @@ - + diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index 05ed215ef..6b2a68190 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -333,7 +333,7 @@ - + diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index 4b4ebb53a..4aa4bbd2a 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -338,7 +338,7 @@ - + diff --git a/translations/translation_sl.xml b/translations/translation_sl.xml index 323e1e9e5..70035f381 100644 --- a/translations/translation_sl.xml +++ b/translations/translation_sl.xml @@ -337,7 +337,7 @@ - +