217 | function AIJobLoadAndDeliver:applyCurrentState(vehicle, mission, farmId, isDirectStart) |
218 | AIJobLoadAndDeliver:superClass().applyCurrentState(self, vehicle, mission, farmId, isDirectStart) |
219 | |
220 | self.vehicleParameter:setVehicle(vehicle) |
221 | self.loopingParameter:setIsLooping(true) |
222 | |
223 | if vehicle.getLastJob ~= nil then |
224 | local lastJob = vehicle:getLastJob() |
225 | if lastJob ~= nil and lastJob:isa(AIJobLoadAndDeliver) then |
226 | self.unloadingStationParameter:setUnloadingStation(lastJob.unloadingStationParameter:getUnloadingStation()) |
227 | self.loadingStationParameter:setLoadingStation(lastJob.loadingStationParameter:getLoadingStation()) |
228 | self.loopingParameter:setIsLooping(lastJob.loopingParameter:getIsLooping()) |
229 | end |
230 | end |
231 | |
232 | local unloadingStations = {} |
233 | for _, unloadingStation in pairs(g_currentMission.storageSystem:getUnloadingStations()) do |
234 | if g_currentMission.accessHandler:canPlayerAccess(unloadingStation) and unloadingStation:isa(UnloadingStation) then |
235 | local fillTypes = unloadingStation:getAISupportedFillTypes() |
236 | if next(fillTypes) ~= nil then |
237 | table.insert(unloadingStations, unloadingStation) |
238 | end |
239 | end |
240 | end |
241 | self.unloadingStationParameter:setValidUnloadingStations(unloadingStations) |
242 | |
243 | local loadingStations = {} |
244 | for _, loadingStation in pairs(g_currentMission.storageSystem:getLoadingStations()) do |
245 | if g_currentMission.accessHandler:canPlayerAccess(loadingStation) then |
246 | local fillTypes = loadingStation:getAISupportedFillTypes() |
247 | if next(fillTypes) ~= nil then |
248 | table.insert(loadingStations, loadingStation) |
249 | end |
250 | end |
251 | end |
252 | self.loadingStationParameter:setValidLoadingStations(loadingStations) |
253 | |
254 | local loadingStation = self.loadingStationParameter:getLoadingStation() |
255 | self:updateFillTypes(loadingStation) |
256 | end |
375 | function AIJobLoadAndDeliver:canContinueWork() |
376 | local vehicle = self.vehicleParameter:getVehicle() |
377 | if vehicle == nil then |
378 | return false, AIMessageErrorVehicleDeleted.new() |
379 | end |
380 | |
381 | local loadingStation = self.loadingStationParameter:getLoadingStation() |
382 | if loadingStation == nil then |
383 | return false, AIMessageErrorLoadingStationDeleted.new() |
384 | end |
385 | |
386 | local unloadingStation = self.unloadingStationParameter:getUnloadingStation() |
387 | if unloadingStation == nil then |
388 | return false, AIMessageErrorUnloadingStationDeleted.new() |
389 | end |
390 | |
391 | local fillTypeIndex = self.fillTypeParameter:getFillTypeIndex() |
392 | if unloadingStation:getFreeCapacity(fillTypeIndex, self.startedFarmId) <= 0 then |
393 | return false, AIMessageErrorUnloadingStationFull.new() |
394 | end |
395 | |
396 | if self.currentTaskIndex == self.loadingTask.taskIndex then |
397 | if loadingStation:getFillLevel(fillTypeIndex, self.startedFarmId) <= 0 then |
398 | local isEmpty = true |
399 | for _, loadingNodeInfo in ipairs(self.loadingNodeInfos) do |
400 | local loadingVehicle = loadingNodeInfo.vehicle |
401 | local fillUnitIndex = loadingNodeInfo.fillUnitIndex |
402 | if loadingVehicle:getFillUnitFillLevel(fillUnitIndex) > 0 and loadingVehicle:getFillUnitFillType(fillUnitIndex) == fillTypeIndex then |
403 | isEmpty = false |
404 | break |
405 | end |
406 | end |
407 | |
408 | if isEmpty then |
409 | return false, AIMessageSuccessSiloEmpty.new() |
410 | end |
411 | end |
412 | end |
413 | |
414 | return true, nil |
415 | end |
462 | function AIJobLoadAndDeliver:getIsAvailableForVehicle(vehicle) |
463 | if vehicle.createAgent == nil or vehicle.setAITarget == nil or not vehicle:getCanStartAIVehicle() then |
464 | return false |
465 | end |
466 | |
467 | local vehicles = vehicle:getChildVehicles() |
468 | for _, childVehicle in ipairs(vehicles) do |
469 | if childVehicle.getAIDischargeNodes ~= nil then |
470 | local nodes = childVehicle:getAIDischargeNodes() |
471 | if next(nodes) ~= nil then |
472 | return true |
473 | end |
474 | end |
475 | end |
476 | |
477 | local foundDischargeNodes = false |
478 | if vehicle.getAIDischargeNodes ~= nil then |
479 | local nodes = vehicle:getAIDischargeNodes() |
480 | if next(nodes) ~= nil then |
481 | foundDischargeNodes = true |
482 | end |
483 | end |
484 | |
485 | if not foundDischargeNodes then |
486 | vehicles = vehicle:getChildVehicles() |
487 | for _, childVehicle in ipairs(vehicles) do |
488 | if childVehicle.getAIDischargeNodes ~= nil then |
489 | local nodes = childVehicle:getAIDischargeNodes() |
490 | if next(nodes) ~= nil then |
491 | foundDischargeNodes = true |
492 | break |
493 | end |
494 | end |
495 | end |
496 | end |
497 | |
498 | if not foundDischargeNodes then |
499 | return false |
500 | end |
501 | |
502 | local foundLoadingNodes = false |
503 | if vehicle.getAIFillUnits ~= nil then |
504 | local fillUnits = vehicle:getAIFillUnits() |
505 | if next(fillUnits) ~= nil then |
506 | foundLoadingNodes = true |
507 | end |
508 | end |
509 | |
510 | if not foundLoadingNodes then |
511 | vehicles = vehicle:getChildVehicles() |
512 | for _, childVehicle in ipairs(vehicles) do |
513 | if childVehicle.getAIFillUnits ~= nil then |
514 | local fillUnits = childVehicle:getAIFillUnits() |
515 | if next(fillUnits) ~= nil then |
516 | foundLoadingNodes = true |
517 | break |
518 | end |
519 | end |
520 | end |
521 | end |
522 | |
523 | if not foundLoadingNodes then |
524 | return false |
525 | end |
526 | |
527 | return true |
528 | end |
419 | function AIJobLoadAndDeliver:getNextTaskIndex(isSkipTask) |
420 | if self.currentTaskIndex == self.driveToLoadingTask.taskIndex or self.currentTaskIndex == self.loadingTask.taskIndex then |
421 | -- check if there are more loadingNodes that need loading |
422 | for _, loadingNodeInfo in ipairs(self.loadingNodeInfos) do |
423 | if loadingNodeInfo.isDirty then |
424 | local vehicle = loadingNodeInfo.vehicle |
425 | local fillUnitIndex = loadingNodeInfo.fillUnitIndex |
426 | if vehicle:getFillUnitFillLevel(fillUnitIndex) == 0 then |
427 | self.loadingTask:setFillUnit(vehicle, fillUnitIndex, loadingNodeInfo.offsetZ) |
428 | loadingNodeInfo.isDirty = false |
429 | --#debug log("drive to loading next fillunit") |
430 | return self.loadingTask.taskIndex |
431 | end |
432 | |
433 | loadingNodeInfo.isDirty = false |
434 | end |
435 | end |
436 | |
437 | elseif self.currentTaskIndex == self.driveToUnloadingTask.taskIndex or self.currentTaskIndex == self.dischargeTask.taskIndex then |
438 | local fillTypeIndex = self.fillTypeParameter:getFillTypeIndex() |
439 | for _, dischargeNodeInfo in ipairs(self.dischargeNodeInfos) do |
440 | if dischargeNodeInfo.isDirty then |
441 | local vehicle = dischargeNodeInfo.vehicle |
442 | local fillUnitIndex = dischargeNodeInfo.dischargeNode.fillUnitIndex |
443 | if vehicle:getFillUnitFillLevel(fillUnitIndex) > 1 and vehicle:getFillUnitFillType(fillUnitIndex) == fillTypeIndex then |
444 | self.dischargeTask:setDischargeNode(vehicle, dischargeNodeInfo.dischargeNode, dischargeNodeInfo.offsetZ) |
445 | dischargeNodeInfo.isDirty = false |
446 | --#debug log("drive to discharge next trailer") |
447 | return self.dischargeTask.taskIndex |
448 | end |
449 | |
450 | dischargeNodeInfo.isDirty = false |
451 | end |
452 | end |
453 | end |
454 | |
455 | local nextTaskIndex = AIJobDeliver:superClass().getNextTaskIndex(self, isSkipTask) |
456 | --#debug log("AIJobLoadAndDeliver:getNextTaskIndex(): ", self.currentTaskIndex, "->", nextTaskIndex) |
457 | return nextTaskIndex |
458 | end |
22 | function AIJobLoadAndDeliver.new(isServer, customMt) |
23 | local self = AIJob.new(isServer, customMt or AIJobLoadAndDeliver_mt) |
24 | |
25 | self.dischargeNodeInfos = {} |
26 | self.loadingNodeInfos = {} |
27 | |
28 | self.driveToLoadingTask = AITaskDriveTo.new(isServer, self) |
29 | self.loadingTask = AITaskLoading.new(isServer, self) |
30 | self.driveToUnloadingTask = AITaskDriveTo.new(isServer, self) |
31 | self.dischargeTask = AITaskDischarge.new(isServer, self) |
32 | |
33 | self:addTask(self.driveToLoadingTask) |
34 | self:addTask(self.loadingTask) |
35 | self:addTask(self.driveToUnloadingTask) |
36 | self:addTask(self.dischargeTask) |
37 | |
38 | self.vehicleParameter = AIParameterVehicle.new() |
39 | self.unloadingStationParameter = AIParameterUnloadingStation.new() |
40 | self.loadingStationParameter = AIParameterLoadingStation.new() |
41 | self.fillTypeParameter = AIParameterFillType.new() |
42 | self.loopingParameter = AIParameterLooping.new() |
43 | |
44 | self:addNamedParameter("vehicle", self.vehicleParameter) |
45 | self:addNamedParameter("loadingStation", self.loadingStationParameter) |
46 | self:addNamedParameter("fillType", self.fillTypeParameter) |
47 | self:addNamedParameter("unloadingStation", self.unloadingStationParameter) |
48 | self:addNamedParameter("looping", self.loopingParameter) |
49 | |
50 | local vehicleGroup = AIParameterGroup.new(g_i18n:getText("ai_parameterGroupTitleVehicle")) |
51 | vehicleGroup:addParameter(self.vehicleParameter) |
52 | |
53 | local loadTargetGroup = AIParameterGroup.new(g_i18n:getText("ai_parameterGroupTitleLoadingStation")) |
54 | loadTargetGroup:addParameter(self.loadingStationParameter) |
55 | loadTargetGroup:addParameter(self.fillTypeParameter) |
56 | |
57 | local unloadTargetGroup = AIParameterGroup.new(g_i18n:getText("ai_parameterGroupTitleUnloadingStation")) |
58 | unloadTargetGroup:addParameter(self.unloadingStationParameter) |
59 | |
60 | local loopingGroup = AIParameterGroup.new(g_i18n:getText("ai_parameterGroupTitleLooping")) |
61 | loopingGroup:addParameter(self.loopingParameter) |
62 | |
63 | table.insert(self.groupedParameters, vehicleGroup) |
64 | table.insert(self.groupedParameters, loadTargetGroup) |
65 | table.insert(self.groupedParameters, unloadTargetGroup) |
66 | table.insert(self.groupedParameters, loopingGroup) |
67 | |
68 | return self |
69 | end |
73 | function AIJobLoadAndDeliver:setValues() |
74 | self:resetTasks() |
75 | |
76 | local vehicle = self.vehicleParameter:getVehicle() |
77 | if vehicle == nil then |
78 | return |
79 | end |
80 | local loadingStation = self.loadingStationParameter:getLoadingStation() |
81 | if loadingStation == nil then |
82 | return |
83 | end |
84 | local unloadingStation = self.unloadingStationParameter:getUnloadingStation() |
85 | if unloadingStation == nil then |
86 | return |
87 | end |
88 | local fillTypeIndex = self.fillTypeParameter:getFillTypeIndex() |
89 | |
90 | self.loadingTask:setVehicle(vehicle) |
91 | self.driveToUnloadingTask:setVehicle(vehicle) |
92 | self.driveToLoadingTask:setVehicle(vehicle) |
93 | self.dischargeTask:setVehicle(vehicle) |
94 | |
95 | self.loadingNodeInfos = {} |
96 | self.dischargeNodeInfos = {} |
97 | |
98 | if vehicle.getAIFillUnits ~= nil then |
99 | for _, fillUnit in ipairs(vehicle:getAIFillUnits()) do |
100 | local fillUnitIndex = fillUnit.fillUnitIndex |
101 | local _, _, z = vehicle:getAILoadingNodeZAlignedOffset(fillUnitIndex, vehicle) |
102 | table.insert(self.loadingNodeInfos, {vehicle=vehicle, fillUnitIndex=fillUnitIndex, offsetZ=z, isDirty=true}) |
103 | end |
104 | end |
105 | |
106 | if vehicle.getAIDischargeNodes ~= nil then |
107 | for _, dischargeNode in ipairs(vehicle:getAIDischargeNodes()) do |
108 | local _, _, z = vehicle:getAIDischargeNodeZAlignedOffset(dischargeNode, vehicle) |
109 | table.insert(self.dischargeNodeInfos, {vehicle=vehicle, dischargeNode=dischargeNode, offsetZ=z, isDirty=true}) |
110 | end |
111 | end |
112 | |
113 | local childVehicles = vehicle:getChildVehicles() |
114 | for _, childVehicle in ipairs(childVehicles) do |
115 | if childVehicle.getAIDischargeNodes ~= nil then |
116 | for _, dischargeNode in ipairs(childVehicle:getAIDischargeNodes()) do |
117 | local _, _, z = childVehicle:getAIDischargeNodeZAlignedOffset(dischargeNode, vehicle) |
118 | table.insert(self.dischargeNodeInfos, {vehicle=childVehicle, dischargeNode=dischargeNode, offsetZ=z, isDirty=true}) |
119 | end |
120 | end |
121 | |
122 | if childVehicle.getAIFillUnits ~= nil then |
123 | for _, fillUnit in ipairs(childVehicle:getAIFillUnits()) do |
124 | local fillUnitIndex = fillUnit.fillUnitIndex |
125 | local _, _, z = childVehicle:getAILoadingNodeZAlignedOffset(fillUnitIndex, vehicle) |
126 | table.insert(self.loadingNodeInfos, {vehicle=childVehicle, fillUnitIndex=fillUnitIndex, offsetZ=z, isDirty=true}) |
127 | end |
128 | end |
129 | end |
130 | |
131 | table.sort(self.dischargeNodeInfos, function(a, b) |
132 | return a.offsetZ > b.offsetZ |
133 | end) |
134 | |
135 | table.sort(self.loadingNodeInfos, function(a, b) |
136 | return a.offsetZ > b.offsetZ |
137 | end) |
138 | |
139 | local maxDischargeOffset = 0 |
140 | if #self.dischargeNodeInfos > 0 then |
141 | maxDischargeOffset = self.dischargeNodeInfos[#self.dischargeNodeInfos].offsetZ |
142 | end |
143 | self.driveToUnloadingTask:setTargetOffset(-maxDischargeOffset) |
144 | |
145 | local maxLoadingOffset = 0 |
146 | if #self.loadingNodeInfos > 0 then |
147 | maxLoadingOffset = self.loadingNodeInfos[#self.loadingNodeInfos].offsetZ |
148 | end |
149 | self.driveToLoadingTask:setTargetOffset(-maxLoadingOffset) |
150 | |
151 | if fillTypeIndex ~= nil then |
152 | if loadingStation ~= nil then |
153 | local x, z, dirX, dirZ, trigger = loadingStation:getAITargetPositionAndDirection(fillTypeIndex) |
154 | if trigger ~= nil then |
155 | self.driveToLoadingTask:setTargetPosition(x, z) |
156 | self.driveToLoadingTask:setTargetDirection(dirX, dirZ) |
157 | self.loadingTask:setLoadTrigger(trigger) |
158 | end |
159 | end |
160 | if unloadingStation ~= nil then |
161 | local x, z, dirX, dirZ, trigger = unloadingStation:getAITargetPositionAndDirection(fillTypeIndex) |
162 | if trigger ~= nil then |
163 | self.driveToUnloadingTask:setTargetPosition(x, z) |
164 | self.driveToUnloadingTask:setTargetDirection(dirX, dirZ) |
165 | self.dischargeTask:setUnloadTrigger(trigger) |
166 | end |
167 | end |
168 | |
169 | self.loadingTask:setFillType(fillTypeIndex) |
170 | end |
171 | end |
175 | function AIJobLoadAndDeliver:validate(farmId) |
176 | self:setParamterValid(true) |
177 | |
178 | local isVehicleValid, vehicleErrorMessage = self.vehicleParameter:validate() |
179 | if isVehicleValid then |
180 | if #self.dischargeNodeInfos == 0 then |
181 | isVehicleValid = false |
182 | vehicleErrorMessage = g_i18n:getText("ai_validationErrorNoAIDischargeNodesFound") |
183 | elseif #self.loadingNodeInfos == 0 then |
184 | isVehicleValid = false |
185 | vehicleErrorMessage = g_i18n:getText("ai_validationErrorNoAILoadingNodesFound") |
186 | end |
187 | end |
188 | if not isVehicleValid then |
189 | self.vehicleParameter:setIsValid(false) |
190 | end |
191 | |
192 | local isFillTypeValid, fillTypeErrorMessage = self.fillTypeParameter:validate() |
193 | if not isFillTypeValid then |
194 | self.fillTypeParameter:setIsValid(false) |
195 | end |
196 | |
197 | local fillTypeIndex = self.fillTypeParameter:getFillTypeIndex() |
198 | |
199 | local isLoadingStationValid, loadingStationErrorMessage = self.loadingStationParameter:validate(fillTypeIndex, farmId) |
200 | if not isLoadingStationValid then |
201 | self.loadingStationParameter:setIsValid(false) |
202 | end |
203 | |
204 | local isUnloadingStationValid, unloadingStationErrorMessage = self.unloadingStationParameter:validate(fillTypeIndex, farmId) |
205 | if not isUnloadingStationValid then |
206 | self.unloadingStationParameter:setIsValid(false) |
207 | end |
208 | |
209 | local isValid = isVehicleValid and isFillTypeValid and isLoadingStationValid and isUnloadingStationValid |
210 | local errorMessage = vehicleErrorMessage or fillTypeErrorMessage or loadingStationErrorMessage or unloadingStationErrorMessage |
211 | |
212 | return isValid, errorMessage |
213 | end |