Skip to content
This repository has been archived by the owner on May 23, 2023. It is now read-only.

Commit

Permalink
Merge pull request #7188 from Courseplay/issue-7102
Browse files Browse the repository at this point in the history
Mode2 improvements
  • Loading branch information
Tensuko authored May 12, 2021
2 parents cd4b802 + 525e2cd commit 3c6a826
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 42 deletions.
40 changes: 33 additions & 7 deletions CombineAIDriver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,22 @@ function CombineAIDriver:createPullBackCourse()
end
end

--- Get the area the unloader should avoid when approaching the combine.
--- Main (and for now, only) use case is to prevent the unloader to cross in front of the combine after the
--- combine pulled back full with pipe in the fruit, making room for the unloader on its left side.
--- @return table, number, number, number, number node, xOffset, zOffset, width, length : the area to avoid is
--- a length x width m rectangle, the rectangle's bottom right corner (when looking from node) is at xOffset/zOffset
--- from node.
function CombineAIDriver:getAreaToAvoid()
if self:isWaitingForUnloadAfterPulledBack() then
local xOffset = self.vehicle.cp.workWidth / 2
local zOffset = 0
local length = self.pullBackDistanceEnd
local width = self.pullBackRightSideOffset
return PathfinderUtil.Area(AIDriverUtil.getDirectionNode(self.vehicle), xOffset, zOffset, width, length)
end
end

function CombineAIDriver:createPullBackReturnCourse()
-- nothing fancy here either, just move forward a few meters before returning to the fieldwork course
local referenceNode = AIDriverUtil.getDirectionNode(self.vehicle)
Expand Down Expand Up @@ -1334,7 +1350,7 @@ function CombineAIDriver:startSelfUnload()
self.pathfinder, done, path = PathfinderUtil.startPathfindingFromVehicleToNode(
self.vehicle, targetNode, offsetX, offsetZ,
self:getAllowReversePathfinding(),
fieldNum, {}, nil, nil, true)
fieldNum, {}, nil, nil, nil, true)
if done then
return self:onPathfindingDone(path)
else
Expand Down Expand Up @@ -1661,15 +1677,25 @@ end

function CombineAIDriver:onDraw()

if not courseplay.debugChannels[courseplay.DBG_IMPLEMENTS] then return end
if courseplay.debugChannels[courseplay.DBG_IMPLEMENTS] then

local dischargeNode = self:getCurrentDischargeNode()
if dischargeNode then
DebugUtil.drawDebugNode(dischargeNode.node, 'discharge')
local dischargeNode = self:getCurrentDischargeNode()
if dischargeNode then
DebugUtil.drawDebugNode(dischargeNode.node, 'discharge')
end

if self.aiDriverData.backMarkerNode then
DebugUtil.drawDebugNode(self.aiDriverData.backMarkerNode, 'back marker')
end
end

if self.aiDriverData.backMarkerNode then
DebugUtil.drawDebugNode(self.aiDriverData.backMarkerNode, 'back marker')
if courseplay.debugChannels[courseplay.DBG_PATHFINDER] then
local areaToAvoid = self:getAreaToAvoid()
if areaToAvoid then
local x, y, z = localToWorld(areaToAvoid.node, areaToAvoid.xOffset, 0, areaToAvoid.zOffset)
cpDebug:drawLine(x, y + 1.2, z, 10, 10, 10, x, y + 1.2, z + areaToAvoid.length)
cpDebug:drawLine(x + areaToAvoid.width, y + 1.2, z, 10, 10, 10, x + areaToAvoid.width, y + 1.2, z + areaToAvoid.length)
end
end

