-
-
Notifications
You must be signed in to change notification settings - Fork 72
Expand file tree
/
Copy pathPathfinderContext.lua
More file actions
133 lines (122 loc) · 6.57 KB
/
Copy pathPathfinderContext.lua
File metadata and controls
133 lines (122 loc) · 6.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
--[[
This file is part of Courseplay (https://github.com/Courseplay/Courseplay_FS25)
Copyright (C) 2023 Courseplay Dev Team
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 <http://www.gnu.org/licenses/>.
]]
--[[
--------------------------------------------
--- Pathfinder controller context
--------------------------------------------
PathfinderContext implements all pathfinder parameters.
- Other Context Classes could be derived for commonly used contexts.
- Only the attributesToDefaultValue table has to be changed in the derived class.
Example usage of the builder api:
local context = PathfinderContext():maxFruitPercent(100):useFieldNum(10):vehiclesToIgnore({vehicle})
]]
---@class PathfinderContext
---@field maxFruitPercent function
---@field offFieldPenalty function
---@field useFieldNum function
---@field areaToAvoid function
---@field allowReverse function
---@field vehiclesToIgnore function
---@field mustBeAccurate function
---@field areaToIgnoreFruit function
---@field areaToIgnoreOffFieldPenalty function
---@field ignoreFruitHeaps function
---@field maxIterations function
---@field obstacleAtStartRange function
---@field ignoreTrailerAtStartRange function
---@field _maxFruitPercent number
---@field _offFieldPenalty number
---@field _useFieldNum number
---@field _areaToAvoid PathfinderUtil.NodeArea|nil
---@field _allowReverse boolean
---@field _vehiclesToIgnore table[]|nil
---@field _objectsToIgnore table[]|nil
---@field _mustBeAccurate boolean
---@field _areaToIgnoreFruit PathfinderUtil.Area|nil
---@field _areaToIgnoreOffFieldPenalty PathfinderUtil.NodeArea|nil
---@field _ignoreFruitHeaps boolean
---@field _maxIterations number
---@field _obstacleAtStartRange number
---@field _ignoreTrailerAtStartRange number
PathfinderContext = CpObject()
PathfinderContext.defaultOffFieldPenalty = 7.5
PathfinderContext.preferredPathAffinity = 5
PathfinderContext.attributesToDefaultValue = {
-- If an 4 x 4 m area around a pathfinder node has more than this fruit, a penalty of 0.5 * actual fruit
-- percentage will be applied to that node.
-- TODO: check if the fruitValue returned by FSDensityMapUtil.getFruitArea() really is a percentage
["maxFruitPercent"] = 50,
-- This penalty is added to the cost of each step, which is about 12% of the turning radius when using
-- the hybrid A* and 3 with the simple A*.
-- Simple A* is used for long-range pathfinding, in that case we are willing to drive about 3 times longer
-- to stay on the field. Hybrid A* is more restrictive, TODO: review if these should be balanced
["offFieldPenalty"] = PathfinderContext.defaultOffFieldPenalty,
-- If useFieldNum > 0, fields that are not owned have a 20% greater penalty.
["useFieldNum"] = 0,
-- Pathfinder nodes in this area have a prohibitive penalty (2000)
["areaToAvoid"] = CpObjectUtil.BUILDER_API_NIL,
["allowReverse"] = false,
["vehiclesToIgnore"] = {},
["objectsToIgnore"] = {},
-- If false, as we reach the maximum iterations, we relax our criteria to reach the goal: allow for arriving at
-- bigger angle differences, trading off accuracy for speed. This usually results in a direction at the goal
-- being less then 30º off which in many cases isn't a problem.
-- Otherwise, for example when a combine self unloading must accurately find the trailer, set this to true.
["mustBeAccurate"] = false,
-- No fruit penalty in this area (e.g. when we know the goal is in fruit but want to avoid fruit all the way there)
["areaToIgnoreFruit"] = CpObjectUtil.BUILDER_API_NIL,
-- A path [{x, z}, {x, z}, ...] that the pathfinder will try to follow. Nodes closer than
-- PathfinderContext.preferredPathAffinity to the path will have a negative penalty
["preferredPath"] = CpObjectUtil.BUILDER_API_NIL,
-- No off-field penalty in this area (for instance when need to approach another vehicle, such as a trailer
-- to unload to, regardless of it is on the field or not, but we do want to have normal off-field penalty for
-- the rest of the path
["areaToIgnoreOffFieldPenalty"] = CpObjectUtil.BUILDER_API_NIL,
-- Tell the collision detector to ignore heaps of fruit on the ground.
["ignoreFruitHeaps"] = false,
-- If the pathfinding fails without getting further than 1.5 * turning radius, the controller triggers
-- the "obstacle at start" callback. This is to override that limit when needed.
["obstacleAtStartRange"] = CpObjectUtil.BUILDER_API_NIL,
-- If true, ignore the trailer within so many meters (actual path length, not distance) of the start.
-- This is useful when there's another vehicle close to the starting point, and the trailer (with the
-- buffer area around it) often triggers a collision which, when the vehicle drives the path, isn't
-- really a problem.
["ignoreTrailerAtStartRange"] = 0,
-- Maximum number of iterations before the hybrid A* pathfinding gives up. The default works well
-- for normal sized fields, on bigger maps with huge fields you may want to double or triple this
["maxIterations"] = HybridAStar.defaultMaxIterations,
-- Collision mask to use, to disable collisions completely, set to 0objectsToIgnore
["collisionMask"] = CpUtil.getDefaultCollisionFlags() + CollisionFlag.TERRAIN_DELTA
}
function PathfinderContext:init(vehicle)
self._vehicle = vehicle
CpObjectUtil.registerBuilderAPI(self, self.attributesToDefaultValue)
end
--- Disables the fruit avoidance if true, otherwise sets the default value.
function PathfinderContext:ignoreFruit(ignore)
self._maxFruitPercent = ignore and math.huge or PathfinderContext.attributesToDefaultValue.maxFruitPercent
return self
end
--- Uses the field number of the vehicle to restrict path finding.
function PathfinderContext:useVehicleFieldNumber()
self._useFieldNum = CpFieldUtil.getFieldNumUnderVehicle(self._vehicle)
return self
end
function PathfinderContext:__tostring()
local str = string.format('[ %s: ', CpUtil.getName(self._vehicle))
str = self:attributesToString(str, PathfinderContext.attributesToDefaultValue, '_')
str = str .. ']'
return str
end