-
-
Notifications
You must be signed in to change notification settings - Fork 69
Expand file tree
/
Copy pathCpAIJobFieldWork.lua
More file actions
260 lines (234 loc) · 10.1 KB
/
CpAIJobFieldWork.lua
File metadata and controls
260 lines (234 loc) · 10.1 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
--- AI job derived of CpAIJob.
if CpAIJobFieldWork == nil then
-- create class only on game load once, otherwise just overwrite the members, so the class can be reloaded without
-- restarting the game
---@class CpAIJobFieldWork : CpAIJob
CpAIJobFieldWork = CpObject(CpAIJob)
end
CpAIJobFieldWork.name = "FIELDWORK_CP"
CpAIJobFieldWork.jobName = "CP_job_fieldWork"
CpAIJobFieldWork.GenerateButton = "FIELDWORK_BUTTON"
function CpAIJobFieldWork:init(isServer)
CpAIJob.init(self, isServer)
self.foundVines = nil
self.selectedFieldPlot = FieldPlot(true)
self.selectedFieldPlot:setVisible(false)
self.selectedFieldPlot:setBrightColor(true)
self.courseGeneratorInterface = CourseGeneratorInterface()
end
function CpAIJobFieldWork:setupTasks(isServer)
-- this will add a standard driveTo task to drive to the target position selected by the user
CpAIJob.setupTasks(self, isServer)
-- then we add our own driveTo task to drive from the target position to the waypoint where the
-- fieldwork starts (first waypoint or the one we worked on last)
self.attachHeaderTask = CpAITaskAttachHeader(isServer, self)
self.driveToFieldWorkStartTask = CpAITaskDriveTo(isServer, self)
self.fieldWorkTask = CpAITaskFieldWork(isServer, self)
end
function CpAIJobFieldWork:onPreStart()
CpAIJob.onPreStart(self)
self:removeTask(self.attachHeaderTask)
self:removeTask(self.driveToFieldWorkStartTask)
self:removeTask(self.fieldWorkTask)
local vehicle = self:getVehicle()
if vehicle and (AIUtil.hasCutterOnTrailerAttached(vehicle)
or AIUtil.hasCutterAsTrailerAttached(vehicle)) then
--- Only add the attach header task, if needed.
self:addTask(self.attachHeaderTask)
end
self:addTask(self.driveToFieldWorkStartTask)
self:addTask(self.fieldWorkTask)
end
function CpAIJobFieldWork:setupJobParameters()
CpAIJob.setupJobParameters(self)
self:setupCpJobParameters(CpFieldWorkJobParameters(self))
end
---@param vehicle table
---@param mission table
---@param farmId number
---@param isDirectStart boolean disables the drive to by giants
---@param resetToVehiclePosition boolean resets the drive to target position by giants and the field position to the vehicle position.
function CpAIJobFieldWork:applyCurrentState(vehicle, mission, farmId, isDirectStart, resetToVehiclePosition)
CpAIJob.applyCurrentState(self, vehicle, mission, farmId, isDirectStart)
if resetToVehiclePosition then
-- set the start and the field position to the vehicle's position (
local x, _, z = getWorldTranslation(vehicle.rootNode)
local dirX, _, dirZ = localDirectionToWorld(vehicle.rootNode, 0, 0, 1)
local angle = MathUtil.getYRotationFromDirection(dirX, dirZ)
self.cpJobParameters.startPosition:setPosition(x, z)
self.cpJobParameters.startPosition:setAngle(angle)
self.cpJobParameters.fieldPosition:setPosition(x, z)
else
local x, z = self.cpJobParameters.fieldPosition:getPosition()
if x == nil or z == nil then
-- if there is no field position set, set it to the vehicle's position
x, _, z = getWorldTranslation(vehicle.rootNode)
self.cpJobParameters.fieldPosition:setPosition(x, z)
end
end
local fieldPolygon = vehicle:cpGetFieldPolygon()
if fieldPolygon then
-- if we already have a field polygon, show it
self.selectedFieldPlot:setWaypoints(fieldPolygon)
self.selectedFieldPlot:setVisible(true)
end
end
function CpAIJobFieldWork:onFieldBoundaryDetectionFinished(vehicle, fieldPolygon, islandPolygons)
if fieldPolygon then
local x, z = vehicle:cpGetFieldPosition()
self.foundVines = g_vineScanner:findVineNodesInField(fieldPolygon, x, z, self.customField ~= nil)
if self.foundVines then
CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, vehicle, "Found vine nodes, generating a vine field border.")
fieldPolygon = g_vineScanner:getCourseGeneratorVertices(0, x, z)
end
self.selectedFieldPlot:setWaypoints(fieldPolygon)
self.selectedFieldPlot:setVisible(true)
else
self.selectedFieldPlot:setVisible(false)
self:callFieldBoundaryDetectionFinishedCallback(false, 'CP_error_field_detection_failed')
end
self:callFieldBoundaryDetectionFinishedCallback(true)
end
function CpAIJobFieldWork:setValues()
CpAIJob.setValues(self)
local vehicle = self.vehicleParameter:getVehicle()
self.driveToFieldWorkStartTask:reset()
self.driveToFieldWorkStartTask:setVehicle(vehicle)
self.attachHeaderTask:setVehicle(vehicle)
self.fieldWorkTask:setVehicle(vehicle)
end
--- Called when parameters change, scan field
---@param farmId number not used
function CpAIJobFieldWork:validate(farmId)
local isValid, isRunning, errorMessage = CpAIJob.validate(self, farmId)
if not isValid then
return isValid, errorMessage
end
local vehicle = self.vehicleParameter:getVehicle()
--- Only check the valid field position in the in game menu.
if not self.isDirectStart then
isValid, isRunning, errorMessage = self:detectFieldBoundary()
if not isValid then
return isValid, errorMessage
end
self.cpJobParameters:validateSettings()
end
if not vehicle:hasCpCourse() then
return false, g_i18n:getText("CP_error_no_course")
end
return true, ''
end
function CpAIJobFieldWork:draw(map, isOverviewMap)
CpAIJob.draw(self, map, isOverviewMap)
if not isOverviewMap then
if self.selectedFieldPlot then
self.selectedFieldPlot:draw(map)
end
end
end
function CpAIJobFieldWork:getCanGenerateFieldWorkCourse()
local vehicle = self:getVehicle()
return vehicle and vehicle:cpGetFieldPolygon() ~= nil and not vehicle:cpIsFieldBoundaryDetectionRunning()
end
-- To pass an alignment course from the drive to fieldwork start to the fieldwork, so the
-- fieldwork strategy can continue the alignment course set up by the drive to fieldwork start strategy.
function CpAIJobFieldWork:setStartFieldWorkCourse(course, ix)
self.startFieldWorkCourse = course
self.startFieldWorkCourseIx = ix
end
function CpAIJobFieldWork:getStartFieldWorkCourse()
return self.startFieldWorkCourse, self.startFieldWorkCourseIx
end
--- Is course generation allowed ?
function CpAIJobFieldWork:isCourseGenerationAllowed()
local vehicle = self:getVehicle()
--- Disables the course generation for bale loaders and wrappers.
local baleFinderAllowed = vehicle and vehicle:getCanStartCpBaleFinder()
return self:getCanGenerateFieldWorkCourse() and not baleFinderAllowed
end
function CpAIJobFieldWork:getCanStartJob()
local vehicle = self:getVehicle()
return vehicle and vehicle:hasCpCourse()
end
--- Button callback to generate a field work course.
function CpAIJobFieldWork:onClickGenerateFieldWorkCourse(callback)
local vehicle = self.vehicleParameter:getVehicle()
local settings = vehicle:getCourseGeneratorSettings()
local tx, tz = self.cpJobParameters.fieldPosition:getPosition()
local ok, course
if self.foundVines then
local vineSettings = vehicle:getCpVineSettings()
local vertices, width, startingPoint, rowAngleDeg = g_vineScanner:getCourseGeneratorVertices(
vineSettings.vineCenterOffset:getValue(),
tx, tz
)
ok, course = self.courseGeneratorInterface:generateVineCourse(
vertices,
startingPoint,
vehicle,
width,
AIUtil.getTurningRadius(vehicle),
rowAngleDeg,
vineSettings.vineRowsToSkip:getValue(),
-- no multitools until we fix the generation for vines
1,
g_vineScanner:getLines(),
vineSettings.vineCenterOffset:getValue())
callback(course)
else
self.courseGeneratorInterface:startGeneration(
{ x = tx, z = tz },
vehicle,
settings,
nil,
callback,
-- this button is only enabled if we already detected the field boundary, so use the existing polygons
vehicle:cpGetFieldPolygon(),
vehicle:cpGetIslandPolygons())
end
return true
end
function CpAIJobFieldWork:isPipeOnLeftSide(vehicle)
local pipeObject = AIUtil.getImplementOrVehicleWithSpecialization(vehicle, Pipe)
if pipeObject and SpecializationUtil.hasSpecialization(Combine, pipeObject.specializations) then
--- The controller measures the pipe attributes on creation.
local controller = PipeController(vehicle, pipeObject, true)
local isPipeOnLeftSide = controller:isPipeOnTheLeftSide()
controller:delete()
return isPipeOnLeftSide
else
return true
end
end
function CpAIJobFieldWork:getIsAvailableForVehicle(vehicle, cpJobsAllowed)
return CpAIJob.getIsAvailableForVehicle(self, vehicle, cpJobsAllowed) and vehicle.getCanStartCpFieldWork and vehicle:getCanStartCpFieldWork() -- TODO_25
end
--- Ugly hack to fix a mp problem from giants, where the helper is not always reset correctly on the client side.
function CpAIJobFieldWork:stop(aiMessage)
CpAIJob.stop(self, aiMessage)
local vehicle = self.vehicleParameter:getVehicle()
if vehicle and vehicle.spec_aiFieldWorker.isActive then
vehicle.spec_aiFieldWorker.isActive = false
end
end
function CpAIJobFieldWork:hasFoundVines()
return self.foundVines
end
function CpAIJobFieldWork:setStartPosition(startPosition)
if self.fieldWorkTask then
self.fieldWorkTask:setStartPosition(startPosition)
end
end
--- Gets the additional task description shown.
function CpAIJobFieldWork:getDescription()
local desc = CpAIJob.getDescription(self)
local currentTask = self:getTaskByIndex(self.currentTaskIndex)
if currentTask == self.driveToTask then
desc = desc .. " - " .. g_i18n:getText("ai_taskDescriptionDriveToField")
elseif currentTask == self.fieldWorkTask then
desc = desc .. " - " .. g_i18n:getText("ai_taskDescriptionFieldWork")
elseif currentTask == self.attachHeaderTask then
desc = desc .. " - " .. g_i18n:getText("CP_ai_taskDescriptionAttachHeader")
end
return desc
end