UnloadableFieldworkAIDriver.onDraw(self)
Expand Down
12 changes: 7 additions & 5 deletions CombineUnloadAIDriver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1586,16 +1586,16 @@ function CombineUnloadAIDriver:startPathfinding(
-- the Giants coordinate system and the waypoint uses the course's conventions. This is confusing, should use
-- the same reference everywhere
self.pathfinder, done, path, goalNodeInvalid = PathfinderUtil.startPathfindingFromVehicleToWaypoint(
self.vehicle, target, -xOffset or 0, zOffset or 0, self.allowReversePathfinding,
self.vehicle, target, -xOffset or 0, zOffset or 0, false,
fieldNum, vehiclesToIgnore,
self.vehicle.cp.settings.useRealisticDriving:is(true) and self.maxFruitPercent or math.huge,
self.offFieldPenalty)
self.offFieldPenalty, self.combineToUnload.cp.driver:getAreaToAvoid())
else
self.pathfinder, done, path, goalNodeInvalid = PathfinderUtil.startPathfindingFromVehicleToNode(
self.vehicle, target, xOffset or 0, zOffset or 0, self.allowReversePathfinding,
self.vehicle, target, xOffset or 0, zOffset or 0, false,
fieldNum, vehiclesToIgnore,
self.vehicle.cp.settings.useRealisticDriving:is(true) and self.maxFruitPercent or math.huge,
self.offFieldPenalty)
self.offFieldPenalty, self.combineToUnload.cp.driver:getAreaToAvoid())
end
if done then
return pathfindingCallbackFunc(self, path, goalNodeInvalid)
Expand Down Expand Up @@ -1687,7 +1687,9 @@ function CombineUnloadAIDriver:checkForCombineProximity()
-- do not swerve for our combine towards the end of the course,
-- otherwise we won't be able to align with it when coming from
-- the wrong angle
if self.course:getDistanceToLastWaypoint(self.course:getCurrentWaypointIx()) < 20 then
-- Increased distance from 20 to 75, so we don't swerve for our combine
-- when we are coming from the front and drive to close to our combine
if self.course:getDistanceToLastWaypoint(self.course:getCurrentWaypointIx()) < 75 then
if not self.doNotSwerveForVehicle:get() then
self:debug('Disable swerve for %s', nameNum(self.combineToUnload))
end
Expand Down
16 changes: 6 additions & 10 deletions base.lua
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ function courseplay:onLoad(savegame)

local directionNodeOffset, isTruck = courseplay:getVehicleDirectionNodeOffset(self, DirectionNode);
if directionNodeOffset ~= 0 then
self.cp.oldDirectionNode = DirectionNode; -- Only used for debugging.
DirectionNode = courseplay:createNewLinkedNode(self, "realDirectionNode", DirectionNode);
setTranslation(DirectionNode, 0, 0, directionNodeOffset);
end;
Expand Down Expand Up @@ -457,17 +456,14 @@ function courseplay:onDraw()
--DEBUG SHOW DIRECTIONNODE
if courseplay.debugChannels[courseplay.DBG_PPC] then
-- For debugging when setting the directionNodeZOffset. (Visual points shown for old node)
if self.cp.oldDirectionNode then
local ox,oy,oz = getWorldTranslation(self.cp.oldDirectionNode);
cpDebug:drawPoint(ox, oy+4, oz, 0.9098, 0.6902 , 0.2706);
end;
if self.cp.driver then
self.cp.driver:onDraw()
end
local nx,ny,nz = getWorldTranslation(self.cp.directionNode);
cpDebug:drawPoint(nx, ny+4, nz, 0.6196, 0.3490 , 0);
end;

end;

if self.cp.driver then
self.cp.driver:onDraw()
end

if self:getIsActive() then
if self.cp.hud.show then
courseplay.hud:setContent(self);
Expand Down
83 changes: 65 additions & 18 deletions course-generator/PathfinderUtil.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ PathfinderUtil.dubinsSolver = DubinsSolver()
PathfinderUtil.reedSheppSolver = ReedsSheppSolver()

PathfinderUtil.defaultOffFieldPenalty = 7.5
PathfinderUtil.defaultAreaToAvoidPenalty = 2000
PathfinderUtil.visualDebugLevel = 0

