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

Mode2 improvements #7188

Merged
merged 6 commits into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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