-
-
Notifications
You must be signed in to change notification settings - Fork 69
Expand file tree
/
Copy pathCpAIFieldWorker.lua
More file actions
339 lines (288 loc) · 16.5 KB
/
CpAIFieldWorker.lua
File metadata and controls
339 lines (288 loc) · 16.5 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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
--- Cp field worker that drives along a course.
local modName = CpAIFieldWorker and CpAIFieldWorker.MOD_NAME -- for reload
---@class CpAIFieldWorker
CpAIFieldWorker = {}
CpAIFieldWorker.MOD_NAME = g_currentModName or modName
CpAIFieldWorker.NAME = ".cpAIFieldWorker"
CpAIFieldWorker.SPEC_NAME = CpAIFieldWorker.MOD_NAME .. CpAIFieldWorker.NAME
CpAIFieldWorker.KEY = "."..CpAIFieldWorker.MOD_NAME..CpAIFieldWorker.NAME
local profilers = {}
-- shortcut to access the spec
function CpAIFieldWorker.getSpec(self)
return self["spec_" .. CpAIFieldWorker.SPEC_NAME]
end
function CpAIFieldWorker.initSpecialization()
local schema = Vehicle.xmlSchemaSavegame
local key = "vehicles.vehicle(?)" .. CpAIFieldWorker.KEY
CpJobParameters.registerXmlSchema(schema, key..".cpJob")
CpJobParameters.registerXmlSchema(schema, key..".cpJobStartAtLastWp")
end
function CpAIFieldWorker.prerequisitesPresent(specializations)
return SpecializationUtil.hasSpecialization(CpAIWorker, specializations)
end
function CpAIFieldWorker.register(typeManager,typeName,specializations)
if CpAIFieldWorker.prerequisitesPresent(specializations) then
typeManager:addSpecialization(typeName, CpAIFieldWorker.SPEC_NAME)
end
end
function CpAIFieldWorker.registerEvents(vehicleType)
-- SpecializationUtil.registerEvent(vehicleType, "onCpFinished")
end
function CpAIFieldWorker.registerEventListeners(vehicleType)
SpecializationUtil.registerEventListener(vehicleType, "onLoad", CpAIFieldWorker)
SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", CpAIFieldWorker)
SpecializationUtil.registerEventListener(vehicleType, "onCpEmpty", CpAIFieldWorker)
SpecializationUtil.registerEventListener(vehicleType, "onCpFull", CpAIFieldWorker)
SpecializationUtil.registerEventListener(vehicleType, "onCpFinished", CpAIFieldWorker)
SpecializationUtil.registerEventListener(vehicleType, "onStateChange", CpAIFieldWorker)
SpecializationUtil.registerEventListener(vehicleType, 'onCpCourseChange', CpAIFieldWorker)
SpecializationUtil.registerEventListener(vehicleType, 'onCpADStartedByPlayer', CpAIFieldWorker)
SpecializationUtil.registerEventListener(vehicleType, 'onCpADRestarted', CpAIFieldWorker)
end
function CpAIFieldWorker.registerFunctions(vehicleType)
SpecializationUtil.registerFunction(vehicleType, "getIsCpFieldWorkActive", CpAIFieldWorker.getIsCpFieldWorkActive)
SpecializationUtil.registerFunction(vehicleType, "getCpFieldWorkProgress", CpAIFieldWorker.getCpFieldWorkProgress)
SpecializationUtil.registerFunction(vehicleType, "getIsCpHarvesterWaitingForUnload",
CpAIFieldWorker.getIsCpHarvesterWaitingForUnload)
SpecializationUtil.registerFunction(vehicleType, "getIsCpHarvesterWaitingForUnloadInPocket",
CpAIFieldWorker.getIsCpHarvesterWaitingForUnloadInPocket)
SpecializationUtil.registerFunction(vehicleType, "getIsCpHarvesterWaitingForUnloadAfterPulledBack",
CpAIFieldWorker.getIsCpHarvesterWaitingForUnloadAfterPulledBack)
SpecializationUtil.registerFunction(vehicleType, "getIsCpHarvesterManeuvering", CpAIFieldWorker.getIsCpHarvesterManeuvering)
SpecializationUtil.registerFunction(vehicleType, "holdCpHarvesterTemporarily", CpAIFieldWorker.holdCpHarvesterTemporarily)
SpecializationUtil.registerFunction(vehicleType, "getCanStartCpFieldWork", CpAIFieldWorker.getCanStartCpFieldWork)
SpecializationUtil.registerFunction(vehicleType, "startCpAtFirstWp", CpAIFieldWorker.startCpAtFirstWp)
SpecializationUtil.registerFunction(vehicleType, "startCpAtLastWp", CpAIFieldWorker.startCpAtLastWp)
SpecializationUtil.registerFunction(vehicleType, "getCpStartingPointSetting", CpAIFieldWorker.getCpStartingPointSetting)
SpecializationUtil.registerFunction(vehicleType, "getCpLaneOffsetSetting", CpAIFieldWorker.getCpLaneOffsetSetting)
end
function CpAIFieldWorker.registerOverwrittenFunctions(vehicleType)
SpecializationUtil.registerOverwrittenFunction(vehicleType, 'getCanStartCp', CpAIFieldWorker.getCanStartCp)
SpecializationUtil.registerOverwrittenFunction(vehicleType, 'getCpStartableJob', CpAIFieldWorker.getCpStartableJob)
SpecializationUtil.registerOverwrittenFunction(vehicleType, 'getCpDriveStrategy', CpAIFieldWorker.getCpDriveStrategy)
end
------------------------------------------------------------------------------------------------------------------------
--- Event listeners
---------------------------------------------------------------------------------------------------------------------------
function CpAIFieldWorker:onLoad(savegame)
--- Register the spec: spec_cpAIFieldWorker
self.spec_cpAIFieldWorker = CpAIFieldWorker.getSpec(self)
local spec = CpAIFieldWorker.getSpec(self)
--- This job is for starting the driving with a key bind or the hud.
spec.cpJob = g_currentMission.aiJobTypeManager:createJob(AIJobType.FIELDWORK_CP)
spec.cpJob:getCpJobParameters().startAt:setValue(CpFieldWorkJobParameters.START_AT_NEAREST_POINT)
spec.cpJob:setVehicle(self, true)
--- Theses jobs are used for external mod, for example AutoDrive.
spec.cpJobStartAtFirstWp = g_currentMission.aiJobTypeManager:createJob(AIJobType.FIELDWORK_CP)
spec.cpJobStartAtFirstWp:getCpJobParameters().startAt:setValue(CpFieldWorkJobParameters.START_AT_FIRST_POINT)
spec.cpJobStartAtLastWp = g_currentMission.aiJobTypeManager:createJob(AIJobType.FIELDWORK_CP)
spec.cpJobStartAtLastWp:getCpJobParameters().startAt:setValue(CpFieldWorkJobParameters.START_AT_LAST_POINT)
end
function CpAIFieldWorker:onLoadFinished(savegame)
local spec = CpAIFieldWorker.getSpec(self)
if savegame ~= nil then
spec.cpJob:getCpJobParameters():loadFromXMLFile(savegame.xmlFile, savegame.key.. CpAIFieldWorker.KEY..".cpJob")
spec.cpJobStartAtLastWp:getCpJobParameters():loadFromXMLFile(savegame.xmlFile, savegame.key.. CpAIFieldWorker.KEY..".cpJobStartAtLastWp")
end
end
function CpAIFieldWorker:saveToXMLFile(xmlFile, baseKey, usedModNames)
local spec = CpAIFieldWorker.getSpec(self)
spec.cpJob:getCpJobParameters():saveToXMLFile(xmlFile, baseKey.. ".cpJob")
spec.cpJobStartAtLastWp:getCpJobParameters():saveToXMLFile(xmlFile, baseKey.. ".cpJobStartAtLastWp")
end
function CpAIFieldWorker:onStateChange(state, data)
local spec = CpAIFieldWorker.getSpec(self)
if state == VehicleStateChange.ATTACH then
spec.cpJob:getCpJobParameters():validateSettings()
elseif state == VehicleStateChange.DETACH then
spec.cpJob:getCpJobParameters():validateSettings()
end
end
function CpAIFieldWorker:onCpCourseChange()
local spec = CpAIFieldWorker.getSpec(self)
if spec.cpJob then
spec.cpJob:getCpJobParameters():validateSettings()
end
end
function CpAIFieldWorker:getCpStartingPointSetting()
local spec = CpAIFieldWorker.getSpec(self)
return spec.cpJob:getCpJobParameters().startAt
end
function CpAIFieldWorker:getCpLaneOffsetSetting()
local spec = CpAIFieldWorker.getSpec(self)
return spec.cpJob:getCpJobParameters().laneOffset
end
------------------------------------------------------------------------------------------------------------------------
--- Interface for other mods, like AutoDrive
------------------------------------------------------------------------------------------------------------------------
--- Is a cp fieldwork job active ?
function CpAIFieldWorker:getIsCpFieldWorkActive()
return self:getIsAIActive() and self:getJob() and self:getJob().is_a and self:getJob():is_a(CpAIJobFieldWork)
end
function CpAIFieldWorker:getCpFieldWorkProgress()
local strategy = self:getCpDriveStrategy()
if strategy then
return strategy:getProgress()
end
end
--- Gets the current field work drive strategy.
function CpAIFieldWorker:getCpDriveStrategy(superFunc)
return superFunc(self) or (self.spec_cpAIFieldWorker and self.spec_cpAIFieldWorker.driveStrategy)
end
--- To find out if a harvester is waiting to be unloaded, either because it is full or ended the fieldwork course
--- with some grain in the tank.
---@return boolean true when the harvester is waiting to be unloaded
function CpAIFieldWorker:getIsCpHarvesterWaitingForUnload()
return self.spec_cpAIFieldWorker.combineDriveStrategy and
self.spec_cpAIFieldWorker.combineDriveStrategy:isWaitingForUnload()
end
--- To find out if a harvester is waiting to be unloaded in a pocket. Harvesters may cut a pocket on the opposite
--- side of the pipe to make room for an unloader if:
--- * working on the first headland (so the unloader can get under the pipe while staying on the headland)
--- * cutting the first row in the middle of the field
---@return boolean true when the harvester is waiting to be unloaded in a pocket
function CpAIFieldWorker:getIsCpHarvesterWaitingForUnloadInPocket()
return self.spec_cpAIFieldWorker.combineDriveStrategy and
self.spec_cpAIFieldWorker.combineDriveStrategy:isWaitingInPocket()
end
--- To find out if a harvester is waiting to be unloaded after it pulled back to the side. This
--- is similar to a pocket but in this case there is no fruit on the opposite side of the pipe,
--- so the harvester just moves to the side and backwards without cutting a pocket.
---@return boolean
function CpAIFieldWorker:getIsCpHarvesterWaitingForUnloadAfterPulledBack()
return self.spec_cpAIFieldWorker.combineDriveStrategy and
self.spec_cpAIFieldWorker.combineDriveStrategy:isWaitingForUnloadAfterPulledBack()
end
--- Maneuvering means turning or working on a pocket or pulling back due to the pipe in fruit
---@return boolean true when the harvester is maneuvering so that an unloader should stay away.
function CpAIFieldWorker:getIsCpHarvesterManeuvering()
return self.spec_cpAIFieldWorker.combineDriveStrategy and
self.spec_cpAIFieldWorker.combineDriveStrategy:isManeuvering()
end
--- Hold the harvester (set its speed to 0) for a period of periodMs milliseconds.
--- Calling this again will restart the timer with the new value. Calling with 0 will end the temporary hold
--- immediately.
---@param periodMs number
function CpAIFieldWorker:holdCpHarvesterTemporarily(periodMs)
return self.spec_cpAIFieldWorker.combineDriveStrategy and
self.spec_cpAIFieldWorker.combineDriveStrategy:hold(periodMs)
end
--- Starts the cp driver at the first waypoint.
function CpAIFieldWorker:startCpAtFirstWp()
local spec = CpAIFieldWorker.getSpec(self)
self:updateAIFieldWorkerImplementData()
if self:hasCpCourse() and self:getCanStartCpFieldWork() then
spec.cpJobStartAtFirstWp:applyCurrentState(self, g_currentMission, g_currentMission.playerSystem:getLocalPlayer().farmId, true, false)
--- Applies the lane offset set in the hud, so ad can start with the correct lane offset.
spec.cpJobStartAtFirstWp:getCpJobParameters().laneOffset:setValue(self:getCpLaneOffsetSetting():getValue())
spec.cpJobStartAtFirstWp:setValues()
local success = spec.cpJobStartAtFirstWp:validate(false)
if success then
g_client:getServerConnection():sendEvent(AIJobStartRequestEvent.new(spec.cpJobStartAtFirstWp, self:getOwnerFarmId()))
return true
end
end
end
--- Starts the cp driver at the last driven waypoint.
function CpAIFieldWorker:startCpAtLastWp()
local spec = CpAIFieldWorker.getSpec(self)
self:updateAIFieldWorkerImplementData()
if self:hasCpCourse() and self:getCanStartCpFieldWork() then
spec.cpJobStartAtLastWp:applyCurrentState(self, g_currentMission, g_currentMission.playerSystem:getLocalPlayer().farmId, true, false)
spec.cpJobStartAtLastWp:setValues()
CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, "lane offset: %s", spec.cpJobStartAtLastWp:getCpJobParameters().laneOffset:getString())
local success = spec.cpJobStartAtLastWp:validate(false)
CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, "lane offset: %s", spec.cpJobStartAtLastWp:getCpJobParameters().laneOffset:getString())
if success then
g_client:getServerConnection():sendEvent(AIJobStartRequestEvent.new(spec.cpJobStartAtLastWp, self:getOwnerFarmId()))
return true
end
end
end
function CpAIFieldWorker:onCpADStartedByPlayer()
local spec = CpAIFieldWorker.getSpec(self)
--- Applies the lane offset set in the hud, so ad can start with the correct one.
spec.cpJobStartAtLastWp:getCpJobParameters().laneOffset:setValue(self:getCpLaneOffsetSetting():getValue())
end
function CpAIFieldWorker:onCpADRestarted()
local spec = CpAIFieldWorker.getSpec(self)
end
--- Event listener called, when an implement is full.
function CpAIFieldWorker:onCpFull()
end
--- Event listener called, when an implement is empty.
function CpAIFieldWorker:onCpEmpty()
end
--- Event listener called, when the cp job is finished.
function CpAIFieldWorker:onCpFinished()
end
function CpAIFieldWorker:getCanStartCpFieldWork()
self:updateAIFieldWorkerImplementData()
-- built in helper can't handle it, but we may be able to ...
if AIUtil.hasChildVehicleWithSpecialization(self, nil, "spec_pdlc_goeweilPack.balerStationary") then
--- Make sure stationary balers are ignored!
return false
end
if AIUtil.hasChildVehicleWithSpecialization(self, Baler) or
AIUtil.hasChildVehicleWithSpecialization(self, StonePicker) or
AIUtil.hasImplementWithSpecialization(self, BaleWrapper) or
AIUtil.hasImplementWithSpecialization(self, BaleLoader) or
AIUtil.hasChildVehicleWithSpecialization(self, ForageWagon) or
-- built in helper can't handle forage harvesters.
AIUtil.hasImplementWithSpecialization(self, Cutter) or
AIUtil.hasChildVehicleWithSpecialization(self, VineCutter) or
AIUtil.hasChildVehicleWithSpecialization(self, VinePrepruner) or
--- This also allows the use of stump cutters, that work like a mulcher.
AIUtil.hasChildVehicleWithSpecialization(self, Plow) or
--- A few mowers can't be used by the giants fieldworker
AIUtil.hasChildVehicleWithSpecialization(self, Mower) or
--- Pushed hand tools can't be used by the giants fieldworker
AIUtil.hasChildVehicleWithSpecialization(self, PushHandTool) or
-- Harvester with cutter on trailer attached.
AIUtil.hasCutterOnTrailerAttached(self) or
--- precision farming
AIUtil.hasChildVehicleWithSpecialization(self, nil, CpUtil.getSoilSamplerSpecName()) or
--- FS22_aPalletAutoLoader from Achimobil: https://bitbucket.org/Achimobil79/ls22_palletautoloader/src/master/
AIUtil.hasChildVehicleWithSpecialization(self, nil, "spec_aPalletAutoLoader") or
--- FS22_UniversalAutoload from Loki79uk: https://github.com/loki79uk/FS22_UniversalAutoload
AIUtil.hasValidUniversalTrailerAttached(self) then
return true
end
return self:getCanStartFieldWork()
end
--- Only allow the basic field work job to start, if a course is assigned.
function CpAIFieldWorker:getCanStartCp(superFunc)
return not self:getIsCpCourseRecorderActive() and self:hasCpCourse() and self:getCanStartCpFieldWork() or superFunc(self)
end
--- Gets the field work job for the hud or start action event.
function CpAIFieldWorker:getCpStartableJob(superFunc, isStartedByHud)
local spec = CpAIFieldWorker.getSpec(self)
if isStartedByHud and self:cpIsHudFieldWorkJobSelected() then
return self:getCanStartCpFieldWork() and self:hasCpCourse() and spec.cpJob
end
return superFunc(self, isStartedByHud) or not isStartedByHud and self:getCanStartCpFieldWork() and self:hasCpCourse() and spec.cpJob
end
--- Makes sure a callstack is printed, when an error appeared.
--- TODO: Might be a good idea to stop the cp helper.
local function onUpdate(vehicle, superFunc, ...)
if not profilers[vehicle] then
profilers[vehicle] = Profiler(vehicle)
end
profilers[vehicle]:start()
CpUtil.try(superFunc, vehicle, ...)
profilers[vehicle]:stop()
profilers[vehicle]:render()
if g_updateLoopIndex % 100 == 0 then
profilers[vehicle]:log()
end
end
AIFieldWorker.onUpdate = Utils.overwrittenFunction(AIFieldWorker.onUpdate, onUpdate)
--- Ugly hack to avoid restarting the giants helper after loading a savegame,
--- if cp was active prior to saving the game ...
local function saveToXMLFile(vehicle, superFunc, xmlFile, key, ...)
superFunc(vehicle, xmlFile, key, ...)
if vehicle.getIsCpActive and vehicle:getIsCpActive() then
xmlFile:setValue(key .. "#isActive", false)
end
end
AIFieldWorker.saveToXMLFile = Utils.overwrittenFunction(AIFieldWorker.saveToXMLFile, saveToXMLFile)