diff --git a/AIDriver.lua b/AIDriver.lua index c5d9f295d..c52014a75 100644 --- a/AIDriver.lua +++ b/AIDriver.lua @@ -1438,6 +1438,18 @@ function AIDriver:resetBGASiloTables() self.bestColumnToFill = nil end +--- Helper functions to generate a straight course +function AIDriver:getStraightForwardCourse(length) + local l = length or 100 + return Course.createFromNode(self.vehicle, self.vehicle.rootNode, 0, 0, l, 5, false) +end + +function AIDriver:getStraightReverseCourse(length) + local lastTrailer = AIDriverUtil.getLastAttachedImplement(self.vehicle) + local l = length or 100 + return Course.createFromNode(self.vehicle, lastTrailer.rootNode or self.vehicle.rootNode, 0, 0, -l, -5, true) +end + ------------------------------------------------------------------------------ --- PATHFINDING ------------------------------------------------------------------------------ diff --git a/AITurn.lua b/AITurn.lua index f81c8e4cf..037bee6e5 100644 --- a/AITurn.lua +++ b/AITurn.lua @@ -537,8 +537,6 @@ function CourseTurn:changeDirectionWhenAligned() end function CourseTurn:generateCalculatedTurn() - -- TODO: fix ugly dependency on global variables, there should be one function to create the turn maneuver - self.vehicle.cp.settings.turnStage:set(true) -- call turn() with stage 1 which will generate the turn waypoints (dt isn't used by that part) courseplay:turn(self.vehicle, 1, self.turnContext) -- they waypoints should now be in turnTargets, create a course based on that diff --git a/BaleCollectorAIDriver.lua b/BaleCollectorAIDriver.lua new file mode 100644 index 000000000..1e0ee9b0d --- /dev/null +++ b/BaleCollectorAIDriver.lua @@ -0,0 +1,304 @@ +--[[ +This file is part of Courseplay (https://github.com/Courseplay/courseplay) +Copyright (C) 2021 Peter Vaiko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +]] + +--[[ + +A bale loader AI driver who can find and collect bales on a field +without a field course. + +For unloading, it has the same behavior as the BaleLoaderAIDriver. + +--]] + +---@class BaleCollectorAIDriver : BaleLoaderAIDriver +BaleCollectorAIDriver = CpObject(BaleLoaderAIDriver) + +BaleCollectorAIDriver.myStates = { + SEARCHING_FOR_NEXT_BALE = {}, + WAITING_FOR_PATHFINDER = {}, + DRIVING_TO_NEXT_BALE = {}, + APPROACHING_BALE = {}, + PICKING_UP_BALE = {} +} + +function BaleCollectorAIDriver:init(vehicle) + courseplay.debugVehicle(11,vehicle,'BaleCollectorAIDriver:init()') + BaleLoaderAIDriver.init(self, vehicle) + self:initStates(BaleCollectorAIDriver.myStates) + self.fieldId = 0 + self.bales = {} + -- make sure we have a good turning radius set + self.turnRadius = AIDriverUtil.getTurningRadius(self.vehicle) +end + +function BaleCollectorAIDriver:setHudContent() + -- skip the inheritance from fieldwork/bale loader as this is very special + AIDriver.setHudContent(self) + courseplay.hud:setBaleCollectorAIDriverContent(self.vehicle) +end + +function BaleCollectorAIDriver:setUpAndStart(startingPoint) + -- we only have an unload course since we are driving on the field autonomously + self.unloadRefillCourse = Course(self.vehicle, self.vehicle.Waypoints, false) + -- Set the offset to 0, we'll take care of getting the grabber to the right place + self.vehicle.cp.settings.toolOffsetX:set(0) + + if startingPoint:is(StartingPointSetting.START_COLLECTING_BALES) then + -- to always have a valid course (for the traffic conflict detector mainly) + self.fieldworkCourse = self:getStraightForwardCourse(25) + self:startCourse(self.fieldworkCourse, 1) + local myField = self.vehicle.cp.settings.baleCollectionField:get() + if not myField or myField < 1 then + self:stop("NO_FIELD_SELECTED") + return + end + self.bales = self:findBales(myField) + self:changeToFieldwork() + self:collectNextBale() + else + local closestIx, _, closestIxRightDirection, _ = + self.unloadRefillCourse:getNearestWaypoints(AIDriverUtil.getDirectionNode(self.vehicle)) + local startIx = 1 + if startingPoint:is(StartingPointSetting.START_AT_NEAREST_POINT) then + startIx = closestIx + elseif startingPoint:is(StartingPointSetting.START_AT_NEXT_POINT) then + startIx = closestIxRightDirection + end + self:changeToUnloadOrRefill() + self:startCourseWithAlignment(self.unloadRefillCourse, startIx) + end +end + +function BaleCollectorAIDriver:setBaleCollectingState(state) + self.baleCollectingState = state + self:debug('baleCollectingState: %s', self.baleCollectingState.name) +end + + +function BaleCollectorAIDriver:collectNextBale() + self:setBaleCollectingState(self.states.SEARCHING_FOR_NEXT_BALE) + if #self.bales > 0 then + self:findPathToNextBale() + else + self:info('No bales found.') + if self:getFillLevel() > 0.1 then + self:changeToUnloadOrRefill() + self:startCourseWithPathfinding(self.unloadRefillCourse, 1) + else + self:stop('WORK_END') + end + end +end + +--- Find bales on field +---@return BaleToCollect[] list of bales found +function BaleCollectorAIDriver:findBales(fieldId) + self:debug('Finding bales on field %d...', fieldId or 0) + local balesFound = {} + for _, object in pairs(g_currentMission.nodeToObject) do + if object:isa(Bale) then + local bale = BaleToCollect(object) + -- if the bale has a mountObject it is already on the loader so ignore it + if (not fieldId or fieldId == 0 or bale:getFieldId() == fieldId) and + not object.mountObject and + object:getOwnerFarmId() == self.vehicle:getOwnerFarmId() + then + -- bales may have multiple nodes, using the object.id deduplicates the list + balesFound[object.id] = bale + end + end + end + -- convert it to a normal array so lua can give us the number of entries + local bales = {} + for _, bale in pairs(balesFound) do + table.insert(bales, bale) + end + self:debug('Found %d bales on field %d', #bales, fieldId) + return bales +end + +---@return BaleToCollect, number closest bale and its distance +function BaleCollectorAIDriver:findClosestBale(bales) + local closestBale, minDistance, ix = nil, math.huge + for i, bale in ipairs(bales) do + local _, _, _, d = bale:getPositionInfoFromNode(AIDriverUtil.getDirectionNode(self.vehicle)) + self:debug('%d. bale (%d) in %.1f m', i, bale:getId(), d) + if d < self.vehicle.cp.turnDiameter * 2 then + -- if it is really close, check the length of the Dubins path + -- as we may need to drive a loop first to get to it + d = self:getDubinsPathLengthToBale(bale) + self:debug(' Dubins length is %.1f m', d) + end + if d < minDistance then + closestBale = bale + minDistance = d + ix = i + end + end + return closestBale, minDistance, ix +end + +function BaleCollectorAIDriver:getDubinsPathLengthToBale(bale) + local start = PathfinderUtil.getVehiclePositionAsState3D(self.vehicle) + local goal = self:getBaleTarget(bale) + local solution = PathfinderUtil.dubinsSolver:solve(start, goal, self.turnRadius) + return solution:getLength(self.turnRadius) +end + +function BaleCollectorAIDriver:findPathToNextBale() + if not self.bales then return end + local bale, d, ix = self:findClosestBale(self.bales) + if ix then + self:startPathfindingToBale(bale) + -- remove bale from list + table.remove(self.bales, ix) + end +end + +--- The trick here is to get a target direction at the bale +function BaleCollectorAIDriver:getBaleTarget(bale) + -- first figure out the direction at the goal, as the pathfinder needs that. + -- for now, just use the direction from our location towards the bale + local xb, zb, yRot, d = bale:getPositionInfoFromNode(AIDriverUtil.getDirectionNode(self.vehicle)) + return State3D(xb, -zb, courseGenerator.fromCpAngle(yRot)) +end + +---@param bale BaleToCollect +function BaleCollectorAIDriver:startPathfindingToBale(bale) + if not self.pathfinder or not self.pathfinder:isActive() then + self.pathfindingStartedAt = self.vehicle.timer + local safeDistanceFromBale = bale:getSafeDistance() + local halfVehicleWidth = self.vehicle.sizeWidth and self.vehicle.sizeWidth / 2 or 1.5 + self:debug('Start pathfinding to next bale (%d), safe distance from bale %.1f, half vehicle width %.1f', + bale:getId(), safeDistanceFromBale, halfVehicleWidth) + local goal = self:getBaleTarget(bale) + local offset = Vector(0, safeDistanceFromBale + halfVehicleWidth + 0.2) + goal:add(offset:rotate(goal.t)) + local done, path, goalNodeInvalid + self.pathfinder, done, path, goalNodeInvalid = + PathfinderUtil.startPathfindingFromVehicleToGoal(self.vehicle, goal, false, self.fieldId, {}) + if done then + return self:onPathfindingDoneToNextBale(path, goalNodeInvalid) + else + self:setBaleCollectingState(self.states.WAITING_FOR_PATHFINDER) + self:setPathfindingDoneCallback(self, self.onPathfindingDoneToNextBale) + return true + end + else + self:debug('Pathfinder already active') + end +end + +function BaleCollectorAIDriver:onPathfindingDoneToNextBale(path, goalNodeInvalid) + if path and #path > 2 then + self:debug('Found path (%d waypoints, %d ms)', #path, self.vehicle.timer - (self.pathfindingStartedAt or 0)) + self.fieldworkCourse = Course(self.vehicle, courseGenerator.pointsToXzInPlace(path), true) + self:startCourse(self.fieldworkCourse, 1) + self:debug('Driving to next bale') + self:setBaleCollectingState(self.states.DRIVING_TO_NEXT_BALE) + return true + else + self:setBaleCollectingState(self.states.SEARCHING_FOR_NEXT_BALE) + return false + end +end + +function BaleCollectorAIDriver:onLastWaypoint() + if self.state == self.states.ON_FIELDWORK_COURSE and self.fieldworkState == self.states.WORKING then + if self.baleCollectingState == self.states.DRIVING_TO_NEXT_BALE then + self:debug('last waypoint while driving to next bale reached') + self:approachBale() + elseif self.baleCollectingState == self.states.PICKING_UP_BALE then + self:debug('last waypoint on bale pickup reached, start collecting bales again') + self:collectNextBale() + elseif self.baleCollectingState == self.states.APPROACHING_BALE then + self:debug('looks like somehow missed a bale, rescanning field') + self.bales = self:findBales(self.vehicle.cp.settings.baleCollectionField:get()) + self:collectNextBale() + end + else + BaleLoaderAIDriver.onLastWaypoint(self) + end +end + +function BaleCollectorAIDriver:onEndCourse() + if self.state == self.states.ON_UNLOAD_OR_REFILL_COURSE or + self.state == self.states.ON_UNLOAD_OR_REFILL_WITH_AUTODRIVE then + self:debug('Back from unload course, check for bales again') + self.bales = self:findBales(self.vehicle.cp.settings.baleCollectionField:get()) + self:changeToFieldwork() + self:collectNextBale() + else + BaleLoaderAIDriver.onEndCourse(self) + end +end + +function BaleCollectorAIDriver:approachBale() + self:debug('Approaching bale...') + self:startCourse(self:getStraightForwardCourse(20), 1) + self:setBaleCollectingState(self.states.APPROACHING_BALE) +end + +--- Called from the generic driveFieldwork(), this the part doing the actual work on the field after/before all +--- implements are started/lowered etc. +function BaleCollectorAIDriver:work() + if self.baleCollectingState == self.states.SEARCHING_FOR_NEXT_BALE then + self:setSpeed(0) + self:debug('work: searching for next bale') + self:collectNextBale() + elseif self.baleCollectingState == self.states.WAITING_FOR_PATHFINDER then + self:setSpeed(0) + elseif self.baleCollectingState == self.states.DRIVING_TO_NEXT_BALE then + self:setSpeed(self.vehicle:getSpeedLimit()) + elseif self.baleCollectingState == self.states.APPROACHING_BALE then + self:setSpeed(self:getWorkSpeed() / 2) + self:debug('%s %s', tostring(self.baleLoader.spec_baleLoader.grabberIsMoving), tostring(self.baleLoader.spec_baleLoader.grabberMoveState)) + if self.baleLoader.spec_baleLoader.grabberMoveState then + self:debug('Start picking up bale') + self:setBaleCollectingState(self.states.PICKING_UP_BALE) + end + elseif self.baleCollectingState == self.states.PICKING_UP_BALE then + self:setSpeed(0) + if not self.baleLoader.spec_baleLoader.grabberMoveState then + self:debug('Bale picked up, moving on to the next') + self:collectNextBale() + end + end + self:checkFillLevels() +end + +function BaleCollectorAIDriver:calculateTightTurnOffset() + self.tightTurnOffset = 0 +end + +function BaleCollectorAIDriver:getFillLevel() + local fillLevelInfo = {} + self:getAllFillLevels(self.vehicle, fillLevelInfo) + for fillType, info in pairs(fillLevelInfo) do + if fillType == FillType.SQUAREBALE or + fillType == FillType.SQUAREBALE_WHEAT or + fillType == FillType.SQUAREBALE_BARLEY or + fillType == FillType.ROUNDBALE or + fillType == FillType.ROUNDBALE_WHEAT or + fillType == FillType.ROUNDBALE_BARLEY or + fillType == FillType.ROUNDBALE_GRASS or + fillType == FillType.ROUNDBALE_DRYGRASS then + return info.fillLevel + end + end +end \ No newline at end of file diff --git a/BaleLoaderAIDriver.lua b/BaleLoaderAIDriver.lua index 7384c1fdc..e5cc41356 100644 --- a/BaleLoaderAIDriver.lua +++ b/BaleLoaderAIDriver.lua @@ -26,7 +26,7 @@ BaleLoaderAIDriver.myStates = { } --- Make sure the the bale loader behaves like a proper AIImplement and reacts on AIImplementStart/End ---- events so there's no special handling is needed elswhere. +--- events so there's no special handling is needed elsewhere. function BaleLoaderAIDriver.register() BaleLoader.onAIImplementStart = Utils.overwrittenFunction(BaleLoader.onAIImplementStart, @@ -59,8 +59,8 @@ end function BaleLoaderAIDriver:init(vehicle) courseplay.debugVehicle(11,vehicle,'BaleLoaderAIDriver:init()') UnloadableFieldworkAIDriver.init(self, vehicle) - self.baleLoader = AIDriverUtil.getAIImplementWithSpecialization(vehicle, BaleLoader) - + self.baleLoader = AIDriverUtil.getImplementWithSpecialization(vehicle, BaleLoader) + self:debug('baleloader %s', tostring(self.baleLoader)) -- Bale loaders have no AI markers (as they are not AIImplements according to Giants) so add a function here -- to get the markers self.baleLoader.getAIMarkers = function(self) @@ -78,12 +78,6 @@ function BaleLoaderAIDriver:init(vehicle) self:debug('Initialized, bale loader: %s', self.baleLoader:getName()) end -function BaleLoaderAIDriver:setHudContent() - UnloadableFieldworkAIDriver.setHudContent(self) - courseplay.hud:setBaleLoaderAIDriverContent(self.vehicle) -end - - ---@return boolean true if unload took over the driving function BaleLoaderAIDriver:driveUnloadOrRefill(dt) self:updateOffset() diff --git a/BaleToCollect.lua b/BaleToCollect.lua new file mode 100644 index 000000000..c1be44e54 --- /dev/null +++ b/BaleToCollect.lua @@ -0,0 +1,69 @@ +--[[ +This file is part of Courseplay (https://github.com/Courseplay/courseplay) +Copyright (C) 2021 Peter Vaiko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +]] + +--[[ + +A wrapper :) for the standard Bale object + +--]] + +---@class BaleToCollect +BaleToCollect = CpObject() + +---@param baleObject : Bale +function BaleToCollect:init(baleObject) + self.bale = baleObject + local x, _, z = getWorldTranslation(self.bale.nodeId) + self.fieldId = PathfinderUtil.getFieldIdAtWorldPosition(x, z) +end + +function BaleToCollect:getFieldId() + return self.fieldId +end + +function BaleToCollect:getId() + return self.bale.id +end + +function BaleToCollect:getPosition() + return getWorldTranslation(self.bale.nodeId) +end + +---@return number, number, number, number x, z, direction from node, distance from node +function BaleToCollect:getPositionInfoFromNode(node) + local xb, _, zb = self:getPosition() + local x, _, z = getWorldTranslation(node) + local dx, dz = xb - x, zb - z + local yRot = MathUtil.getYRotationFromDirection(dx, dz) + return xb, zb, yRot, math.sqrt(dx * dx + dz * dz) +end + +function BaleToCollect:getPositionAsState3D() + local xb, _, zb = self:getPosition() + local _, yRot, _ = getWorldRotation(self.bale.nodeId) + return State3D(xb, -zb, courseGenerator.fromCpAngle(yRot)) +end + +--- Minimum distance from the bale's center (node) to avoid hitting the bale +--- when driving by in any direction +function BaleToCollect:getSafeDistance() + -- round bales don't have length, just diameter + local length = self.bale.baleDiameter and self.bale.baleDiameter or self.bale.baleLength + -- no matter what kind of bale, the footprint is a rectangle, get the diagonal + return math.sqrt(length * length + self.bale.baleWidth * self.bale.baleWidth) / 2 +end \ No newline at end of file diff --git a/CombineUnloadAIDriver.lua b/CombineUnloadAIDriver.lua index 030079efc..1648df9a6 100644 --- a/CombineUnloadAIDriver.lua +++ b/CombineUnloadAIDriver.lua @@ -609,17 +609,6 @@ function CombineUnloadAIDriver:getCourseToAlignTo(vehicle,offset) return tempCourse end -function CombineUnloadAIDriver:getStraightForwardCourse(length) - local l = length or 100 - return Course.createFromNode(self.vehicle, self.vehicle.rootNode, 0, 0, l, 5, false) -end - -function CombineUnloadAIDriver:getStraightReverseCourse(length) - local lastTrailer = AIDriverUtil.getLastAttachedImplement(self.vehicle) - local l = length or 100 - return Course.createFromNode(self.vehicle, lastTrailer.rootNode, 0, 0, -l, -5, true) -end - function CombineUnloadAIDriver:getTrailersTargetNode() local allTrailersFull = true for i=1, #self.vehicle.cp.workTools do diff --git a/CpManager.lua b/CpManager.lua index 5a6d607ae..733f86b35 100644 --- a/CpManager.lua +++ b/CpManager.lua @@ -991,6 +991,7 @@ function CpManager:setupGlobalInfoText() RUNCOUNTER_ERROR_FOR_TRIGGER = { level = 0, text = 'COURSEPLAY_RUNCOUNTER_ERROR_FOR_TRIGGER' }; WAITING_FOR_UNLOADERS = { level = 0, text = 'COURSEPLAY_WAITING_FOR_UNLOADERS' }; WAITING_FOR_LEVELCOMPACTAIDRIVER = { level = 0, text = 'COURSEPLAY_WAITING_FOR_LEVELCOMPACTAIDRIVER' }; + NO_FIELD_SELECTED = { level = -1, text = 'COURSEPLAY_NO_FIELD_SELECTED' }; }; end; diff --git a/DevHelper.lua b/DevHelper.lua index bb7cf826c..bd09bfc17 100644 --- a/DevHelper.lua +++ b/DevHelper.lua @@ -108,7 +108,11 @@ function DevHelper:overlapBoxCallback(transformId) if collidingObject.getRootVehicle then text = 'vehicle' .. collidingObject:getName() else - text = collidingObject.getName and collidingObject:getName() or 'N/A' + if collidingObject:isa(Bale) then + text = 'Bale' + else + text = collidingObject.getName and collidingObject:getName() or 'N/A' + end end else text = '' @@ -138,12 +142,15 @@ function DevHelper:updateProximitySensors(vehicle) end end +-- Left-Alt + , (<) = mark current position as start for pathfinding -- Left-Alt + , (<) = mark current position as start for pathfinding -- Left-Alt + . (>) = mark current position as goal for pathfinding -- Left-Ctrl + . (>) = start pathfinding from marked start to marked goal -- Left-Ctrl + , (<) = mark current field as field for pathfinding -- Left-Alt + Space = save current vehicle position -- Left-Ctrl + Space = restore current vehicle position +-- Left-Alt + / = look for bales +-- Left-Ctrl + / = find path to next bale function DevHelper:keyEvent(unicode, sym, modifier, isDown) if not CpManager.isDeveloper then return end if bitAND(modifier, Input.MOD_LALT) ~= 0 and isDown and sym == Input.KEY_comma then @@ -179,6 +186,10 @@ function DevHelper:keyEvent(unicode, sym, modifier, isDown) elseif bitAND(modifier, Input.MOD_LCTRL) ~= 0 and isDown and sym == Input.KEY_space then -- restore vehicle position DevHelper.restoreVehiclePosition(g_currentMission.controlledVehicle) + elseif bitAND(modifier, Input.MOD_LALT) ~= 0 and isDown and sym == Input.KEY_slash then + self:initBales() + elseif bitAND(modifier, Input.MOD_LCTRL) ~= 0 and isDown and sym == Input.KEY_slash then + self:findPathToNextBale() end end @@ -196,7 +207,7 @@ function DevHelper:startPathfinding() tostring(self.start), tostring(self.goal), self.fieldNumForPathfinding or 0) local start = State3D:copy(self.start) - self.pathfinder, done, path = PathfinderUtil.startPathfindingFromVehicleToGoal(self.vehicle, start, self.goal, + self.pathfinder, done, path = PathfinderUtil.startPathfindingFromVehicleToGoal(self.vehicle, self.goal, false, self.fieldNumForPathfinding or 0, {}, 10) end @@ -260,7 +271,6 @@ function DevHelper:drawCourse() end end - function DevHelper:showFillNodes() for _, vehicle in pairs(g_currentMission.vehicles) do if SpecializationUtil.hasSpecialization(Trailer, vehicle.specializations) then @@ -274,6 +284,61 @@ function DevHelper:showFillNodes() end end +function DevHelper:findBales(fieldId) + self:debug('Finding bales on field %d...', fieldId or 0) + local bales = {} + for _, object in pairs(g_currentMission.nodeToObject) do + if object:isa(Bale) then + local bale = BaleToCollect(object) + if not fieldId or fieldId == 0 or bale:getFieldId() == fieldId then + bales[object.id] = bale + end + end + end + return bales +end + +function DevHelper:initBales() + local allBales = self:findBales() + local closestBale, d = self:findClosestBale(allBales, self.node) + self:debug('Closest bale is at %.1f m, on field %d', d, closestBale:getFieldId()) + self.bales = self:findBales(closestBale:getFieldId()) +end + +---@return BaleToCollect, number closest bale and its distance +function DevHelper:findClosestBale(bales, node) + local closestBale, minDistance = nil, math.huge + for _, bale in pairs(bales) do + local _, _, _, d = bale:getPositionFromNodeInfo(node) + if d < minDistance then + closestBale = bale + minDistance = d + end + end + return closestBale, minDistance +end + +function DevHelper:findPathToNextBale() + if not self.bales then return end + local baleId + for id, bale in pairs(self.bales) do + -- first figure out the direction at the goal, as the pathfinder needs that. + -- for now, just use the direction from our location towards the bale + local xb, zb, yRot, d = bale:getPositionFromNodeInfo(self.node) + + self.start = State3D(self.data.x, -self.data.z, courseGenerator.fromCpAngleDeg(self.data.yRotDeg)) + self.goal = State3D(xb, -zb, courseGenerator.fromCpAngle(yRot)) + local offset = Vector(0, 3.5) + self.goal:add(offset:rotate(self.goal.t)) + + self:startPathfinding() + baleId = id + break + end + -- remove bale from list + if baleId then self.bales[baleId] = nil end +end + function DevHelper:showVehicleSize() local vehicle = g_currentMission.controlledVehicle if not vehicle then return end diff --git a/FieldworkAIDriver.lua b/FieldworkAIDriver.lua index 6e89add57..b57ebee35 100644 --- a/FieldworkAIDriver.lua +++ b/FieldworkAIDriver.lua @@ -52,8 +52,6 @@ function FieldworkAIDriver:init(vehicle) courseplay.debugVehicle(11,vehicle,'FieldworkAIDriver:init()') AIDriver.init(self, vehicle) self:initStates(FieldworkAIDriver.myStates) - -- waiting for tools to turn on, unfold and lower - self.waitingForTools = true self.debugChannel = 14 -- waypoint index on main (fieldwork) course where we aborted the work before going on -- an unload/refill course @@ -190,7 +188,12 @@ function FieldworkAIDriver:start(startingPoint) self.aiDriverOffsetZ = 0 self.workWidth = courseplay:getWorkWidth(self.vehicle) + self.ppc:setNormalLookaheadDistance() + + self:setUpAndStart(startingPoint) +end +function FieldworkAIDriver:setUpAndStart(startingPoint) self:setUpCourses() -- now that we have our unload/refill and fieldwork courses set up, see where to start @@ -250,9 +253,6 @@ function FieldworkAIDriver:start(startingPoint) startWithFieldwork = true end - self.waitingForTools = true - self.ppc:setNormalLookaheadDistance() - if startWithFieldwork then self:startFieldworkWithPathfinding(ix) else @@ -422,10 +422,7 @@ function FieldworkAIDriver:driveFieldwork(dt) self:checkFillLevels() end elseif self.fieldworkState == self.states.WORKING then - self:setSpeed(self:getWorkSpeed()) - self:manageConvoy() - self:checkWeather() - self:checkFillLevels() + self:work() elseif self.fieldworkState == self.states.UNLOAD_OR_REFILL_ON_FIELD then self:driveFieldworkUnloadOrRefill() elseif self.fieldworkState == self.states.TEMPORARY then @@ -441,6 +438,16 @@ function FieldworkAIDriver:driveFieldwork(dt) return iAmDriving end +--- Do the actual fieldwork. This is extracted here so derived classes can use the +--- existing start/end work mechanisms to lower/raise start/stop implements and only +--- need to implement the actual work themselves. +function FieldworkAIDriver:work() + self:setSpeed(self:getWorkSpeed()) + self:manageConvoy() + self:checkWeather() + self:checkFillLevels() +end + function FieldworkAIDriver:getNominalSpeed() if self.state == self.states.ON_FIELDWORK_COURSE then return self:getWorkSpeed() diff --git a/UnloadableFieldworkAIDriver.lua b/UnloadableFieldworkAIDriver.lua index 0437058db..24ff693ce 100644 --- a/UnloadableFieldworkAIDriver.lua +++ b/UnloadableFieldworkAIDriver.lua @@ -48,7 +48,7 @@ function UnloadableFieldworkAIDriver:setHudContent() end function UnloadableFieldworkAIDriver.create(vehicle) - if AIDriverUtil.hasAIImplementWithSpecialization(vehicle, BaleLoader) then + if AIDriverUtil.hasImplementWithSpecialization(vehicle, BaleLoader) then return BaleLoaderAIDriver(vehicle) elseif AIDriverUtil.hasAIImplementWithSpecialization(vehicle, BaleWrapper) then -- Bale wrapper is derived from baler so must check it first to make sure that we instantiate a diff --git a/base.lua b/base.lua index f68cc3d75..8919fd245 100644 --- a/base.lua +++ b/base.lua @@ -412,7 +412,7 @@ end; function courseplay:onLeaveVehicle() if self.cp.mouseCursorActive then courseplay:setMouseCursor(self, false); - courseEditor:reset() + courseEditor:reset() end --hide visual i3D waypoint signs when not in vehicle @@ -1160,9 +1160,9 @@ function courseplay:loadVehicleCPSettings(xmlFile, key, resetVehicles) if not resetVehicles and g_server ~= nil then -- COURSEPLAY local curKey = key .. '.courseplay.basics'; - courseplay:setCpMode(self, Utils.getNoNil( getXMLInt(xmlFile, curKey .. '#aiMode'), self.cp.mode)); - self.cp.waitTime = Utils.getNoNil( getXMLInt(xmlFile, curKey .. '#waitTime'), 0); - local courses = Utils.getNoNil(getXMLString(xmlFile, curKey .. '#courses'), ''); + courseplay:setCpMode(self, Utils.getNoNil(getXMLInt(xmlFile, curKey .. '#aiMode'), self.cp.mode), true); + self.cp.waitTime = Utils.getNoNil(getXMLInt(xmlFile, curKey .. '#waitTime'), 0); + local courses = Utils.getNoNil(getXMLString(xmlFile, curKey .. '#courses'), ''); self.cp.loadedCourses = StringUtil.splitString(",", courses); courseplay:reloadCourses(self, true); diff --git a/config/ValidModeSetup.xml b/config/ValidModeSetup.xml index 1e2270057..fcf015216 100644 --- a/config/ValidModeSetup.xml +++ b/config/ValidModeSetup.xml @@ -174,13 +174,11 @@ - + - - diff --git a/course-generator/PathfinderUtil.lua b/course-generator/PathfinderUtil.lua index 8a72dfc03..52144a8ae 100644 --- a/course-generator/PathfinderUtil.lua +++ b/course-generator/PathfinderUtil.lua @@ -35,7 +35,7 @@ PathfinderUtil.VehicleData = CpObject() --- If the vehicle has a trailer, it is handled separately from other implements to allow for the --- pathfinding to consider the trailer's heading (which will be different from the vehicle's heading) function PathfinderUtil.VehicleData:init(vehicle, withImplements, buffer) - self.turnRadius = vehicle.cp and vehicle.cp.turnDiameter and vehicle.cp.turnDiameter / 2 or 10 + self.turnRadius = vehicle.cp and vehicle.cp.driver and AIDriverUtil.getTurningRadius(vehicle) or 10 self.vehicle = vehicle self.rootVehicle = vehicle:getRootVehicle() self.name = vehicle.getName and vehicle:getName() or 'N/A' @@ -283,8 +283,11 @@ function PathfinderUtil.CollisionDetector:overlapBoxCallback(transformId) -- just bumped into myself or a vehicle we want to ignore return end - --courseplay.debugFormat(7, 'collision: %s', collidingObject:getName()) + courseplay.debugFormat(7, 'collision: %s', collidingObject:getName()) end + if collidingObject and collidingObject:isa(Bale) then + courseplay.debugFormat(7, 'collision with bale') + end if not getHasClassId(transformId, ClassIds.TERRAIN_TRANSFORM_GROUP) then --[[ local text = '' @@ -316,14 +319,14 @@ function PathfinderUtil.CollisionDetector:findCollidingShapes(node, vehicleData, self.collidingShapes = 0 - overlapBox(x, y + 1, z, 0, yRot, 0, width, 1, length, 'overlapBoxCallback', self, bitOR(AIVehicleUtil.COLLISION_MASK, 2), true, true, true) + overlapBox(x, y + 0.2, z, 0, yRot, 0, width, 1, length, 'overlapBoxCallback', self, bitOR(AIVehicleUtil.COLLISION_MASK, 2), true, true, true) if log and self.collidingShapes > 0 then table.insert(PathfinderUtil.overlapBoxes, - { x = x, y = y + 1, z = z, yRot = yRot, width = width, length = length}) + { x = x, y = y + 0.2, z = z, yRot = yRot, width = width, length = length}) courseplay.debugFormat(7, 'pathfinder colliding shapes (%s) at x = %.1f, z = %.1f, (%.1fx%.1f), yRot = %d', vehicleData.name, x, z, width, length, math.deg(yRot)) end - --DebugUtil.drawOverlapBox(x, y, z, 0, yRot, 0, width, 1, length, 100, 0, 0) + DebugUtil.drawOverlapBox(x, y, z, 0, yRot, 0, width, 1, length, 100, 0, 0) return self.collidingShapes end @@ -601,9 +604,12 @@ end ---@param start State3D ---@param goal State3D -function PathfinderUtil.startPathfindingFromVehicleToGoal(vehicle, start, goal, +function PathfinderUtil.startPathfindingFromVehicleToGoal(vehicle, goal, allowReverse, fieldNum, vehiclesToIgnore, maxFruitPercent, offFieldPenalty, mustBeAccurate) + + local start = PathfinderUtil.getVehiclePositionAsState3D(vehicle) + local otherVehiclesCollisionData = PathfinderUtil.setUpVehicleCollisionData(vehicle, vehiclesToIgnore) local vehicleData = PathfinderUtil.VehicleData(vehicle, true, 0.5) @@ -737,6 +743,7 @@ function PathfinderUtil.findDubinsPath(vehicle, startOffset, goalReferenceNode, return dubinsPath, solution:getLength(turnRadius) end + function PathfinderUtil.getNodePositionAndDirection(node, xOffset, zOffset) local x, _, z = localToWorld(node, xOffset or 0, 0, zOffset or 0) local lx, _, lz = localDirectionToWorld(node, 0, 0, 1) @@ -744,6 +751,13 @@ function PathfinderUtil.getNodePositionAndDirection(node, xOffset, zOffset) return x, z, yRot end +---@param vehicle Vehicle +---@return State3D position/heading of vehicle +function PathfinderUtil.getVehiclePositionAsState3D(vehicle) + local x, z, yRot = PathfinderUtil.getNodePositionAndDirection(AIDriverUtil.getDirectionNode(vehicle)) + return State3D(x, -z, courseGenerator.fromCpAngle(yRot)) +end + ------------------------------------------------------------------------------------------------------------------------ --- Interface function to start the pathfinder in the game ------------------------------------------------------------------------------------------------------------------------ @@ -760,13 +774,12 @@ end function PathfinderUtil.startPathfindingFromVehicleToWaypoint(vehicle, goalWaypoint, xOffset, zOffset, allowReverse, fieldNum, vehiclesToIgnore, maxFruitPercent, offFieldPenalty) - local x, z, yRot = PathfinderUtil.getNodePositionAndDirection(AIDriverUtil.getDirectionNode(vehicle)) - local start = State3D(x, -z, courseGenerator.fromCpAngle(yRot)) + local goal = State3D(goalWaypoint.x, -goalWaypoint.z, courseGenerator.fromCpAngleDeg(goalWaypoint.angle)) local offset = Vector(zOffset, -xOffset) goal:add(offset:rotate(goal.t)) return PathfinderUtil.startPathfindingFromVehicleToGoal( - vehicle, start, goal, allowReverse, fieldNum, vehiclesToIgnore, maxFruitPercent, offFieldPenalty) + vehicle, goal, allowReverse, fieldNum, vehiclesToIgnore, maxFruitPercent, offFieldPenalty) end ------------------------------------------------------------------------------------------------------------------------ --- Interface function to start the pathfinder in the game. The goal is a point at sideOffset meters from the goal node @@ -786,12 +799,10 @@ function PathfinderUtil.startPathfindingFromVehicleToNode(vehicle, goalNode, xOffset, zOffset, allowReverse, fieldNum, vehiclesToIgnore, maxFruitPercent, offFieldPenalty, mustBeAccurate) - local x, z, yRot = PathfinderUtil.getNodePositionAndDirection(AIDriverUtil.getDirectionNode(vehicle)) - local start = State3D(x, -z, courseGenerator.fromCpAngle(yRot)) x, z, yRot = PathfinderUtil.getNodePositionAndDirection(goalNode, xOffset, zOffset) local goal = State3D(x, -z, courseGenerator.fromCpAngle(yRot)) return PathfinderUtil.startPathfindingFromVehicleToGoal( - vehicle, start, goal, allowReverse, fieldNum, + vehicle, goal, allowReverse, fieldNum, vehiclesToIgnore, maxFruitPercent, offFieldPenalty, mustBeAccurate) end diff --git a/courseplay.lua b/courseplay.lua index 4bacf08e2..47a108188 100644 --- a/courseplay.lua +++ b/courseplay.lua @@ -84,6 +84,7 @@ local function initialize() 'Waypoint', 'StateModule', 'TriggerHandler', + 'BaleToCollect', 'AIDriver', 'CombineUnloadAIDriver', 'OverloaderAIDriver', @@ -95,6 +96,7 @@ local function initialize() 'PlowAIDriver', 'UnloadableFieldworkAIDriver', 'BaleLoaderAIDriver', + 'BaleCollectorAIDriver', 'BalerAIDriver', 'BaleWrapperAIDriver', 'CombineAIDriver', @@ -184,7 +186,7 @@ local function setGlobalData() courseplay.MODE_SEED_FERTILIZE = 4; courseplay.MODE_TRANSPORT = 5; courseplay.MODE_FIELDWORK = 6; - courseplay.MODE_COMBINE_SELF_UNLOADING = 7; --removed by Tommi + courseplay.MODE_BALE_COLLECTOR = 7; courseplay.MODE_FIELD_SUPPLY = 8; courseplay.MODE_SHOVEL_FILL_AND_EMPTY = 9; courseplay.MODE_BUNKERSILO_COMPACTER = 10; diff --git a/hud.lua b/hud.lua index 7d4f3e1f3..4965e033d 100644 --- a/hud.lua +++ b/hud.lua @@ -231,7 +231,7 @@ function courseplay.hud:setup() [courseplay.MODE_SEED_FERTILIZE] = { 220, 72, 252,40 }; [courseplay.MODE_TRANSPORT] = { 4,108, 36,76 }; [courseplay.MODE_FIELDWORK] = { 40,108, 72,76 }; - [courseplay.MODE_COMBINE_SELF_UNLOADING] = { 76,108, 108,76 }; + [courseplay.MODE_BALE_COLLECTOR] = { 76,108, 108,76 }; [courseplay.MODE_FIELD_SUPPLY] = { 112,108, 144,76 }; [courseplay.MODE_SHOVEL_FILL_AND_EMPTY] = { 148,108, 180,76 }; [courseplay.MODE_BUNKERSILO_COMPACTER] = { 219,431, 251,399 }; @@ -323,7 +323,7 @@ function courseplay.hud:setup() [courseplay.MODE_SEED_FERTILIZE] = { 40,144, 72,112 }; [courseplay.MODE_TRANSPORT] = { 76,144, 108,112 }; [courseplay.MODE_FIELDWORK] = { 112,144, 144,112 }; - [courseplay.MODE_COMBINE_SELF_UNLOADING] = { 148,144, 180,112 }; + [courseplay.MODE_BALE_COLLECTOR] = { 148,144, 180,112 }; [courseplay.MODE_FIELD_SUPPLY] = { 184,144, 216,112 }; [courseplay.MODE_SHOVEL_FILL_AND_EMPTY] = { 220,144, 252,112 }; [courseplay.MODE_BUNKERSILO_COMPACTER] = { 219,394, 251,362 }; @@ -355,8 +355,8 @@ function courseplay.hud:setup() [courseplay.MODE_SEED_FERTILIZE] = courseplay.utils:rgbToNormal( 30, 255, 156, 1), -- Light Green [courseplay.MODE_TRANSPORT] = courseplay.utils:rgbToNormal( 21, 198, 255, 1), -- Blue [courseplay.MODE_FIELDWORK] = courseplay.utils:rgbToNormal( 49, 52, 140, 1), -- Dark Blue - [courseplay.MODE_COMBINE_SELF_UNLOADING] = courseplay.utils:rgbToNormal(159, 29, 250, 1), -- Purple - [courseplay.MODE_FIELD_SUPPLY] = courseplay.utils:rgbToNormal(255, 27, 231, 1), -- Pink + [courseplay.MODE_BALE_COLLECTOR] = courseplay.utils:rgbToNormal(159, 29, 250, 1), -- Purple + [courseplay.MODE_FIELD_SUPPLY] = courseplay.utils:rgbToNormal(255, 27, 231, 1), -- Pink [courseplay.MODE_SHOVEL_FILL_AND_EMPTY] = courseplay.utils:rgbToNormal(231, 19, 19, 1), -- Red [courseplay.MODE_BUNKERSILO_COMPACTER] = courseplay.utils:rgbToNormal(231, 19, 19, 1), -- Red }; @@ -654,7 +654,6 @@ function courseplay.hud:updatePageContent(vehicle, page) --autoDriveModeSetting if not vehicle.cp.settings.autoDriveMode:isDisabled() then self:enableButtonWithFunction(vehicle,page, 'changeByX',vehicle.cp.settings.autoDriveMode) - -- self:disableButtonWithFunction(vehicle,page, 'toggle',vehicle.cp.settings.stopAtEnd) vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.autoDriveMode:getLabel() vehicle.cp.hud.content.pages[page][line][2].text = vehicle.cp.settings.autoDriveMode:getText() else @@ -671,7 +670,7 @@ function courseplay.hud:updatePageContent(vehicle, page) end elseif entry.functionToCall == 'returnToFirstPoint:changeByX' then --ReturnToFirstPointSetting - if vehicle.cp.canDrive then + if not vehicle.cp.settings.returnToFirstPoint:isDisabled() then self:enableButtonWithFunction(vehicle,page, 'changeByX',vehicle.cp.settings.returnToFirstPoint) self:disableButtonWithFunction(vehicle,page, 'setCustomSingleFieldEdge') vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.returnToFirstPoint:getLabel() @@ -1001,18 +1000,6 @@ function courseplay.hud:updatePageContent(vehicle, page) else self:disableButtonWithFunction(vehicle,page, 'switchCourseplayerSide') end - elseif entry.functionToCall == 'turnStage:toggle' then - --TurnStageSetting - if g_combineUnloadManager:getHasUnloaders(vehicle) then - --manual chopping: initiate/end turning maneuver - if not vehicle:getIsCourseplayDriving() then - self:enableButtonWithFunction(vehicle,page, 'toggle',vehicle.cp.settings.turnStage) - vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.turnStage:getLabel() - vehicle.cp.hud.content.pages[page][line][2].text = vehicle.cp.settings.turnStage:getText() - end - else - self:disableButtonWithFunction(vehicle,page, 'toggle',vehicle.cp.settings.turnStage) - end elseif entry.functionToCall == 'driverPriorityUseFillLevel:toggle' then --DriverPriorityUseFillLevelSetting vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.driverPriorityUseFillLevel:getLabel() @@ -1151,16 +1138,9 @@ function courseplay.hud:updatePageContent(vehicle, page) else self:disableButtonWithFunction(vehicle,page,'changeByX',vehicle.cp.settings.separateFillTypeLoading) end - elseif entry.functionToCall == 'automaticUnloadingOnField:toggle' then - --not used right now! - --AutomaticUnloadingOnFieldSetting - if not vehicle.cp.hasUnloadingRefillingCourse then - self:enableButtonWithFunction(vehicle,page,'toggle',vehicle.cp.settings.automaticUnloadingOnField) - vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.automaticUnloadingOnField:getLabel() - vehicle.cp.hud.content.pages[page][line][2].text = vehicle.cp.settings.automaticUnloadingOnField:getText() - else - self:disableButtonWithFunction(vehicle,page,'toggle',vehicle.cp.settings.automaticUnloadingOnField) - end + elseif entry.functionToCall == 'baleCollectionField:changeByX' then + vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.baleCollectionField:getLabel() + vehicle.cp.hud.content.pages[page][line][2].text = vehicle.cp.settings.baleCollectionField:getText() elseif entry.functionToCall == 'shovelStopAndGo:toggle' then --ShovelStopAndGoSetting vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.shovelStopAndGo:getLabel() @@ -2166,7 +2146,6 @@ function courseplay.hud:setCombineAIDriverContent(vehicle) self:addRowButton(vehicle,nil,'sendCourseplayerHome', 0, 3, 1 ) if vehicle.cp.isChopper then self:addRowButton(vehicle,nil,'switchCourseplayerSide', 0, 4, 1 ) - self:addRowButton(vehicle,vehicle.cp.settings.turnStage,'toggle', 0, 5, 1 ) else self:addRowButton(vehicle,vehicle.cp.settings.driverPriorityUseFillLevel,'toggle', 0, 4, 1 ) self:addRowButton(vehicle,vehicle.cp.settings.stopForUnload,'toggle', 0, 5, 1 ) @@ -2264,9 +2243,23 @@ function courseplay.hud:setLevelCompactAIDriverContent(vehicle) end -function courseplay.hud:setBaleLoaderAIDriverContent(vehicle) - self:debug(vehicle,"setBaleLoaderAIDriverContent") - self:addRowButton(vehicle,vehicle.cp.settings.automaticUnloadingOnField,'toggle', 3, 6, 1 ) +function courseplay.hud:setBaleCollectorAIDriverContent(vehicle) + self:debug(vehicle,"setBaleCollectorAIDriverContent") + self:addRowButton(vehicle,vehicle.cp.settings.autoDriveMode,'changeByX', 1, 3, 1 ) + self:addSettingsRow(vehicle, vehicle.cp.settings.baleCollectionField,'changeByX', 1, 4, 1 ) + self:addRowButton(vehicle,nil,'setCustomSingleFieldEdge', 1, 5, 1 ) + self:addSettingsRow(vehicle,nil,'setCustomFieldEdgePathNumber', 1, 5, 2 ) + self:setupCustomFieldEdgeButtons(vehicle,1,5) + self:addRowButton(vehicle,nil,'addCustomSingleFieldEdgeToList', 1, 6, 1 ) + -- shown in place of the custom field row when a course is loaded + + --page 3 settings + self:enablePageButton(vehicle, 3) + self:addSettingsRow(vehicle,nil,'changeTurnDiameter', 3, 1, 1 ) + + --page 8 fieldwork settings + self:enablePageButton(vehicle, 8) + self:addRowButton(vehicle,vehicle.cp.settings.useRealisticDriving,'toggle', 8, 4, 1 ) self:setReloadPageOrder(vehicle, -1, true) end diff --git a/img/iconSprite.dds b/img/iconSprite.dds index 80dff3f76..5d2ca1948 100644 Binary files a/img/iconSprite.dds and b/img/iconSprite.dds differ diff --git a/modDesc.xml b/modDesc.xml index c79564b47..31369b859 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -1,6 +1,6 @@ - 6.03.00042 + 6.03.00043 <!-- en=English de=German fr=French es=Spanish ru=Russian pl=Polish it=Italian br=Brazilian-Portuguese cs=Chinese(Simplified) ct=Chinese(Traditional) cz=Czech nl=Netherlands hu=Hungary jp=Japanese kr=Korean pt=Portuguese ro=Romanian tr=Turkish --> <en>CoursePlay SIX</en> diff --git a/reloadAIDriver.bat b/reloadAIDriver.bat index f69caeb63..46d770f1f 100644 --- a/reloadAIDriver.bat +++ b/reloadAIDriver.bat @@ -9,6 +9,7 @@ type PlowAIDriver.lua >> %outfile% type UnloadableFieldworkAIDriver.lua >> %outfile% type GrainTransportAIDriver.lua >> %outfile% type BaleLoaderAIDriver.lua >> %outfile% +type BaleCollectorAIDriver.lua >> %outfile% type BaleWrapperAIDriver.lua >> %outfile% type BalerAIDriver.lua >> %outfile% type CombineAIDriver.lua >> %outfile% diff --git a/settings.lua b/settings.lua index 66e22546c..a2f92203d 100644 --- a/settings.lua +++ b/settings.lua @@ -11,14 +11,16 @@ function courseplay:openCloseHud(vehicle, open) end; end; -function courseplay:setCpMode(vehicle, modeNum) +function courseplay:setCpMode(vehicle, modeNum, dontSetAIDriver) if vehicle.cp.mode ~= modeNum then vehicle.cp.mode = modeNum; - --courseplay:setNextPrevModeVars(vehicle); courseplay.utils:setOverlayUVsPx(vehicle.cp.hud.currentModeIcon, courseplay.hud.bottomInfo.modeUVsPx[modeNum], courseplay.hud.iconSpriteSize.x, courseplay.hud.iconSpriteSize.y); - --courseplay.buttons:setActiveEnabled(vehicle, 'all'); - --end - courseplay:setAIDriver(vehicle, modeNum) + if not dontSetAIDriver then + -- another ugly hack: when this is called from loadVehicleCPSettings, + -- setAIDriver fails as not everything is loaded yet, so just for that case, + -- don't call it from here. + courseplay:setAIDriver(vehicle, modeNum) + end end; end; @@ -43,6 +45,8 @@ function courseplay:setAIDriver(vehicle, mode) status,driver,err,errDriverName = xpcall(FillableFieldworkAIDriver, function(err) printCallstack(); return self,err,"FillableFieldworkAIDriver" end, vehicle) elseif mode == courseplay.MODE_FIELDWORK then status,driver,err,errDriverName = xpcall(UnloadableFieldworkAIDriver.create, function(err) printCallstack(); return self,err,"UnloadableFieldworkAIDriver" end, vehicle) + elseif mode == courseplay.MODE_BALE_COLLECTOR then + status,driver,err,errDriverName = xpcall(BaleCollectorAIDriver, function(err) printCallstack(); return self,err,"BaleCollectorAIDriver" end, vehicle) elseif mode == courseplay.MODE_BUNKERSILO_COMPACTER then status,driver,err,errDriverName = xpcall(LevelCompactAIDriver, function(err) printCallstack(); return self,err,"LevelCompactAIDriver" end, vehicle) elseif mode == courseplay.MODE_FIELD_SUPPLY then @@ -1806,35 +1810,63 @@ StartingPointSetting.START_AT_FIRST_POINT = 2 -- first waypoint StartingPointSetting.START_AT_CURRENT_POINT = 3 -- current waypoint StartingPointSetting.START_AT_NEXT_POINT = 4 -- nearest waypoint with approximately same direction as vehicle StartingPointSetting.START_WITH_UNLOAD = 5 -- start with unloading the combine (only for CombineUnloadAIDriver) +StartingPointSetting.START_COLLECTING_BALES = 6 -- start with unloading the combine (only for CombineUnloadAIDriver) function StartingPointSetting:init(vehicle) - SettingList.init(self, 'startingPoint', 'COURSEPLAY_START_AT_POINT', 'COURSEPLAY_START_AT_POINT', vehicle, - { - StartingPointSetting.START_AT_NEAREST_POINT, - StartingPointSetting.START_AT_FIRST_POINT , - StartingPointSetting.START_AT_CURRENT_POINT, - StartingPointSetting.START_AT_NEXT_POINT, - StartingPointSetting.START_WITH_UNLOAD - }, - { - "COURSEPLAY_NEAREST_POINT", - "COURSEPLAY_FIRST_POINT" , - "COURSEPLAY_CURRENT_POINT", - "COURSEPLAY_NEXT_POINT", - "COURSEPLAY_UNLOAD" - }) + local values, texts = self:getValuesForMode(vehicle.cp.mode) + SettingList.init(self, 'startingPoint', + 'COURSEPLAY_START_AT_POINT', 'COURSEPLAY_START_AT_POINT', vehicle, values, texts) +end + +function StartingPointSetting:getValuesForMode(mode) + if mode == courseplay.MODE_COMBI or mode == courseplay.MODE_OVERLOADER then + return { + StartingPointSetting.START_AT_NEAREST_POINT, + StartingPointSetting.START_AT_FIRST_POINT , + StartingPointSetting.START_AT_CURRENT_POINT, + StartingPointSetting.START_AT_NEXT_POINT, + StartingPointSetting.START_WITH_UNLOAD + }, + { + "COURSEPLAY_NEAREST_POINT", + "COURSEPLAY_FIRST_POINT" , + "COURSEPLAY_CURRENT_POINT", + "COURSEPLAY_NEXT_POINT", + "COURSEPLAY_UNLOAD", + } + elseif mode == courseplay.MODE_BALE_COLLECTOR then + return { + StartingPointSetting.START_AT_NEAREST_POINT, + StartingPointSetting.START_AT_FIRST_POINT , + StartingPointSetting.START_AT_NEXT_POINT, + StartingPointSetting.START_COLLECTING_BALES + }, + { + "COURSEPLAY_NEAREST_POINT", + "COURSEPLAY_FIRST_POINT" , + "COURSEPLAY_NEXT_POINT", + "COURSEPLAY_COLLECT_BALES", + } + else + return { + StartingPointSetting.START_AT_NEAREST_POINT, + StartingPointSetting.START_AT_FIRST_POINT , + StartingPointSetting.START_AT_CURRENT_POINT, + StartingPointSetting.START_AT_NEXT_POINT, + }, + { + "COURSEPLAY_NEAREST_POINT", + "COURSEPLAY_FIRST_POINT" , + "COURSEPLAY_CURRENT_POINT", + "COURSEPLAY_NEXT_POINT", + } + end end function StartingPointSetting:checkAndSetValidValue(new) - -- enable unload only for CombineUnloadAIDriver/Overloader - if self.vehicle.cp.driver and - self.vehicle.cp.mode ~= courseplay.MODE_COMBI and - self.vehicle.cp.mode ~= courseplay.MODE_OVERLOADER and - self.values[new] == StartingPointSetting.START_WITH_UNLOAD then - return 1 - else - return SettingList.checkAndSetValidValue(self, new) - end + -- make sure we always have a valid set for the current mode + self.values, self.texts = self:getValuesForMode(self.vehicle.cp.mode) + return SettingList.checkAndSetValidValue(self, new) end ---@class StartingLocationSetting : SettingList @@ -2053,6 +2085,10 @@ function ReturnToFirstPointSetting:init(vehicle) }) end +function ReturnToFirstPointSetting:isDisabled() + return not self.vehicle.cp.canDrive or self.vehicle.cp.mode == courseplay.MODE_BALE_COLLECTOR +end + function ReturnToFirstPointSetting:isReturnToStartActive() return self:get() == self.RETURN_TO_START or self:get() == self.RETURN_TO_START_AND_RELEASE_DRIVER end @@ -2119,10 +2155,9 @@ end --- Setting to select a field ---@class FieldNumberSetting : SettingList FieldNumberSetting = CpObject(SettingList) -function FieldNumberSetting:init(vehicle) +function FieldNumberSetting:init(name, label, toolTip, vehicle) local values, texts = self:loadFields() - SettingList.init(self, 'fieldNumbers', 'COURSEPLAY_FIELD', 'COURSEPLAY_FIELD', - vehicle, values, texts) + SettingList.init(self, name, label, toolTip, vehicle, values, texts) end function FieldNumberSetting:loadFields() @@ -2130,10 +2165,13 @@ function FieldNumberSetting:loadFields() local texts = {} for fieldNumber, _ in pairs( courseplay.fields.fieldData ) do table.insert(values, fieldNumber) - table.insert(texts, fieldNumber) end + -- numeric sort first table.sort( values, function( a, b ) return a < b end ) - table.sort( texts, function( a, b ) return a < b end ) + -- then convert to text + for _, fieldNumber in ipairs(values) do + table.insert(texts, tostring(fieldNumber)) + end return values, texts end @@ -2149,16 +2187,40 @@ function FieldNumberSetting:refresh() self.current = math.min(self.current, #self.values) end +-- see above, refresh in case it was not initialized +function FieldNumberSetting:get() + if #self.values == 0 then + self:refresh() + end + return SettingList.get(self) +end + +-- see above, refresh in case it was not initialized +function FieldNumberSetting:getText() + if #self.values == 0 then + self:refresh() + end + return SettingList.getText(self) +end + +function FieldNumberSetting:changeByX(x) + self:refresh() + SettingList.changeByX(self, x) +end + +--- Field to collect the bales from +---@class BaleCollectionFieldSetting : FieldNumberSetting +BaleCollectionFieldSetting = CpObject(FieldNumberSetting) +function BaleCollectionFieldSetting:init(vehicle) + FieldNumberSetting.init(self, 'baleCollectionField', 'COURSEPLAY_FIELD', 'COURSEPLAY_FIELD', vehicle) +end + --- Search combine on field ---@class SearchCombineOnFieldSetting : FieldNumberSetting SearchCombineOnFieldSetting = CpObject(FieldNumberSetting) function SearchCombineOnFieldSetting:init(vehicle) - FieldNumberSetting.init(self, vehicle) - self.name = 'searchCombineOnField' - self.label = 'COURSEPLAY_SEARCH_COMBINE_ON_FIELD' - self.tooltip = 'COURSEPLAY_SEARCH_COMBINE_ON_FIELD' - self.xmlKey = 'searchCombineOnField' - self.xmlAttribute = '#fieldNumber' + FieldNumberSetting.init(self, 'searchCombineOnField', + 'COURSEPLAY_SEARCH_COMBINE_ON_FIELD', 'COURSEPLAY_SEARCH_COMBINE_ON_FIELD', vehicle) self:addNoneSelected() end @@ -2188,7 +2250,6 @@ end SelectedCombineToUnloadSetting = CpObject(SettingList) function SelectedCombineToUnloadSetting:init() - print("SelectedCombineToUnloadSetting:init()") self.name = 'selectedCombineToUnload' self.label = 'COURSEPLAY_SEARCH_COMBINE_ON_FIELD' self.tooltip = 'COURSEPLAY_SEARCH_COMBINE_ON_FIELD' @@ -2386,14 +2447,6 @@ function AutomaticCoverHandlingSetting:init(vehicle) self:set(true) end ---no Function!! ----@class AutomaticUnloadingOnFieldSetting : BooleanSetting -AutomaticUnloadingOnFieldSetting = CpObject(BooleanSetting) -function AutomaticUnloadingOnFieldSetting:init(vehicle) - BooleanSetting.init(self, 'automaticUnloadingOnField', 'COURSEPLAY_UNLOADING_ON_FIELD', 'COURSEPLAY_UNLOADING_ON_FIELD',vehicle, {'COURSEPLAY_MANUAL','COURSEPLAY_AUTOMATIC'}) - self:set(false) -end - ---@class DriverPriorityUseFillLevelSetting : BooleanSetting DriverPriorityUseFillLevelSetting = CpObject(BooleanSetting) function DriverPriorityUseFillLevelSetting:init(vehicle) @@ -2491,9 +2544,6 @@ function ShowMapHotspotSetting:getMapHotspotText(vehicle) end function ShowMapHotspotSetting:createMapHotspot() - if self.vehicle.cp.mode == courseplay.MODE_COMBINE_SELF_UNLOADING then - return - end --[[ local hotspotX, _, hotspotZ = getWorldTranslation(vehicle.rootNode); local _, textSize = getNormalizedScreenValues(0, 6); @@ -3032,13 +3082,6 @@ function TurnOnFieldSetting:init(vehicle) self:set(true) end ----@class TurnStageSetting : BooleanSetting -TurnStageSetting = CpObject(BooleanSetting) -function TurnStageSetting:init(vehicle) - BooleanSetting.init(self, 'turnStage','COURSEPLAY_TURN_MANEUVER', 'COURSEPLAY_TURN_MANEUVER', vehicle, {'COURSEPLAY_START','COURSEPLAY_FINISH'}) - self:set(false) -end - ---@class RefillUntilPctSetting : PercentageSettingList RefillUntilPctSetting = CpObject(PercentageSettingList) function RefillUntilPctSetting:init(vehicle) @@ -4143,7 +4186,7 @@ function SettingsContainer.createVehicleSpecificSettings(vehicle) container:addSetting(EnableVisualWaypointsTemporary, vehicle) container:addSetting(StopAtEndSetting, vehicle) container:addSetting(AutomaticCoverHandlingSetting, vehicle) - container:addSetting(AutomaticUnloadingOnFieldSetting, vehicle) + container:addSetting(BaleCollectionFieldSetting, vehicle) container:addSetting(DriverPriorityUseFillLevelSetting, vehicle) container:addSetting(UseRecordingSpeedSetting, vehicle) container:addSetting(WarningLightsModeSetting, vehicle) @@ -4154,7 +4197,6 @@ function SettingsContainer.createVehicleSpecificSettings(vehicle) container:addSetting(DriveUnloadNowSetting, vehicle) container:addSetting(CombineWantsCourseplayerSetting, vehicle) container:addSetting(TurnOnFieldSetting, vehicle) - container:addSetting(TurnStageSetting, vehicle) container:addSetting(GrainTransportDriver_SiloSelectedFillTypeSetting, vehicle) container:addSetting(FillableFieldWorkDriver_SiloSelectedFillTypeSetting, vehicle) container:addSetting(FieldSupplyDriver_SiloSelectedFillTypeSetting, vehicle) diff --git a/test/mock-Courseplay.lua b/test/mock-Courseplay.lua index 3e21d8798..06db1427d 100644 --- a/test/mock-Courseplay.lua +++ b/test/mock-Courseplay.lua @@ -73,7 +73,7 @@ courseplay.MODE_OVERLOADER = 3; courseplay.MODE_SEED_FERTILIZE = 4; courseplay.MODE_TRANSPORT = 5; courseplay.MODE_FIELDWORK = 6; -courseplay.MODE_COMBINE_SELF_UNLOADING = 7; +courseplay.MODE_BALE_COLLECTOR = 7; courseplay.MODE_LIQUIDMANURE_TRANSPORT = 8; courseplay.MODE_SHOVEL_FILL_AND_EMPTY = 9; courseplay.MODE_BUNKERSILO_COMPACTER = 10; diff --git a/toolManager.lua b/toolManager.lua index cd92d29cf..9d30cc23d 100644 --- a/toolManager.lua +++ b/toolManager.lua @@ -37,18 +37,18 @@ function courseplay:updateOnAttachOrDetach(vehicle) -- TODO: refactor this (if it is even still needed), this ignore list is all over the place... vehicle.cpTrafficCollisionIgnoreList = {} - if vehicle.cp and vehicle.cp.settings then - vehicle.cp.settings:validateCurrentValues() - if vehicle.cp.driver then - vehicle.cp.driver:refreshHUD() - end - end - courseplay:resetTools(vehicle) + courseplay:setAIDriver(vehicle, vehicle.cp.mode) + + vehicle.cp.settings:validateCurrentValues() -- reset tool offset to the preconfigured value if exists vehicle.cp.settings.toolOffsetX:setToConfiguredValue() + if vehicle.cp.driver then + vehicle.cp.driver:refreshHUD() + end + end --- Set up tool configuration after something is attached or detached @@ -490,9 +490,6 @@ function courseplay:getIsToolCombiValidForCpMode(vehicle,cpModeToCheck) if cpModeToCheck == 5 then return true; end - if cpModeToCheck == 7 then - return false; - end local callback = {} callback.isDisallowedOkay = true courseplay:getIsToolValidForCpMode(vehicle,cpModeToCheck,callback) diff --git a/translations/translation_br.xml b/translations/translation_br.xml index b890686f3..80ad9b501 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -3,9 +3,6 @@ <texts> <text name="COURSEPLAY_RECORDING_START" text="Iniciar gravação de rota" /> <text name="COURSEPLAY_DRIVER" text="Motorista" /> - <text name="COURSEPLAY_PPC_OFF" text="Modo Normal" /> - <text name="COURSEPLAY_PPC_ON" text="Modo Experimental" /> - <text name="COURSEPLAY_AIDRIVER" text="AI Driver" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Dirigir" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Parar" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Parar no último ponto" /> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": equipamento precisa ser descarregado." /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": equipamento precisa ser recarregado." /> <text name="COURSEPLAY_UNLOADING_BALES" text="descarregando fardos." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="em tráfego." /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="em tráfego, desacelerando" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="trigger alcançado" /> @@ -285,6 +283,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="Ponto atual" /> <text name="COURSEPLAY_NEXT_POINT" text="Segundo ponto mais próximo" /> <text name="COURSEPLAY_UNLOAD" text="Unloading" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Caminho de borda do campo" /> <text name="COURSEPLAY_FIELD" text="Campo" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(Rota atual carregada)" /> diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 137cc33e5..69f50b744 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -5,9 +5,6 @@ <text name="COURSEPLAY_DRIVER" text="任务车辆" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="开始卸载" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="停止卸载" /> - <text name="COURSEPLAY_PPC_OFF" text="普通模式" /> - <text name="COURSEPLAY_PPC_ON" text="高级模式" /> <!-- a new driving algorithm using a pure pursuit controller --> - <text name="COURSEPLAY_AIDRIVER" text="AI 驾驶" /> <!-- enable new AI driver --> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Stop at last point" /> <text name="COURSEPLAY_NUMBER_OF_RUNS" text="任务执行次数" /> <text name="COURSEPLAY_RESET_NUMBER_OF_RUNS" text="重置运行次数"/> <!-- Todo translate --> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": 工具需要卸载。" /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": 工具需要装载。" /> <text name="COURSEPLAY_UNLOADING_BALES" text="卸载。" /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="卡住了!" /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="slowing down for traffic" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="已进入触发区" /> @@ -285,6 +283,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="当前点" /> <text name="COURSEPLAY_NEXT_POINT" text="下一个路径点" /> <text name="COURSEPLAY_UNLOAD" text="Unloading" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="选择地块范围编号" /> <text name="COURSEPLAY_FIELD" text="地块范围路径编号:" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="当前任务" /> diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index ab338e0e1..93e6a7224 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -3,9 +3,6 @@ <texts> <text name="COURSEPLAY_RECORDING_START" text="Zapnout ukládání trasy" /> <text name="COURSEPLAY_DRIVER" text="Odvozce:" /> - <text name="COURSEPLAY_PPC_OFF" text="normal mode" /> - <text name="COURSEPLAY_PPC_ON" text="experimentální mód" /> <!-- a new driving algorithm using a pure pursuit controller --> - <text name="COURSEPLAY_AIDRIVER" text="AI driver" /> <!-- enable new AI driver --> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Aktivovat odvozce" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Zastavit odvozce" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Zastavit na posledním bodě " /> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": potřebuje vyložit." /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": pracovní stroj musí být doplněn." /> <text name="COURSEPLAY_UNLOADING_BALES" text="skládá balíky." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="stojí v dopravní zácpě." /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="zpomalil díky provozu." /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="Místo vysypání dosaženo" /> @@ -285,6 +283,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="na vybraném bodě" /> <text name="COURSEPLAY_NEXT_POINT" text="následující čekací bod" /> <text name="COURSEPLAY_UNLOAD" text="Vykládání" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Trasa okolo pole:" /> <text name="COURSEPLAY_FIELD" text="Pole" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(načtená trasa)" /> diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 23a67ff42..1ea904561 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -5,9 +5,6 @@ <text name="COURSEPLAY_DRIVER" text="Abfahrer" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Abfahrer starten" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Abfahrer stoppen" /> - <text name="COURSEPLAY_PPC_OFF" text="Normaler Modus" /> - <text name="COURSEPLAY_PPC_ON" text="Experimenteller Modus" /> <!-- a new driving algorithm using a pure pursuit controller --> - <text name="COURSEPLAY_AIDRIVER" text="KI Fahrer" /> <!-- enable new AI driver --> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Stop am Endpunkt" /> <text name="COURSEPLAY_NUMBER_OF_RUNS" text="Anzahl der Durchläufe"/> <text name="COURSEPLAY_RESET_NUMBER_OF_RUNS" text="Durchläufe zurücksetzen"/> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": Gerät muss entladen werden." /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": Gerät muss aufgefüllt werden" /> <text name="COURSEPLAY_UNLOADING_BALES" text="Ballen werden entladen." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="Steckt im Verkehr fest" /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="Bremse ab auf Grund von Verkehr" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="Abladestelle erreicht" /> @@ -285,6 +283,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="aktuellen Wegpunkt" /> <text name="COURSEPLAY_NEXT_POINT" text="nächsten passenden Wegpunkt" /> <text name="COURSEPLAY_UNLOAD" text="Unloading" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Feldrandkurs" /> <text name="COURSEPLAY_FIELD" text="Feld" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(aktuell geladener Kurs)" /> diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 7de65bac4..2b6efd5cc 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -5,9 +5,6 @@ <text name="COURSEPLAY_DRIVER" text="Driver" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Start driver" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Stop driver" /> - <text name="COURSEPLAY_PPC_OFF" text="Normal mode" /> - <text name="COURSEPLAY_PPC_ON" text="Experimental mode" /> <!-- a new driving algorithm using a pure pursuit controller --> - <text name="COURSEPLAY_AIDRIVER" text="AI driver" /> <!-- enable new AI driver --> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Stop at last point" /> <text name="COURSEPLAY_NUMBER_OF_RUNS" text="Number of runs to perform" /> <text name="COURSEPLAY_RESET_NUMBER_OF_RUNS" text="reset current number of runs"/> @@ -41,7 +38,7 @@ <text name="COURSEPLAY_HEADLAND_DIRECTION" text="Headland counter-/clockwise" /> <text name="COURSEPLAY_HEADLAND_BEFORE_AFTER" text="Headland before/after field course" /> <text name="COURSEPLAY_GENERATE_FIELD_COURSE" text="Generate field course" /> - <text name="COURSEPLAY_MULTI_TOOLS" text="Multiple Tools" /> <!-- Todo use somthing better --> + <text name="COURSEPLAY_MULTI_TOOLS" text="Multiple tools" /> <text name="COURSEPLAY_DISTANCE" text="Distance" /> <text name="COURSEPLAY_WAITING_TIME" text="Waiting time" /> <text name="COURSEPLAY_MINUTES" text="%d min" /> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": work tool needs to be unloaded." /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": work tool needs to be refilled." /> <text name="COURSEPLAY_UNLOADING_BALES" text="unloading bales." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="is in traffic." /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="slowing down for traffic" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="tip trigger reached" /> @@ -122,10 +120,10 @@ <text name="COURSEPLAY_PAGE_TITLE_COMBINE_CONTROLS" text="Combine controls" /> <text name="COURSEPLAY_PAGE_TITLE_MANAGE_COURSES" text="Manage courses" /> <text name="COURSEPLAY_PAGE_TITLE_CHOOSE_FOLDER" text="Choose folder" /> - <text name="COURSEPLAY_PAGE_TITLE_COMBI_MODE" text="vehicle specific settings" /> <!-- TODO TRANSLATE: --> + <text name="COURSEPLAY_PAGE_TITLE_COMBI_MODE" text="Vehicle specific settings" /> <text name="COURSEPLAY_PAGE_TITLE_MANAGE_COMBINES" text="Manage combines" /> <text name="COURSEPLAY_PAGE_TITLE_SPEEDS" text="Speed limits" /> - <text name="COURSEPLAY_PAGE_TITLE_DRIVING_SETTINGS" text="general driving settings" /> <!-- TODO TRANSLATE: --> + <text name="COURSEPLAY_PAGE_TITLE_DRIVING_SETTINGS" text="General driving settings" /> <text name="COURSEPLAY_PAGE_TITLE_GENERAL_SETTINGS" text="Settings" /> <text name="COURSEPLAY_COMBINE_IS_TURNING" text="Combine is turning" /> <text name="COURSEPLAY_NO_WORK_AREA" text="No work area at this course" /> @@ -171,7 +169,7 @@ <text name="COURSEPLAY_ENDING_LOCATION" text="Ending location" /> <text name="COURSEPLAY_STARTING_DIRECTION" text="Starting direction" /> <text name="COURSEPLAY_RETURN_TO_FIRST_POINT" text="Return to first point" /> - <text name="COURSEPLAY_RETURN_TO_FIRST_POINT_RELEASE_DRIVER" text="deactivated and release CP Driver" /> + <text name="COURSEPLAY_RETURN_TO_FIRST_POINT_RELEASE_DRIVER" text="Stop and dismiss driver" /> <text name="COURSEPLAY_RETURN_TO_FIRST_POINT_RETURN_TO_START_AND_RELEASE_DRIVER" text="activated and release CP Driver" /> <text name="COURSEPLAY_CORNER_1" text="South-West" /> <text name="COURSEPLAY_CORNER_2" text="North-West" /> @@ -232,7 +230,7 @@ <text name="COURSEPLAY_HEADLAND_REVERSE_MANEUVER_TYPE" text="Headland reverse maneuver"/> <text name="COURSEPLAY_HEADLAND_REVERSE_MANEUVER_TYPE_STRAIGHT" text="Straight" /> <text name="COURSEPLAY_HEADLAND_REVERSE_MANEUVER_TYPE_CURVE" text="Curve" /> - <text name="COURSEPLAY_CENTER_MODE" text="Field center (experimental)" /> + <text name="COURSEPLAY_CENTER_MODE" text="Field center" /> <text name="COURSEPLAY_CENTER_MODE_UP_DOWN" text="Up/Down" /> <text name="COURSEPLAY_CENTER_MODE_CIRCULAR" text="Racetrack" /> <text name="COURSEPLAY_CENTER_MODE_SPIRAL" text="Spiral" /> @@ -245,7 +243,7 @@ <text name="COURSEPLAY_DEACTIVATED" text="Deactivated" /> <text name="COURSEPLAY_RIDGEMARKERS" text="Ridge markers" /> <text name="COURSEPLAY_YES_NO_RIDGEMARKERS" text="Ridge markers on/off" /> - <text name="COURSEPLAY_KEEP_CURRENT_STEERING" text="Keep Current Steering" /> + <text name="COURSEPLAY_KEEP_CURRENT_STEERING" text="Keep current steering" /> <text name="COURSEPLAY_KEEP_CURRENT_STEERING_TOOLTIP " text="Courseplay won't change the current crab steering setting of the vehicle" /> <text name="COURSEPLAY_UNLOADING_ON_FIELD" text="Unloading on field" /> <text name="COURSEPLAY_AUTOMATIC" text="Automatic" /> @@ -286,6 +284,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="Current waypoint" /> <text name="COURSEPLAY_NEXT_POINT" text="Next closest waypoint" /> <text name="COURSEPLAY_UNLOAD" text="Unloading" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Field edge path" /> <text name="COURSEPLAY_FIELD" text="Field" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(Currently loaded course)" /> @@ -334,7 +333,7 @@ <text name="COURSEPLAY_DRIVECONTROL" text="Drive Control Setting" /> <text name="COURSEPLAY_DRIVECONTROL_MODE_0" text="Automatic" /> <text name="COURSEPLAY_DRIVECONTROL_MODE_1" text="4WD Always" /> - <text name="COURSEPLAY_DRIVECONTROL_MODE_2" text="4WD Always & Foward Diff on for Fieldwork" /> + <text name="COURSEPLAY_DRIVECONTROL_MODE_2" text="4WD Always & Forward Diff on for Fieldwork" /> <text name="COURSEPLAY_DRIVECONTROL_MODE_3" text="4WD Always & Rear Diff on for Fieldwork" /> <text name="COURSEPLAY_DRIVECONTROL_MODE_4" text="4WD Always & Both Diffs on for Fieldwork" /> <text name="COURSEPLAY_YES_NO_FIELDSCAN" text="Do you want the fields to be scanned automatically?" /> @@ -398,16 +397,16 @@ <text name="COURSEPLAY_MODE10_MODE_LEVELING" text="Leveling" /> <text name="COURSEPLAY_MODE10_MODE_BUILDUP" text="Filling up" /> <text name="COURSEPLAY_MODE10_MODE_COMPACTING" text="Compacting" /> - <text name="COURSEPLAY_MODE10_SEARCH_MODE" text="Automaticly stop for" /> - <text name="COURSEPLAY_MODE10_SEARCH_MODE_CP" text="Just courseplayer/autodrive driver" /> + <text name="COURSEPLAY_MODE10_SEARCH_MODE" text="Automatically stop for" /> + <text name="COURSEPLAY_MODE10_SEARCH_MODE_CP" text="Courseplay/Autodrive driver only" /> <text name="COURSEPLAY_MODE10_SEARCH_MODE_ALL" text="All player controlled unloaders" /> <text name="COURSEPLAY_MODE10_SEARCHRADIUS" text="Detect unloaders in range of " /> <text name="COURSEPLAY_MODE10_MAX_BUNKERSPEED" text="Max speed while pushing" /> <text name="COURSEPLAY_MODE10_BLADE_HEIGHT" text="Blade height" /> - <text name="COURSEPLAY_MODE10_NOSILO" text="No bunkersilo detected at start point" /> + <text name="COURSEPLAY_MODE10_NOSILO" text="No bunker silo detected at start point" /> <text name="COURSEPLAY_MODE10_NOBLADE" text="No blade attached" /> <text name="COURSEPLAY_MODE10_NOFRONTBLADE" text="Front mounted blade is not supported" /> - <text name="COURSEPLAY_MODE10_SETTINGS" text="BunkerSilo settings" /> + <text name="COURSEPLAY_MODE10_SETTINGS" text="Bunker silo settings" /> <text name="COURSEPLAY_MODE10_BLADE_WIDTH" text="Blade width" /> <text name="COURSEPLAY_MODE10_SILO_LOADEDBY" text="Silo is being filled by" /> <text name="COURSEPLAY_MODE10_DRIVINGTHROUGH" text="Driving through" /> @@ -448,8 +447,8 @@ <text name="COURSEPLAY_YES_NO_PIPE_ALWAYS_UNFOLD" text="Pipe always out when Combine waits for unload" /> <text name="COURSEPLAY_SHOW_MINI_HUD" text="Show Mini-HUD" /> <text name="COURSEPLAY_YES_NO_SHOW_MINI_HUD" text="Mini-HUD on/off" /> - <text name="COURSEPLAY_STRAW_ON_HEADLAND" text="Harvester Straw discharge" /> - <text name="COURSEPLAY_STRAW_ON_ONLY_CENTER" text="only Center" /> + <text name="COURSEPLAY_STRAW_ON_HEADLAND" text="harvester straw discharge" /> + <text name="COURSEPLAY_STRAW_ON_ONLY_CENTER" text="only center" /> <text name="COURSEPLAY_YES_NO_STRAW_ON_HEADLAND" text="Straw discharge on/off/only Center" /> <text name="COURSEPLAY_FERTILIZE_OPTION" text="Use fertilizer of seeder" /> <text name="COURSEPLAY_YES_NO_FERTILIZE_OPTION" text="Activate or deactivate the fertilizer function of the seeder" /> @@ -469,8 +468,8 @@ <text name="COURSEPLAY_IMPLEMENT_RAISE_TIME_TOOLTIP" text="When to raise implements when ending a row or starting a headland turn" /> <text name="COURSEPLAY_IMPLEMENT_LOWER_TIME" text="When to lower implements" /> <text name="COURSEPLAY_IMPLEMENT_LOWER_TIME_TOOLTIP" text="When to lower implements when starting a row or ending a headland turn" /> - <text name="COURSEPLAY_SHOULD_FOLD_IMPLEMENT" text="Fold Implement" /> - <text name="COURSEPLAY_SHOULD_FOLD_IMPLEMENT_TOOLTIP" text="Turn on/off if the Implement should be folded when work is done" /> + <text name="COURSEPLAY_SHOULD_FOLD_IMPLEMENT" text="Fold implement" /> + <text name="COURSEPLAY_SHOULD_FOLD_IMPLEMENT_TOOLTIP" text="Turn on/off if the implement should be folded when work is done" /> <text name="COURSEPLAY_USE_PATHFINDING_IN_TURNS" text="Use pathfinding in turns" /> <text name="COURSEPLAY_USE_PATHFINDING_IN_TURNS_TOOLTIP" text="Use pathfinding in turns on odd shaped fields, for example to drive on the headland to the next row" /> @@ -481,23 +480,23 @@ <text name="COURSEPLAY_ALLOW_REVERSE_FOR_PATHFINDING_IN_TURNS" text="Reversing for pathfinding in turns" /> <text name="COURSEPLAY_ALLOW_REVERSE_FOR_PATHFINDING_IN_TURNS_TOOLTIP" text="Allow finding a turn path which needs reverse driving (with non-towed implements only)" /> - <text name="COURSEPLAY_NO_SELECTED_FILLTYPE" text="no fillType selected!/no Refillcourse supported!!" /> - <text name="COURSEPLAY_ADD_FILLTYPE" text="add new fillType" /> + <text name="COURSEPLAY_NO_SELECTED_FILLTYPE" text="no fill type selected!/no refill course supported!!" /> + <text name="COURSEPLAY_ADD_FILLTYPE" text="add new fill type" /> <text name="COURSEPLAY_REACHED_REFILL_POINT" text="has reached refill point." /> - <text name="COURSEPLAY_WRONG_FILLTYPE_FOR_TRIGGER" text="has wrong fillType for Trigger." /> + <text name="COURSEPLAY_WRONG_FILLTYPE_FOR_TRIGGER" text="has wrong fill type for trigger." /> <text name="COURSEPLAY_ALLOW_UNLOAD_ON_FIRST_HEADLAND" text="Unload harvester on first headland" /> <text name="COURSEPLAY_ALLOW_UNLOAD_ON_FIRST_HEADLAND_TOOLTIP" text="Allow unloading a moving harvester on the first headland, unloader drives off the field." /> <text name="COURSEPLAY_RUNCOUNTER_ERROR_FOR_TRIGGER" text="last runCounter = 0" /> <text name="COURSEPLAY_LOADING_SEPARATE_FILLTYPES" text="Loading separate FillTypes" /> - <text name="COURSEPLAY_LOADING_SEPARATE_FILLTYPES_TRAILERS" text="Separate Trailers:" /> - <text name="COURSEPLAY_MANUAL_LOADING" text="manual Loading" /> + <text name="COURSEPLAY_LOADING_SEPARATE_FILLTYPES_TRAILERS" text="Separate trailers:" /> + <text name="COURSEPLAY_MANUAL_LOADING" text="manual loading" /> <text name="COURSEPLAY_WAITING_FOR_UNLOADERS" text="waiting for unloaders" /> - <text name="COURSEPLAY_WAITING_FOR_LEVELCOMPACTAIDRIVER" text="waiting for Siloworker" /> + <text name="COURSEPLAY_WAITING_FOR_LEVELCOMPACTAIDRIVER" text="waiting for silo worker" /> <text name="COURSEPLAY_AUTOREPAIR" text="Automatic repair" /> - <text name="COURSEPLAY_AUTOREPAIR_TOOLTIP" text="Repairs automatically on the Field." /> + <text name="COURSEPLAY_AUTOREPAIR_TOOLTIP" text="Repairs automatically on the field." /> <text name="COURSEPLAY_AUTOREPAIR_OFF" text="Don't repair" /> <text name="COURSEPLAY_AUTOREPAIR_ALWAYS" text="Keep it healthy" /> diff --git a/translations/translation_es.xml b/translations/translation_es.xml index df0fb4f24..ed668c63c 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -5,9 +5,6 @@ <text name="COURSEPLAY_DRIVER" text="Conductor" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Empezar a conducir" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Parar de conducir" /> - <text name="COURSEPLAY_PPC_OFF" text="Modo normal" /> - <text name="COURSEPLAY_PPC_ON" text="Modo experimental" /> - <text name="COURSEPLAY_AIDRIVER" text="Conductor IA" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Stop at last point" /> <text name="COURSEPLAY_NUMBER_OF_RUNS" text="Número de viajes a realizar" /> <text name="COURSEPLAY_RESET_NUMBER_OF_RUNS" text="Reiniciar número de viajes"/> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": necesita ser descargado." /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": necesita ser rellenado." /> <text name="COURSEPLAY_UNLOADING_BALES" text="descargando pacas." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="Atasco de trafico." /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="reduciendo velocidad por el tráfico" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="Punto de descarga alcanzado" /> @@ -286,6 +284,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="Punto actual" /> <text name="COURSEPLAY_NEXT_POINT" text="Siguiente punto más cercano" /> <text name="COURSEPLAY_UNLOAD" text="Zona de descarga" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Ruta del borde del campo" /> <text name="COURSEPLAY_FIELD" text="Campo" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(Ruta actualmente cargada)" /> diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index 42217941e..496a87871 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -5,9 +5,6 @@ <text name="COURSEPLAY_DRIVER" text="Remorque" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Rappeler la remorque" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Stopper la remorque" /> - <text name="COURSEPLAY_PPC_OFF" text="Mode normal" /> - <text name="COURSEPLAY_PPC_ON" text="Mode expérimental" /> <!-- a new driving algorithm using a pure pursuit controller --> - <text name="COURSEPLAY_AIDRIVER" text="Ouvrier AI" /> <!-- enable new AI driver --> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="S'arrêter au dernier point" /> <text name="COURSEPLAY_NUMBER_OF_RUNS" text="Nombre de tours à effectuer" /> <text name="COURSEPLAY_RESET_NUMBER_OF_RUNS" text="Réinitialiser le nombre de tours"/> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": doit être vidé." /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": doit être ravitaillé." /> <text name="COURSEPLAY_UNLOADING_BALES" text="décharge les ballots." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="est bloqué par un véhicule." /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="ralentit pour laisser passer un véhicule." /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="le trigger est atteint" /> @@ -285,6 +283,8 @@ <text name="COURSEPLAY_FIRST_POINT" text="Premier point" /> <text name="COURSEPLAY_CURRENT_POINT" text="Point actuel" /> <text name="COURSEPLAY_NEXT_POINT" text="Prochain point" /> + <text name="COURSEPLAY_UNLOAD" text="Unloading" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Contour de la parcelle" /> <text name="COURSEPLAY_FIELD" text="Parcelle" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="Course actuelle" /> diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index 647215cc5..beb963981 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -3,9 +3,6 @@ <texts> <text name="COURSEPLAY_RECORDING_START" text="Útvonal rögzítés indítása" /> <text name="COURSEPLAY_DRIVER" text="Útvonal" /> - <text name="COURSEPLAY_PPC_OFF" text="normál mód" /> - <text name="COURSEPLAY_PPC_ON" text="kísérleti mód" /> - <text name="COURSEPLAY_AIDRIVER" text="AI sofőr" /> <!-- enable new AI driver --> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Szállító elindítása" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Szállító megállítása" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Stop at last point" /> @@ -71,6 +68,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": A munka folytatásához ki kell ürítened!" /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": A munkaeszközt újra kell tölteni." /> <text name="COURSEPLAY_UNLOADING_BALES" text="kirakja a bálákat." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="dugóban van (forgalomban)" /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="slowing down for traffic" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="Elérte a triggert" /> @@ -248,11 +246,11 @@ <text name="COURSEPLAY_FINISH" text="Vége" /> <text name="COURSEPLAY_NO_VALID_COURSE" text="Az útvonal nem megfelelő" /> <text name="COURSEPLAY_FERTILIZERFUNCTION" text="vetőgép műtrágyaszóró funkció" /> - <text name="COURSEPLAY_COMBINE_CONVOY" text="jármű konvoj" /> <!-- TODO: translate --> - <text name="COURSEPLAY_CONVOY_MAX_DISTANCE" text="maximális távolság" /> <!-- TODO: translate --> - <text name="COURSEPLAY_CONVOY_MIN_DISTANCE" text="minimális távolság" /> <!-- TODO: translate --> - <text name="COURSEPLAY_COMBINE_IN_FRUIT" text="A kombájn csöve termény felett van" /> <!-- TODO: this is the local InfoText in mode2 with pathfinding active when the combines pipe is in fruit --> - <text name="COURSEPLAY_MODESPECIFIC_SETTINGS" text="mód specifikus beállítások" /> <!-- TODO: translate --> + <text name="COURSEPLAY_COMBINE_CONVOY" text="jármű konvoj" /> + <text name="COURSEPLAY_CONVOY_MAX_DISTANCE" text="maximális távolság" /> + <text name="COURSEPLAY_CONVOY_MIN_DISTANCE" text="minimális távolság" /> + <text name="COURSEPLAY_COMBINE_IN_FRUIT" text="A kombájn csöve termény felett van" /> + <text name="COURSEPLAY_MODESPECIFIC_SETTINGS" text="mód specifikus beállítások" /> <text name="COURSEPLAY_KEEP_CURRENT_STEERING" text="Keep Current Steering" /> <text name="COURSEPLAY_KEEP_CURRENT_STEERING_TOOLTIP " text="Courseplay won't change the current crab steering setting of the vehicle" /> @@ -278,9 +276,10 @@ <text name="COURSEPLAY_START_AT_POINT" text="Útvonal kezdése:" /> <text name="COURSEPLAY_NEAREST_POINT" text="legközelebbi útpont" /> <text name="COURSEPLAY_FIRST_POINT" text="első útpont" /> - <text name="COURSEPLAY_CURRENT_POINT" text="Aktuális útpont" /> + <text name="COURSEPLAY_CURRENT_POINT" text="aktuális útpont" /> <text name="COURSEPLAY_NEXT_POINT" text="következő útpont" /> - <text name="COURSEPLAY_UNLOAD" text="Unloading" /> + <text name="COURSEPLAY_UNLOAD" text="kombájn ürítés" /> + <text name="COURSEPLAY_COLLECT_BALES" text="bálagyűjtés" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Művelendő terület:" /> <text name="COURSEPLAY_FIELD" text="Terület" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(jelenleg betöltött útvonal)" /> diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 1bcfdbfd4..f45d22bb4 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -3,9 +3,6 @@ <texts> <text name="COURSEPLAY_RECORDING_START" text="Inizia la registrazione del percorso" /> <text name="COURSEPLAY_DRIVER" text="Autista" /> - <text name="COURSEPLAY_PPC_OFF" text="Modalità normale" /> - <text name="COURSEPLAY_PPC_ON" text="Modalità sperimentale" /> - <text name="COURSEPLAY_AIDRIVER" text="AI autista" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Avvia autista" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Ferma autista" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Fermati all'ultimo punto" /> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": l'attrezzo necessita di essere scaricato." /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": l'attrezzo deve essere rifornito." /> <text name="COURSEPLAY_UNLOADING_BALES" text="sta scaricando le balle." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="è nel traffico." /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="Rallentamento per traffico" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="ha raggiunto il punto di scarico" /> @@ -285,6 +283,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="punto corrente" /> <text name="COURSEPLAY_NEXT_POINT" text="punto successivo" /> <text name="COURSEPLAY_UNLOAD" text="Scarico" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Percorso perimetrale del" /> <text name="COURSEPLAY_FIELD" text="campo" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(percorso attualmente caricato)" /> diff --git a/translations/translation_jp.xml b/translations/translation_jp.xml index ebce25fbf..b63f8a143 100644 --- a/translations/translation_jp.xml +++ b/translations/translation_jp.xml @@ -3,9 +3,6 @@ <texts> <text name="COURSEPLAY_RECORDING_START" text="コースのレコーディング開始" /> <!-- 中華フォント使用のため表示不能の漢字有り:藁辺逆矩囲挿 同等文字:边围 --> <text name="COURSEPLAY_DRIVER" text="ドライバー" /> - <text name="COURSEPLAY_PPC_OFF" text="ノーマルモード" /> - <text name="COURSEPLAY_PPC_ON" text="実験モード" /> <!-- a new driving algorithm using a pure pursuit controller --> - <text name="COURSEPLAY_AIDRIVER" text="AIドライバー" /> <!-- enable new AI driver --> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="ドライバーを始動" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="ドライバーを停止" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Stop at last point" /> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": アンロードしてください" /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": 補給してください" /> <text name="COURSEPLAY_UNLOADING_BALES" text="ベールをアンロード" /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="トラフィックエラー" /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="slowing down for traffic" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="トリガーへ到着" /> @@ -285,6 +283,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="記憶中のウェイポイント" /> <text name="COURSEPLAY_NEXT_POINT" text="次のウェイポイント" /> <text name="COURSEPLAY_UNLOAD" text="アンロード中" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="フィールド境界線" /> <text name="COURSEPLAY_FIELD" text="フィールド" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(現在読み込まれているコース)" /> diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 213ff158d..ad6193956 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -3,9 +3,6 @@ <texts> <text name="COURSEPLAY_RECORDING_START" text="Start route vastleggen" /> <text name="COURSEPLAY_DRIVER" text="Bestuurder" /> - <text name="COURSEPLAY_PPC_OFF" text="normale modus" /> - <text name="COURSEPLAY_PPC_ON" text="experimentele modus" /> - <text name="COURSEPLAY_AIDRIVER" text="AI bestuurder" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Start bestuurder" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Stop bestuurder" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Stop at last point" /> @@ -71,6 +68,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": gereedschap legen." /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": gereedschap vullen." /> <text name="COURSEPLAY_UNLOADING_BALES" text="Balen lossen" /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="oponthoud." /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="slowing down for traffic" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="uitlaad trigger bereikt" /> @@ -281,6 +279,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="huidige markering" /> <text name="COURSEPLAY_NEXT_POINT" text="volgende markering" /> <text name="COURSEPLAY_UNLOAD" text="Unloading" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Veldrand route" /> <text name="COURSEPLAY_FIELD" text="Veld" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(huidige route)" /> diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index a52d4d944..277010571 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -3,9 +3,6 @@ <texts> <text name="COURSEPLAY_RECORDING_START" text="Rozpocznij nagrywanie kursu" /> <text name="COURSEPLAY_DRIVER" text="Kierowca" /> - <text name="COURSEPLAY_PPC_OFF" text="Tryb normalny" /> - <text name="COURSEPLAY_PPC_ON" text="Tryb eksperymentalny" /> - <text name="COURSEPLAY_AIDRIVER" text="Kierowca SI" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Wystartuj kierowcę" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Zatrzymaj kierowcę" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Zatrzymaj się w ostatnim punkcie" /> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": Narzędzie pracy musi zostać rozładowane." /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": Narzędzie pracy musi zostać załadowane." /> <text name="COURSEPLAY_UNLOADING_BALES" text="rozładunek bel." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="jest zablokowany." /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="został spowolniony" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="dotarł do punktu kiprowania" /> @@ -285,6 +283,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="Obecny punkt trasy" /> <text name="COURSEPLAY_NEXT_POINT" text="Następny punkt trasy" /> <text name="COURSEPLAY_UNLOAD" text="Rozładunek" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Ścieżka krawędzi pola:" /> <text name="COURSEPLAY_FIELD" text="Pole" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(obecnie załadowany kurs)" /> diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index 6f42157fc..c13ad2dc1 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -3,9 +3,6 @@ <texts> <text name="COURSEPLAY_RECORDING_START" text="Iniciar gravação do percurso" /> <text name="COURSEPLAY_DRIVER" text="Condutor" /> - <text name="COURSEPLAY_PPC_OFF" text="modo normal" /> - <text name="COURSEPLAY_PPC_ON" text="modo experimental" /> - <text name="COURSEPLAY_AIDRIVER" text="Motorista AI" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Conduzir" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Parar o condutor" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Parar no último ponto" /> @@ -71,6 +68,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": equipamento precisa de ser descarregado!" /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": equipamento precisa de ser carregado" /> <text name="COURSEPLAY_UNLOADING_BALES" text="a descarregar fardos" /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="trânsito congestionado" /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="trânsito...a reduzir velocidade" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="chegou ao ponto de descarga" /> @@ -281,6 +279,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="ponto atual" /> <text name="COURSEPLAY_NEXT_POINT" text="ponto seguinte" /> <text name="COURSEPLAY_UNLOAD" text="Descarregar" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Percurso da borda do campo" /> <text name="COURSEPLAY_FIELD" text="Campo" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(Percurso atual carregado)" /> diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index 1b200b29a..0b0bb83dc 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -3,9 +3,6 @@ <texts> <text name="COURSEPLAY_RECORDING_START" text="Начать запись маршрута" /> <text name="COURSEPLAY_DRIVER" text="Транспорт" /> - <text name="COURSEPLAY_PPC_OFF" text="Обычный режим" /> - <text name="COURSEPLAY_PPC_ON" text="Экспериментальный режим" /> - <text name="COURSEPLAY_AIDRIVER" text="AI driver" /> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Отправить транспорт" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Остановить транспорт" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Остановить в конечной точке:" /> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text="нуждается в разгрузке. " /> <text name="COURSEPLAY_NEEDS_REFILLING" text="нуждается в заправке. " /> <text name="COURSEPLAY_UNLOADING_BALES" text="выгрузка тюков." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="застрял." /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="замедляется из-за трафика" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="Достигнут пункт разгрузки" /> @@ -286,6 +284,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="продолжить маршрут" /> <text name="COURSEPLAY_NEXT_POINT" text="с ближайшей подходящей точки" /> <text name="COURSEPLAY_UNLOAD" text="с разгрузки" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Выбрать границу" /> <text name="COURSEPLAY_FIELD" text="поля №" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(загруженный маршрут)" /> diff --git a/translations/translation_sl.xml b/translations/translation_sl.xml index a9b2a9090..56db05325 100644 --- a/translations/translation_sl.xml +++ b/translations/translation_sl.xml @@ -3,9 +3,6 @@ <texts> <text name="COURSEPLAY_RECORDING_START" text="Začni snemanje poti" /> <text name="COURSEPLAY_DRIVER" text="Voznik" /> - <text name="COURSEPLAY_PPC_OFF" text="normal mode" /> <!-- TODO: TRANSLATE --> - <text name="COURSEPLAY_PPC_ON" text="experimental mode" /> <!-- TODO: TRANSLATE: a new driving algorithm using a pure pursuit controller --> - <text name="COURSEPLAY_AIDRIVER" text="AI driver" /> <!-- TODO: TRANSLATE: enable new AI driver --> <text name="COURSEPLAY_UNLOADING_DRIVER_START" text="Poženi voznika" /> <text name="COURSEPLAY_UNLOADING_DRIVER_STOP" text="Ustavi voznika" /> <text name="COURSEPLAY_STOP_AT_LAST_POINT" text="Stop at last point" /> @@ -75,6 +72,7 @@ <text name="COURSEPLAY_NEEDS_UNLOADING" text=": priključek je potrebno raztovoriti." /> <text name="COURSEPLAY_NEEDS_REFILLING" text=": priključek je potrebno napolniti." /> <text name="COURSEPLAY_UNLOADING_BALES" text="odlaganje bal." /> + <text name="COURSEPLAY_NO_FIELD_SELECTED" text=": no field selected" /> <text name="COURSEPLAY_IS_IN_TRAFFIC" text="ima ovire." /> <text name="COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC" text="slowing down for traffic" /> <text name="COURSEPLAY_TIPTRIGGER_REACHED" text="prišel na prožilec za odlaganje" /> @@ -285,6 +283,7 @@ <text name="COURSEPLAY_CURRENT_POINT" text="trenutna točka" /> <text name="COURSEPLAY_NEXT_POINT" text="next waypoint" /> <!-- TODO: TRANSLATE --> <text name="COURSEPLAY_UNLOAD" text="Unloading" /> + <text name="COURSEPLAY_COLLECT_BALES" text="Collect bales" /> <text name="COURSEPLAY_FIELD_EDGE_PATH" text="Pot okrog polja" /> <text name="COURSEPLAY_FIELD" text="Polje" /> <text name="COURSEPLAY_CURRENTLY_LOADED_COURSE" text="(trenutno naložena pot)" /> diff --git a/turn.lua b/turn.lua index f2ceb2d8d..b5c540807 100644 --- a/turn.lua +++ b/turn.lua @@ -99,235 +99,215 @@ function courseplay:turn(vehicle, dt, turnContext) ---------------------------------------------------------- - -- TURN STAGES 1 - Create Turn maneuver (Creating waypoints to follow) + -- Create Turn maneuver (Creating waypoints to follow) ---------------------------------------------------------- - if vehicle.cp.settings.turnStage:is(true) then - --- Cleanup in case we already have old info - courseplay:clearTurnTargets(vehicle); -- Make sure we have cleaned it from any previus usage. - - --- Setting default turnInfo values - local turnInfo = {}; - turnInfo.directionNode = realDirectionNode - turnInfo.frontMarker = frontMarker; - turnInfo.backMarker = backMarker; - turnInfo.halfVehicleWidth = 2.5; - turnInfo.directionNodeToTurnNodeLength = directionNodeToTurnNodeLength + 0.5; -- 0.5 is to make the start turn point just a tiny in front of the tractor - -- when PPC is driving we don't have to care about wp change distances, PPC takes care of that. Still use - -- a small value to make sure none of the turn generator functions end up with overlapping waypoints - turnInfo.wpChangeDistance = 0.5 - turnInfo.reverseWPChangeDistance = 0.5 - turnInfo.direction = -1; - turnInfo.haveHeadlands = courseplay:haveHeadlands(vehicle); - - -- find out the headland height to figure out if we have enough room on the headland to make turns - if vehicle.cp.courseWorkWidth and vehicle.cp.courseWorkWidth > 0 and vehicle.cp.courseNumHeadlandLanes and vehicle.cp.courseNumHeadlandLanes > 0 then - -- First headland is only half the work width - turnInfo.headlandHeight = vehicle.cp.courseWorkWidth / 2 + ((vehicle.cp.courseNumHeadlandLanes - 1) * vehicle.cp.courseWorkWidth) - else - turnInfo.headlandHeight = 0 - end + --- Cleanup in case we already have old info + courseplay:clearTurnTargets(vehicle); -- Make sure we have cleaned it from any previus usage. + + --- Setting default turnInfo values + local turnInfo = {}; + turnInfo.directionNode = realDirectionNode + turnInfo.frontMarker = frontMarker; + turnInfo.backMarker = backMarker; + turnInfo.halfVehicleWidth = 2.5; + turnInfo.directionNodeToTurnNodeLength = directionNodeToTurnNodeLength + 0.5; -- 0.5 is to make the start turn point just a tiny in front of the tractor + -- when PPC is driving we don't have to care about wp change distances, PPC takes care of that. Still use + -- a small value to make sure none of the turn generator functions end up with overlapping waypoints + turnInfo.wpChangeDistance = 0.5 + turnInfo.reverseWPChangeDistance = 0.5 + turnInfo.direction = -1; + turnInfo.haveHeadlands = courseplay:haveHeadlands(vehicle); + + -- find out the headland height to figure out if we have enough room on the headland to make turns + if vehicle.cp.courseWorkWidth and vehicle.cp.courseWorkWidth > 0 and vehicle.cp.courseNumHeadlandLanes and vehicle.cp.courseNumHeadlandLanes > 0 then + -- First headland is only half the work width + turnInfo.headlandHeight = vehicle.cp.courseWorkWidth / 2 + ((vehicle.cp.courseNumHeadlandLanes - 1) * vehicle.cp.courseWorkWidth) + else + turnInfo.headlandHeight = 0 + end - -- if the headland is not perpendicular, we have less room to turn - turnInfo.headlandHeight = turnInfo.headlandHeight * math.cos(turnContext:getHeadlandAngle()) - - -- Headland height in the waypoint overrides the generic headland height calculation. This is for the - -- short edge headlands where we make 180 turns on te headland course. The generic calculation would use - -- the number of headlands and think there is room on the headland to make the turn. - -- Therefore, the course generator will add a headlandHeightForTurn = 0 for these turn waypoints to make - -- sure on field turns are calculated correctly. - turnInfo.headlandHeight = turnContext.turnStartWp.headlandHeightForTurn and - turnContext.turnStartWp.headlandHeightForTurn or turnInfo.headlandHeight; - - turnInfo.numLanes ,turnInfo.onLaneNum = courseplay:getLaneInfo(vehicle); - turnInfo.turnOnField = vehicle.cp.settings.turnOnField:is(true); - turnInfo.reverseOffset = 0; - turnInfo.extraAlignLength = 6; - turnInfo.haveWheeledImplement = reversingWorkTool ~= nil; - if turnInfo.haveWheeledImplement then - turnInfo.reversingWorkTool = reversingWorkTool; - turnInfo.extraAlignLength = turnInfo.extraAlignLength + directionNodeToTurnNodeLength * 2; - end; - turnInfo.isHarvester = isHarvester; - turnInfo.noReverse = g_vehicleConfigurations:getRecursively(vehicle, 'noReverse') - - -- headland turn data - vehicle.cp.headlandTurn = turnContext:isHeadlandCorner() and {} or nil - -- direction halfway between dir of turnStart and turnEnd - turnInfo.halfAngle = math.deg( getAverageAngle( math.rad( turnContext.turnEndWp.angle ), - math.rad( turnContext.turnStartWp.angle ))) - -- delta between turn start and turn end - turnInfo.deltaAngle = math.pi - ( math.rad( turnContext.turnEndWp.angle ) - - math.rad( turnContext.turnStartWp.angle )) - - turnInfo.startDirection = turnContext.turnStartWp.angle - - --- Get the turn radius either by the automatic or user provided turn circle. - local extRadius = 0.5 + (0.15 * directionNodeToTurnNodeLength); -- The extra calculation is for dynamic trailer length to prevent jackknifing; - turnInfo.turnRadius = vehicle.cp.turnDiameter * 0.5 + extRadius; - turnInfo.turnDiameter = turnInfo.turnRadius * 2; - - - --- Create temp target node and translate it. - turnInfo.targetNode = turnContext.turnEndWpNode.node - local cx,cz = turnContext.turnEndWp.x, turnContext.turnEndWp.z - - --- Debug Print - if courseplay.debugChannels[14] then - local x,y,z = getWorldTranslation(turnInfo.targetNode); - local ctx,_,ctz = localToWorld(turnInfo.targetNode, 0, 0, 20); - --drawDebugLine(x, y+5, z, 1, 0, 0, ctx, y+5, ctz, 0, 1, 0); - cpDebug:drawLine(x, y+5, z, 1, 0, 0, ctx, y+5, ctz); - -- this is an test - courseplay:debug(("%s:(Turn) wp%d=%.1f°, wp%d=%.1f°, directionChangeDeg = %.1f° halfAngle = %.1f"):format(nameNum(vehicle), - turnContext.beforeTurnStartWp.cpIndex, turnContext.beforeTurnStartWp.angle, turnContext.turnEndWp.cpIndex, turnContext.turnEndWp.angle, turnContext.directionChangeDeg, turnInfo.halfAngle), 14); - end; + -- if the headland is not perpendicular, we have less room to turn + turnInfo.headlandHeight = turnInfo.headlandHeight * math.cos(turnContext:getHeadlandAngle()) + + -- Headland height in the waypoint overrides the generic headland height calculation. This is for the + -- short edge headlands where we make 180 turns on te headland course. The generic calculation would use + -- the number of headlands and think there is room on the headland to make the turn. + -- Therefore, the course generator will add a headlandHeightForTurn = 0 for these turn waypoints to make + -- sure on field turns are calculated correctly. + turnInfo.headlandHeight = turnContext.turnStartWp.headlandHeightForTurn and + turnContext.turnStartWp.headlandHeightForTurn or turnInfo.headlandHeight; + + turnInfo.numLanes ,turnInfo.onLaneNum = courseplay:getLaneInfo(vehicle); + turnInfo.turnOnField = vehicle.cp.settings.turnOnField:is(true); + turnInfo.reverseOffset = 0; + turnInfo.extraAlignLength = 6; + turnInfo.haveWheeledImplement = reversingWorkTool ~= nil; + if turnInfo.haveWheeledImplement then + turnInfo.reversingWorkTool = reversingWorkTool; + turnInfo.extraAlignLength = turnInfo.extraAlignLength + directionNodeToTurnNodeLength * 2; + end; + turnInfo.isHarvester = isHarvester; + turnInfo.noReverse = g_vehicleConfigurations:getRecursively(vehicle, 'noReverse') + + -- headland turn data + vehicle.cp.headlandTurn = turnContext:isHeadlandCorner() and {} or nil + -- direction halfway between dir of turnStart and turnEnd + turnInfo.halfAngle = math.deg( getAverageAngle( math.rad( turnContext.turnEndWp.angle ), + math.rad( turnContext.turnStartWp.angle ))) + -- delta between turn start and turn end + turnInfo.deltaAngle = math.pi - ( math.rad( turnContext.turnEndWp.angle ) + - math.rad( turnContext.turnStartWp.angle )) - --- Get the local delta distances from the tractor to the targetNode - turnInfo.targetDeltaX, _, turnInfo.targetDeltaZ = worldToLocal(turnInfo.directionNode, cx, vehicleY, cz); - courseplay:debug(string.format("%s:(Turn) targetDeltaX=%.2f, targetDeltaZ=%.2f", nameNum(vehicle), turnInfo.targetDeltaX, turnInfo.targetDeltaZ), 14); + turnInfo.startDirection = turnContext.turnStartWp.angle - --- Get the turn direction - if turnContext:isHeadlandCorner() then - -- headland corner turns have a targetDeltaX around 0 so use the direction diff - if turnContext.directionChangeDeg > 0 then - turnInfo.direction = 1; - end - else - if turnInfo.targetDeltaX > 0 then - turnInfo.direction = 1; - end; - end + --- Get the turn radius either by the automatic or user provided turn circle. + local extRadius = 0.5 + (0.15 * directionNodeToTurnNodeLength); -- The extra calculation is for dynamic trailer length to prevent jackknifing; + turnInfo.turnRadius = vehicle.cp.turnDiameter * 0.5 + extRadius; + turnInfo.turnDiameter = turnInfo.turnRadius * 2; - -- Relative position of the turn start waypoint from the vehicle. - -- Note that as we start the turn when the backMarkerOffset reaches the turn start point, this zOffset - -- is the same as the backMarkerOffset - _, _, turnInfo.zOffset = worldToLocal(turnInfo.directionNode, turnContext.turnStartWp.x, vehicleY, turnContext.turnStartWp.z); - -- remember this as we'll need it later - turnInfo.deltaZBetweenVehicleAndTarget = turnInfo.targetDeltaZ - -- targetDeltaZ is now the delta Z between the turn start and turn end waypoints. - turnInfo.targetDeltaZ = turnInfo.targetDeltaZ - turnInfo.zOffset; - - -- Calculate reverseOffset in case we need to reverse. - -- This is used in both wide turns and in the question mark turn - local offset = turnInfo.zOffset; - -- only if all implements are in the front - if turnInfo.frontMarker > 0 and turnInfo.backMarker > 0 then - offset = -turnInfo.zOffset - turnInfo.frontMarker; - end; - if turnInfo.turnOnField and not turnInfo.isHarvester and not turnInfo.noReverse then - turnInfo.reverseOffset = max((turnInfo.turnRadius + turnInfo.halfVehicleWidth - turnInfo.headlandHeight), offset); - elseif turnInfo.isHarvester and turnInfo.frontMarker > 0 then - -- without fully understanding this reverseOffset, correct it for combines so they don't make - -- unnecessarily wide turns (and hit trees outside the field) - turnInfo.reverseOffset = -turnInfo.frontMarker - else - -- the weird thing about this is that reverseOffset here equals to zOffset and this is why - -- the wide turn works at all, even if there's no reversing. - turnInfo.reverseOffset = offset; + + --- Create temp target node and translate it. + turnInfo.targetNode = turnContext.turnEndWpNode.node + local cx,cz = turnContext.turnEndWp.x, turnContext.turnEndWp.z + + --- Debug Print + if courseplay.debugChannels[14] then + local x,y,z = getWorldTranslation(turnInfo.targetNode); + local ctx,_,ctz = localToWorld(turnInfo.targetNode, 0, 0, 20); + --drawDebugLine(x, y+5, z, 1, 0, 0, ctx, y+5, ctz, 0, 1, 0); + cpDebug:drawLine(x, y+5, z, 1, 0, 0, ctx, y+5, ctz); + -- this is an test + courseplay:debug(("%s:(Turn) wp%d=%.1f°, wp%d=%.1f°, directionChangeDeg = %.1f° halfAngle = %.1f"):format(nameNum(vehicle), + turnContext.beforeTurnStartWp.cpIndex, turnContext.beforeTurnStartWp.angle, turnContext.turnEndWp.cpIndex, turnContext.turnEndWp.angle, turnContext.directionChangeDeg, turnInfo.halfAngle), 14); + end; + + --- Get the local delta distances from the tractor to the targetNode + turnInfo.targetDeltaX, _, turnInfo.targetDeltaZ = worldToLocal(turnInfo.directionNode, cx, vehicleY, cz); + courseplay:debug(string.format("%s:(Turn) targetDeltaX=%.2f, targetDeltaZ=%.2f", nameNum(vehicle), turnInfo.targetDeltaX, turnInfo.targetDeltaZ), 14); + + --- Get the turn direction + if turnContext:isHeadlandCorner() then + -- headland corner turns have a targetDeltaX around 0 so use the direction diff + if turnContext.directionChangeDeg > 0 then + turnInfo.direction = 1; + end + else + if turnInfo.targetDeltaX > 0 then + turnInfo.direction = 1; end; + end - courseplay:debug(("%s:(Turn Data) frontMarker=%q, backMarker=%q, halfVehicleWidth=%q, directionNodeToTurnNodeLength=%q, wpChangeDistance=%q"):format(nameNum(vehicle), tostring(turnInfo.frontMarker), tostring(backMarker), tostring(turnInfo.halfVehicleWidth), tostring(turnInfo.directionNodeToTurnNodeLength), tostring(turnInfo.wpChangeDistance)), 14); - courseplay:debug(("%s:(Turn Data) reverseWPChangeDistance=%q, direction=%q, haveHeadlands=%q, headlandHeight=%q"):format(nameNum(vehicle), tostring(turnInfo.reverseWPChangeDistance), tostring(turnInfo.direction), tostring(turnInfo.haveHeadlands), tostring(turnInfo.headlandHeight)), 14); - courseplay:debug(("%s:(Turn Data) numLanes=%q, onLaneNum=%q, turnOnField=%q, reverseOffset=%q"):format(nameNum(vehicle), tostring(turnInfo.numLanes), tostring(turnInfo.onLaneNum), tostring(turnInfo.turnOnField), tostring(turnInfo.reverseOffset)), 14); - courseplay:debug(("%s:(Turn Data) haveWheeledImplement=%q, reversingWorkTool=%q, turnRadius=%q, turnDiameter=%q"):format(nameNum(vehicle), tostring(turnInfo.haveWheeledImplement), tostring(turnInfo.reversingWorkTool), tostring(turnInfo.turnRadius), tostring(turnInfo.turnDiameter)), 14); - courseplay:debug(("%s:(Turn Data) targetNode=%q, targetDeltaX=%q, targetDeltaZ=%q, zOffset=%q"):format(nameNum(vehicle), tostring(turnInfo.targetNode), tostring(turnInfo.targetDeltaX), tostring(turnInfo.targetDeltaZ), tostring(turnInfo.zOffset)), 14); - courseplay:debug(("%s:(Turn Data) reverseOffset=%q, isHarvester=%q, noReverse=%q"):format(nameNum(vehicle), tostring(turnInfo.reverseOffset), tostring(turnInfo.isHarvester), tostring(turnInfo.noReverse)), 14); + -- Relative position of the turn start waypoint from the vehicle. + -- Note that as we start the turn when the backMarkerOffset reaches the turn start point, this zOffset + -- is the same as the backMarkerOffset + _, _, turnInfo.zOffset = worldToLocal(turnInfo.directionNode, turnContext.turnStartWp.x, vehicleY, turnContext.turnStartWp.z); + -- remember this as we'll need it later + turnInfo.deltaZBetweenVehicleAndTarget = turnInfo.targetDeltaZ + -- targetDeltaZ is now the delta Z between the turn start and turn end waypoints. + turnInfo.targetDeltaZ = turnInfo.targetDeltaZ - turnInfo.zOffset; + + -- Calculate reverseOffset in case we need to reverse. + -- This is used in both wide turns and in the question mark turn + local offset = turnInfo.zOffset; + -- only if all implements are in the front + if turnInfo.frontMarker > 0 and turnInfo.backMarker > 0 then + offset = -turnInfo.zOffset - turnInfo.frontMarker; + end; + if turnInfo.turnOnField and not turnInfo.isHarvester and not turnInfo.noReverse then + turnInfo.reverseOffset = max((turnInfo.turnRadius + turnInfo.halfVehicleWidth - turnInfo.headlandHeight), offset); + elseif turnInfo.isHarvester and turnInfo.frontMarker > 0 then + -- without fully understanding this reverseOffset, correct it for combines so they don't make + -- unnecessarily wide turns (and hit trees outside the field) + turnInfo.reverseOffset = -turnInfo.frontMarker + else + -- the weird thing about this is that reverseOffset here equals to zOffset and this is why + -- the wide turn works at all, even if there's no reversing. + turnInfo.reverseOffset = offset; + end; + courseplay:debug(("%s:(Turn Data) frontMarker=%q, backMarker=%q, halfVehicleWidth=%q, directionNodeToTurnNodeLength=%q, wpChangeDistance=%q"):format(nameNum(vehicle), tostring(turnInfo.frontMarker), tostring(backMarker), tostring(turnInfo.halfVehicleWidth), tostring(turnInfo.directionNodeToTurnNodeLength), tostring(turnInfo.wpChangeDistance)), 14); + courseplay:debug(("%s:(Turn Data) reverseWPChangeDistance=%q, direction=%q, haveHeadlands=%q, headlandHeight=%q"):format(nameNum(vehicle), tostring(turnInfo.reverseWPChangeDistance), tostring(turnInfo.direction), tostring(turnInfo.haveHeadlands), tostring(turnInfo.headlandHeight)), 14); + courseplay:debug(("%s:(Turn Data) numLanes=%q, onLaneNum=%q, turnOnField=%q, reverseOffset=%q"):format(nameNum(vehicle), tostring(turnInfo.numLanes), tostring(turnInfo.onLaneNum), tostring(turnInfo.turnOnField), tostring(turnInfo.reverseOffset)), 14); + courseplay:debug(("%s:(Turn Data) haveWheeledImplement=%q, reversingWorkTool=%q, turnRadius=%q, turnDiameter=%q"):format(nameNum(vehicle), tostring(turnInfo.haveWheeledImplement), tostring(turnInfo.reversingWorkTool), tostring(turnInfo.turnRadius), tostring(turnInfo.turnDiameter)), 14); + courseplay:debug(("%s:(Turn Data) targetNode=%q, targetDeltaX=%q, targetDeltaZ=%q, zOffset=%q"):format(nameNum(vehicle), tostring(turnInfo.targetNode), tostring(turnInfo.targetDeltaX), tostring(turnInfo.targetDeltaZ), tostring(turnInfo.zOffset)), 14); + courseplay:debug(("%s:(Turn Data) reverseOffset=%q, isHarvester=%q, noReverse=%q"):format(nameNum(vehicle), tostring(turnInfo.reverseOffset), tostring(turnInfo.isHarvester), tostring(turnInfo.noReverse)), 14); - if not turnContext:isHeadlandCorner() then - ---------------------------------------------------------- - -- SWITCH TO THE NEXT LANE - ---------------------------------------------------------- - courseplay:debug(string.format("%s:(Turn) Direction difference is %.1f, this is a lane switch.", nameNum(vehicle), turnContext.directionChangeDeg), 14); - ---------------------------------------------------------- - -- WIDE TURNS (Turns where the distance to next lane is bigger than the turning Diameter) - ---------------------------------------------------------- - if abs(turnInfo.targetDeltaX) >= turnInfo.turnDiameter then - if abs(turnInfo.targetDeltaX) >= (turnInfo.turnDiameter * 2) and abs(turnInfo.targetDeltaZ) >= (turnInfo.turnRadius * 3) then - courseplay:generateTurnTypeWideTurnWithAvoidance(vehicle, turnInfo); - else - courseplay:generateTurnTypeWideTurn(vehicle, turnInfo); - end - ---------------------------------------------------------- - -- NAROW TURNS (Turns where the distance to next lane is smaller than the turning Diameter) - ---------------------------------------------------------- + if not turnContext:isHeadlandCorner() then + ---------------------------------------------------------- + -- SWITCH TO THE NEXT LANE + ---------------------------------------------------------- + courseplay:debug(string.format("%s:(Turn) Direction difference is %.1f, this is a lane switch.", nameNum(vehicle), turnContext.directionChangeDeg), 14); + ---------------------------------------------------------- + -- WIDE TURNS (Turns where the distance to next lane is bigger than the turning Diameter) + ---------------------------------------------------------- + if abs(turnInfo.targetDeltaX) >= turnInfo.turnDiameter then + if abs(turnInfo.targetDeltaX) >= (turnInfo.turnDiameter * 2) and abs(turnInfo.targetDeltaZ) >= (turnInfo.turnRadius * 3) then + courseplay:generateTurnTypeWideTurnWithAvoidance(vehicle, turnInfo); else - --- If we have wheeled implement, then do turns based on that. - if turnInfo.haveWheeledImplement then - --- Get the Triangle sides - local centerOffset = abs(turnInfo.targetDeltaX) / 2; - local sideC = turnInfo.turnDiameter; - local sideB = centerOffset + turnInfo.turnRadius; - local centerHeight = square(sideC^2 - sideB^2); - - --- Check if there is enough space to make Ohm turn on the headland. - local useOhmTurn = false; - if (-turnInfo.zOffset + centerHeight + turnInfo.turnRadius + turnInfo.halfVehicleWidth) < turnInfo.headlandHeight then - useOhmTurn = true; - end; + courseplay:generateTurnTypeWideTurn(vehicle, turnInfo); + end - --- Ohm Turn - if useOhmTurn or turnInfo.isHarvester or turnInfo.noReverse or not turnInfo.turnOnField then - courseplay:generateTurnTypeOhmTurn(vehicle, turnInfo); - else - --- Questionmark Turn - courseplay:generateTurnTypeQuestionmarkTurn(vehicle, turnInfo); - end; + ---------------------------------------------------------- + -- NARROW TURNS (Turns where the distance to next lane is smaller than the turning Diameter) + ---------------------------------------------------------- + else + --- If we have wheeled implement, then do turns based on that. + if turnInfo.haveWheeledImplement then + --- Get the Triangle sides + local centerOffset = abs(turnInfo.targetDeltaX) / 2; + local sideC = turnInfo.turnDiameter; + local sideB = centerOffset + turnInfo.turnRadius; + local centerHeight = square(sideC^2 - sideB^2); + + --- Check if there is enough space to make Ohm turn on the headland. + local useOhmTurn = false; + if (-turnInfo.zOffset + centerHeight + turnInfo.turnRadius + turnInfo.halfVehicleWidth) < turnInfo.headlandHeight then + useOhmTurn = true; + end; - --- If not wheeled implement, then do the short turns. + --- Ohm Turn + if useOhmTurn or turnInfo.isHarvester or turnInfo.noReverse or not turnInfo.turnOnField then + courseplay:generateTurnTypeOhmTurn(vehicle, turnInfo); else - --- Get the Triangle sides - turnInfo.centerOffset = (turnInfo.targetDeltaX * turnInfo.direction) - turnInfo.turnRadius; - local sideC = turnInfo.turnDiameter; - local sideB = turnInfo.turnRadius + turnInfo.centerOffset; -- which is exactly targetDeltaX, see above - turnInfo.centerHeight = square(sideC^2 - sideB^2); - - local neededSpace = abs(turnInfo.targetDeltaZ) + turnInfo.zOffset + 1 + turnInfo.centerHeight + (turnInfo.reverseWPChangeDistance * 1.5); - --- Forward 3 Point Turn - if neededSpace < turnInfo.headlandHeight or turnInfo.isHarvester or not turnInfo.turnOnField then - courseplay:generateTurnTypeForward3PointTurn(vehicle, turnInfo); - - --- Reverse 3 Point Turn - else - courseplay:generateTurnTypeReverse3PointTurn(vehicle, turnInfo); - end; + --- Questionmark Turn + courseplay:generateTurnTypeQuestionmarkTurn(vehicle, turnInfo); end; - end - else - ------------------------------------------------------------- - -- A SHARP TURN, LIKELY ON THE HEADLAND BUT NOT A LANE SWITCH - ------------------------------------------------------------- - courseplay:debug(string.format("%s:(Turn) Direction difference is %.1f, this is a corner, maneuver type = %d.", - nameNum(vehicle), turnContext.directionChangeDeg, vehicle.cp.headland.reverseManeuverType), 14); - vehicle.cp.turnCorner = turnContext:createCorner(vehicle, turnInfo.turnRadius) - - courseplay.generateTurnTypeHeadlandCornerReverseStraightTractor(vehicle, turnInfo) + --- If not wheeled implement, then do the short turns. + else + --- Get the Triangle sides + turnInfo.centerOffset = (turnInfo.targetDeltaX * turnInfo.direction) - turnInfo.turnRadius; + local sideC = turnInfo.turnDiameter; + local sideB = turnInfo.turnRadius + turnInfo.centerOffset; -- which is exactly targetDeltaX, see above + turnInfo.centerHeight = square(sideC^2 - sideB^2); + + local neededSpace = abs(turnInfo.targetDeltaZ) + turnInfo.zOffset + 1 + turnInfo.centerHeight + (turnInfo.reverseWPChangeDistance * 1.5); + --- Forward 3 Point Turn + if neededSpace < turnInfo.headlandHeight or turnInfo.isHarvester or not turnInfo.turnOnField then + courseplay:generateTurnTypeForward3PointTurn(vehicle, turnInfo); + + --- Reverse 3 Point Turn + else + courseplay:generateTurnTypeReverse3PointTurn(vehicle, turnInfo); + end; + end; end + else + ------------------------------------------------------------- + -- A SHARP TURN, LIKELY ON THE HEADLAND BUT NOT A LANE SWITCH + ------------------------------------------------------------- + courseplay:debug(string.format("%s:(Turn) Direction difference is %.1f, this is a corner, maneuver type = %d.", + nameNum(vehicle), turnContext.directionChangeDeg, vehicle.cp.headland.reverseManeuverType), 14); - cpPrintLine(14, 1); - courseplay:debug(string.format("%s:(Turn) Generated %d Turn Waypoints", nameNum(vehicle), #vehicle.cp.turnTargets), 14); - cpPrintLine(14, 3); + vehicle.cp.turnCorner = turnContext:createCorner(vehicle, turnInfo.turnRadius) + courseplay.generateTurnTypeHeadlandCornerReverseStraightTractor(vehicle, turnInfo) end - ---------------------------------------------------------- - --Set the driving direction - ---------------------------------------------------------- - if curTurnTarget then - local posX, posZ = curTurnTarget.revPosX or curTurnTarget.posX, curTurnTarget.revPosZ or curTurnTarget.posZ; - local directionNode = vehicle.aiVehicleDirectionNode or vehicle.cp.directionNode; - dtpX,_,dtpZ = worldToLocal(directionNode, posX, vehicleY, posZ); - if courseplay:isWheelloader(vehicle) then - dtpZ = dtpZ * 0.5; -- wheel loaders need to turn more - end; - - lx, lz = AIVehicleUtil.getDriveDirection(vehicle.cp.directionNode, posX, vehicleY, posZ); - if curTurnTarget.turnReverse then - lx, lz, moveForwards = courseplay:goReverse(vehicle,lx,lz); - end; - end; + cpPrintLine(14, 1); + courseplay:debug(string.format("%s:(Turn) Generated %d Turn Waypoints", nameNum(vehicle), #vehicle.cp.turnTargets), 14); + cpPrintLine(14, 3); end function courseplay:generateTurnTypeWideTurn(vehicle, turnInfo) @@ -1495,7 +1475,6 @@ function courseplay:addTurnTarget(vehicle, posX, posZ, turnEnd, turnReverse, rev end function courseplay:clearTurnTargets(vehicle) - vehicle.cp.settings.turnStage:set(false) vehicle.cp.turnTargets = {}; vehicle.cp.curTurnIndex = 1; vehicle.cp.haveCheckedMarkersThisTurn = false; diff --git a/vehicles.lua b/vehicles.lua index 64688f811..4ac25c9cd 100644 --- a/vehicles.lua +++ b/vehicles.lua @@ -1656,11 +1656,14 @@ function AIDriverUtil.getLastAttachedImplement(vehicle) return lastImplement, minDistance end - function AIDriverUtil.hasAIImplementWithSpecialization(vehicle, specialization) return AIDriverUtil.getAIImplementWithSpecialization(vehicle, specialization) ~= nil end +function AIDriverUtil.hasImplementWithSpecialization(vehicle, specialization) + return AIDriverUtil.getImplementWithSpecialization(vehicle, specialization) ~= nil +end + function AIDriverUtil.getAIImplementWithSpecialization(vehicle, specialization) local aiImplements = vehicle:getAttachedAIImplements() return AIDriverUtil.getImplementWithSpecializationFromList(specialization, aiImplements)