------------------------------------------------------------------------------------------------------------------------
---Size/turn radius all other information on the vehicle and its implements
------------------------------------------------------------------------------------------------------------------------
---@class PathfinderUtil.VehicleData
PathfinderUtil.VehicleData = CpObject()

Expand Down Expand Up @@ -59,8 +62,8 @@ function PathfinderUtil.VehicleData:init(vehicle, withImplements, buffer)
rootVehicle = self.trailer:getRootVehicle(),
dFront = buffer or 0,
dRear = - self.trailer.sizeLength - (buffer or 0),
dLeft = self.trailer.sizeWidth / 2,
dRight = -self.trailer.sizeWidth / 2
dLeft = self.trailer.sizeWidth / 2 + (buffer or 0),
dRight = -self.trailer.sizeWidth / 2 - (buffer or 0)
}
local inputAttacherJoint = self.trailer:getActiveInputAttacherJoint()
if inputAttacherJoint then
Expand Down Expand Up @@ -149,6 +152,9 @@ function PathfinderUtil.VehicleData:calculateSizeOfObjectList(vehicle, implement
-- self.dFront, self.dRear, self.dLeft, self.dRight)
end

------------------------------------------------------------------------------------------------------------------------
-- Helpers
------------------------------------------------------------------------------------------------------------------------
--- Is this position on a field (any field)?
function PathfinderUtil.isPosOnField(x, y, z)
if not y then
Expand Down Expand Up @@ -200,7 +206,9 @@ function PathfinderUtil.isWorldPositionOwned(posX, posZ)
return (farmland and farmland.isOwned) or missionAllowed
end

------------------------------------------------------------------------------------------------------------------------
--- Pathfinder context
------------------------------------------------------------------------------------------------------------------------
---@class PathfinderUtil.Context
PathfinderUtil.Context = CpObject()
function PathfinderUtil.Context:init(vehicle, vehiclesToIgnore, objectsToIgnore)
Expand Down Expand Up @@ -266,7 +274,9 @@ function PathfinderUtil.getNormalWorldRotation(x, z)
return xRot, yRot, zRot
end


------------------------------------------------------------------------------------------------------------------------
-- PathfinderUtil.CollisionDetector
---------------------------------------------------------------------------------------------------------------------------
---@class PathfinderUtil.CollisionDetector
PathfinderUtil.CollisionDetector = CpObject()

Expand Down Expand Up @@ -358,7 +368,30 @@ function PathfinderUtil.hasFruit(x, z, length, width)
end
return false
end

---------------------------------------------------------------------------------------------------------------------------
-- A generic rectangular area oriented by a node
---------------------------------------------------------------------------------------------------------------------------
--- @class PathfinderUtil.Area
PathfinderUtil.Area = CpObject()

function PathfinderUtil.Area:init(node, xOffset, zOffset, width, length)
self.node = node
self.xOffset, self.zOffset = xOffset, zOffset
self.width, self.length = width, length
end

--- Is (x, z) world coordinate in the area?
function PathfinderUtil.Area:contains(x, z)
local y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z)
local dx, _, dz = worldToLocal(self.node, x, y, z)
if self.xOffset < dx and dx < self.xOffset + self.width and
self.zOffset < dz and dz < self.zOffset + self.length then
--print(x, z, dx, dz, self.xOffset, self.width, 'contains')
return true
else
return false
end
end

--[[
Pathfinding is controlled by the constraints (validity and penalty) below. The pathfinder will call these functions
Expand Down Expand Up @@ -396,16 +429,21 @@ field or driving to/from the field edge on an unload/refill course.
---@class PathfinderConstraints : PathfinderConstraintInterface
PathfinderConstraints = CpObject(PathfinderConstraintInterface)

function PathfinderConstraints:init(context, maxFruitPercent, offFieldPenalty, fieldNum)
function PathfinderConstraints:init(context, maxFruitPercent, offFieldPenalty, fieldNum, areaToAvoid)
self.context = context
self.maxFruitPercent = maxFruitPercent or 50
self.offFieldPenalty = offFieldPenalty or PathfinderUtil.defaultOffFieldPenalty
self.fieldNum = fieldNum or 0
self.areaToAvoid = areaToAvoid
self.areaToAvoidPenaltyCount = 0
self.initialMaxFruitPercent = self.maxFruitPercent
self.initialOffFieldPenalty = self.offFieldPenalty
self:resetCounts()
courseplay.debugFormat(courseplay.DBG_PATHFINDER, 'Pathfinder constraints: off field penalty %.1f, max fruit percent: %d, field number %d',
self.offFieldPenalty, self.maxFruitPercent, self.fieldNum)
local areaText = self.areaToAvoid and
string.format('%.1f x %.1f m', self.areaToAvoid.length, self.areaToAvoid.width) or 'none'
courseplay.debugFormat(courseplay.DBG_PATHFINDER,
'Pathfinder constraints: off field penalty %.1f, max fruit percent: %d, field number %d, area to avoid %s',
self.offFieldPenalty, self.maxFruitPercent, self.fieldNum, areaText)
end

function PathfinderConstraints:resetCounts()
Expand All @@ -421,7 +459,7 @@ end
function PathfinderConstraints:getNodePenalty(node)
-- tweak these two parameters to set up how far the path will be from the field or fruit boundary
-- size of the area to check for field/fruit
local areaSize = 3
local areaSize = 4
-- minimum ratio of the area checked must be on field/clear of fruit
local minRequiredAreaRatio = 0.8
local penalty = 0
Expand Down Expand Up @@ -450,7 +488,11 @@ function PathfinderConstraints:getNodePenalty(node)
self.fruitPenaltyNodeCount = self.fruitPenaltyNodeCount + 1
end
end
self.totalNodeCount = self.totalNodeCount + 1
if self.areaToAvoid and self.areaToAvoid:contains(node.x, -node.y) then
penalty = penalty + PathfinderUtil.defaultAreaToAvoidPenalty
self.areaToAvoidPenaltyCount = self.areaToAvoidPenaltyCount + 1
end
self.totalNodeCount = self.totalNodeCount + 1
return penalty
end

Expand Down Expand Up @@ -522,8 +564,10 @@ function PathfinderConstraints:relaxConstraints()
end

function PathfinderConstraints:showStatistics()
courseplay.debugFormat(courseplay.DBG_PATHFINDER, 'Nodes: %d, Penalties: fruit: %d, off-field: %d, collisions: %d',
self.totalNodeCount, self.fruitPenaltyNodeCount, self.offFieldPenaltyNodeCount, self.collisionNodeCount)
courseplay.debugFormat(courseplay.DBG_PATHFINDER,
'Nodes: %d, Penalties: fruit: %d, off-field: %d, collisions: %d, area to avoid: %d',
self.totalNodeCount, self.fruitPenaltyNodeCount, self.offFieldPenaltyNodeCount, self.collisionNodeCount,
self.areaToAvoidPenaltyCount)
courseplay.debugFormat(courseplay.DBG_PATHFINDER, ' max fruit %.1f %%, off-field penalty: %.1f',
self.maxFruitPercent, self.offFieldPenalty)
end
Expand All @@ -550,7 +594,7 @@ end
function PathfinderUtil.startPathfindingFromVehicleToGoal(vehicle, goal,
allowReverse, fieldNum,
vehiclesToIgnore, objectsToIgnore,
maxFruitPercent, offFieldPenalty, mustBeAccurate)
maxFruitPercent, offFieldPenalty, areaToAvoid, mustBeAccurate)

local start = PathfinderUtil.getVehiclePositionAsState3D(vehicle)

Expand All @@ -563,7 +607,7 @@ function PathfinderUtil.startPathfindingFromVehicleToGoal(vehicle, goal,
local constraints = PathfinderConstraints(context,
maxFruitPercent or (vehicle.cp.settings.useRealisticDriving:is(true) and 50 or math.huge),
offFieldPenalty or PathfinderUtil.defaultOffFieldPenalty,
fieldNum)
fieldNum, areaToAvoid)

return PathfinderUtil.startPathfinding(start, goal, context, constraints, allowReverse, mustBeAccurate)
end
Expand Down Expand Up @@ -709,15 +753,17 @@ end
---@param vehiclesToIgnore table[] list of vehicles to ignore for the collision detection (optional)
---@param maxFruitPercent number maximum percentage of fruit present before a node is marked as invalid (optional)
---@param offFieldPenalty number penalty to apply to nodes off the field
---@param areaToAvoid PathfinderUtil.Area nodes in this area will be penalized so the path will most likely avoid it
function PathfinderUtil.startPathfindingFromVehicleToWaypoint(vehicle, goalWaypoint,
xOffset, zOffset, allowReverse,
fieldNum, vehiclesToIgnore, maxFruitPercent, offFieldPenalty)
fieldNum, vehiclesToIgnore, maxFruitPercent,
offFieldPenalty, areaToAvoid)

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, goal, allowReverse, fieldNum, vehiclesToIgnore, {}, maxFruitPercent, offFieldPenalty)
vehicle, goal, allowReverse, fieldNum, vehiclesToIgnore, {}, maxFruitPercent, offFieldPenalty, areaToAvoid)
end
------------------------------------------------------------------------------------------------------------------------
--- Interface function to start the pathfinder in the game. The goal is a point at sideOffset meters from the goal node
Expand All @@ -732,16 +778,17 @@ end
---@param vehiclesToIgnore table[] list of vehicles to ignore for the collision detection (optional)
---@param maxFruitPercent number maximum percentage of fruit present before a node is marked as invalid (optional)
---@param offFieldPenalty number penalty to apply to nodes off the field
---@param areaToAvoid PathfinderUtil.Area nodes in this area will be penalized so the path will most likely avoid it
---@param mustBeAccurate boolean must be accurately find the goal position/angle (optional)
function PathfinderUtil.startPathfindingFromVehicleToNode(vehicle, goalNode,
xOffset, zOffset, allowReverse,
fieldNum, vehiclesToIgnore, maxFruitPercent, offFieldPenalty,
mustBeAccurate)
fieldNum, vehiclesToIgnore, maxFruitPercent,
offFieldPenalty, areaToAvoid, mustBeAccurate)
x, z, yRot = PathfinderUtil.getNodePositionAndDirection(goalNode, xOffset, zOffset)
local goal = State3D(x, -z, courseGenerator.fromCpAngle(yRot))
return PathfinderUtil.startPathfindingFromVehicleToGoal(
vehicle, goal, allowReverse, fieldNum,
vehiclesToIgnore, {}, maxFruitPercent, offFieldPenalty, mustBeAccurate)
vehiclesToIgnore, {}, maxFruitPercent, offFieldPenalty, areaToAvoid, mustBeAccurate)
end

------------------------------------------------------------------------------------------------------------------------
Expand Down
Binary file removed img/CoursePlay_Icon.dds
Binary file not shown.
Binary file added img/icon_courseplay.dds
Binary file not shown.
Binary file removed img/store.dds
Binary file not shown.
4 changes: 2 additions & 2 deletions modDesc.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" standalone="no" ?>
<modDesc descVersion="53">
<version>6.03.00058</version>
<version>6.03.00059</version>
<author><![CDATA[Courseplay.devTeam]]></author>
<title><!-- 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>
Expand Down Expand Up @@ -70,7 +70,7 @@ Courseplay non sostituisce il sistema di gioco in gioco, ma crea un nuovo lavora
</description>

<multiplayer supported="true" />
<iconFilename>img/store.dds</iconFilename>
<iconFilename>img/icon_courseplay.dds</iconFilename>
<l10n filenamePrefix="translations/translation" /> <!-- load translations from "translations/translation_<language>.xml file -->

<extraSourceFiles>
Expand Down

0 comments on commit 3c6a826

Please sign in to comment.