467 | function CropSensor.getMaxWorkingWidth(sensorVehicle) |
468 | local childVehicles = sensorVehicle.rootVehicle.childVehicles |
469 | |
470 | local maxWidth = 0 |
471 | |
472 | for i=1, #childVehicles do |
473 | local childVehicle = childVehicles[i] |
474 | if SpecializationUtil.hasSpecialization(ExtendedSprayer, childVehicle.specializations) then |
475 | if childVehicle.getWorkAreaByIndex ~= nil then |
476 | local workAreas = childVehicle.spec_workArea.workAreas |
477 | for j=1, #workAreas do |
478 | local workArea = workAreas[j] |
479 | if workArea.start ~= nil and workArea.width ~= nil then |
480 | local width = calcDistanceFrom(workArea.start, workArea.width) |
481 | maxWidth = math.max(maxWidth, width) |
482 | |
483 | local x1, _, _ = localToLocal(workArea.start, sensorVehicle.rootNode, 0, 0, 0) |
484 | local x2, _, _ = localToLocal(workArea.width, sensorVehicle.rootNode, 0, 0, 0) |
485 | maxWidth = math.max(maxWidth, math.abs(x1) * 2, math.abs(x2) * 2) |
486 | end |
487 | end |
488 | end |
489 | |
490 | if childVehicle.getAIMarkers ~= nil then |
491 | local leftMarker, rightMarker, _, _ = childVehicle:getAIMarkers() |
492 | if leftMarker ~= nil and rightMarker ~= nil then |
493 | local width = calcDistanceFrom(leftMarker, rightMarker) |
494 | maxWidth = math.max(maxWidth, width) |
495 | |
496 | local x1, _, _ = localToLocal(leftMarker, sensorVehicle.rootNode, 0, 0, 0) |
497 | local x2, _, _ = localToLocal(rightMarker, sensorVehicle.rootNode, 0, 0, 0) |
498 | maxWidth = math.max(maxWidth, math.abs(x1) * 2, math.abs(x2) * 2) |
499 | end |
500 | end |
501 | end |
502 | end |
503 | |
504 | return maxWidth |
505 | end |
47 | function CropSensor.initSpecialization() |
48 | g_configurationManager:addConfigurationType("cropSensor", g_i18n:getText("configuration_cropSensor"), "cropSensor", nil, nil, nil, ConfigurationUtil.SELECTOR_MULTIOPTION) |
49 | |
50 | local schema = Vehicle.xmlSchema |
51 | schema:setXMLSpecializationType("CropSensor") |
52 | |
53 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cropSensor.sensorNode(?)#node", "Sensor Node") |
54 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cropSensor.sensorNode(?)#lightNode", "Real light source node") |
55 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cropSensor.sensorNode(?)#staticLight", "Static light shape") |
56 | schema:register(XMLValueType.FLOAT, "vehicle.cropSensor.sensorNode(?)#radius", "Sensor radius", 18) |
57 | schema:register(XMLValueType.BOOL, "vehicle.cropSensor.sensorNode(?)#requiresDaylight", "Sensor requires daylight to work", false) |
58 | schema:register(XMLValueType.STRING, "vehicle.cropSensor.sensorNode(?)#pattern", "Sensor pattern (OCTAGON | ?)", "OCTAGON") |
59 | |
60 | CropSensor.registerSensorLinkNodePaths(schema, "vehicle.cropSensor.cropSensorConfigurations.cropSensorConfiguration(?).sensorLinkNode(?)") |
61 | |
62 | schema:setXMLSpecializationType() |
63 | end |
409 | function CropSensor:linkCropSensor(linkData) |
410 | for i=1, #linkData.linkNodes do |
411 | local linkNodeData = linkData.linkNodes[i] |
412 | |
413 | local linkNode = linkNodeData.node |
414 | if linkNode == nil and linkNodeData.nodeName ~= nil then |
415 | if self.i3dMappings[linkNodeData.nodeName] ~= nil then |
416 | linkNode = self.i3dMappings[linkNodeData.nodeName].nodeId |
417 | end |
418 | end |
419 | |
420 | if linkNode ~= nil then |
421 | local sensorData = g_precisionFarming:getClonedCropSensorNode(linkNodeData.typeName) |
422 | if sensorData ~= nil then |
423 | link(linkNode, sensorData.node) |
424 | |
425 | setTranslation(sensorData.node, linkNodeData.translation[1], linkNodeData.translation[2], linkNodeData.translation[3]) |
426 | setRotation(sensorData.node, linkNodeData.rotation[1], linkNodeData.rotation[2], linkNodeData.rotation[3]) |
427 | |
428 | for j=1, #sensorData.rotationNodes do |
429 | local rotationNode = sensorData.rotationNodes[j] |
430 | |
431 | local autoRotate = false |
432 | if linkNodeData.rotationNodes[j] ~= nil then |
433 | local vRotationNode = linkNodeData.rotationNodes[j] |
434 | if rotationNode.autoRotate and vRotationNode.autoRotate ~= false and vRotationNode.rotation == nil then |
435 | autoRotate = true |
436 | elseif vRotationNode.rotation ~= nil then |
437 | setRotation(rotationNode.node, vRotationNode.rotation[1], vRotationNode.rotation[2], vRotationNode.rotation[3]) |
438 | end |
439 | else |
440 | autoRotate = rotationNode.autoRotate |
441 | end |
442 | |
443 | if autoRotate then |
444 | local rx, ry, rz = localRotationToLocal(self:getParentComponent(sensorData.node), getParent(rotationNode.node), 0, 0, 0) |
445 | setRotation(rotationNode.node, rx, ry, rz) |
446 | end |
447 | end |
448 | |
449 | if sensorData.measurementNode ~= nil then |
450 | local sensorNode = {} |
451 | sensorNode.node = sensorData.measurementNode |
452 | sensorNode.radius = 10 |
453 | sensorNode.origRadius = sensorNode.radius |
454 | sensorNode.pattern = linkNodeData.typeName == "SENSOR_LEFT" and CropSensor.PATTERN_CORNER_LEFT or CropSensor.PATTERN_CORNER_RIGHT |
455 | sensorNode.requiresDaylight = sensorData.requiresDaylight |
456 | sensorNode.index = 1 |
457 | |
458 | table.insert(self.spec_cropSensor.sensorNodes, sensorNode) |
459 | end |
460 | end |
461 | end |
462 | end |
463 | end |
98 | function CropSensor:onLoad(savegame) |
99 | self.spec_cropSensor = self["spec_" .. CropSensor.SPEC_NAME] |
100 | local spec = self.spec_cropSensor |
101 | |
102 | local baseName = "vehicle.cropSensor" |
103 | |
104 | spec.sensorNodes = {} |
105 | self.xmlFile:iterate(baseName .. ".sensorNode", function(index, key) |
106 | local sensorNode = {} |
107 | sensorNode.node = self.xmlFile:getValue(key .. "#node", nil, self.components, self.i3dMappings) |
108 | if sensorNode.node ~= nil then |
109 | sensorNode.radius = self.xmlFile:getValue(key .. "#radius", 20) |
110 | sensorNode.origRadius = sensorNode.radius |
111 | sensorNode.requiresDaylight = self.xmlFile:getValue(key .. "#requiresDaylight", false) |
112 | local patternStr = self.xmlFile:getValue(key .. "#pattern", "OCTAGON") |
113 | local pattern = CropSensor["PATTERN_" .. patternStr:upper()] |
114 | if pattern == nil then |
115 | Logging.xmlWarning(self.xmlFile, "Wrong pattern '%s' ground in '%s'", patternStr, key) |
116 | end |
117 | |
118 | sensorNode.lightNode = self.xmlFile:getValue(key .. "#lightNode", nil, self.components, self.i3dMappings) |
119 | if sensorNode.lightNode ~= nil then |
120 | setVisibility(sensorNode.lightNode, false) |
121 | end |
122 | |
123 | sensorNode.staticLight = self.xmlFile:getValue(key .. "#staticLight", nil, self.components, self.i3dMappings) |
124 | if sensorNode.staticLight ~= nil then |
125 | setShaderParameter(sensorNode.staticLight, "lightControl", 0, 0, 0, 0, false) |
126 | end |
127 | |
128 | sensorNode.pattern = pattern or CropSensor.PATTERN_OCTAGON |
129 | sensorNode.index = 1 |
130 | |
131 | table.insert(spec.sensorNodes, sensorNode) |
132 | end |
133 | end) |
134 | |
135 | spec.isStandaloneSensor = #spec.sensorNodes > 0 |
136 | spec.inputActionToggle = InputAction.PRECISIONFARMING_TOGGLE_CROP_SENSOR |
137 | |
138 | local configIndex = self.configurations["cropSensor"] |
139 | if configIndex ~= nil then |
140 | local configKey = string.format("vehicle.cropSensor.cropSensorConfigurations.cropSensorConfiguration(%d)", configIndex - 1) |
141 | |
142 | spec.sensorLinkNodeData = {} |
143 | spec.sensorLinkNodeData.linkNodes = {} |
144 | self.xmlFile:iterate(configKey .. ".sensorLinkNode", function(index, key) |
145 | local linkNode = {} |
146 | linkNode.node = self.xmlFile:getValue(key .. "#node", nil, self.components, self.i3dMappings) |
147 | if linkNode.node ~= nil then |
148 | linkNode.typeName = self.xmlFile:getValue(key .. "#type", "SENSOR_LEFT"):upper() |
149 | linkNode.translation = self.xmlFile:getValue(key .. "#translation", "0 0 0", true) |
150 | linkNode.rotation = self.xmlFile:getValue(key .. "#rotation", "0 0 0", true) |
151 | |
152 | linkNode.rotationNodes = {} |
153 | self.xmlFile:iterate(key .. ".rotationNode", function(_, rotationNodeKey) |
154 | local rotatioNode = {} |
155 | rotatioNode.autoRotate = self.xmlFile:getValue(rotationNodeKey .. "#autoRotate") |
156 | rotatioNode.rotation = self.xmlFile:getValue(rotationNodeKey .. "#rotation", nil, true) |
157 | |
158 | table.insert(linkNode.rotationNodes, rotatioNode) |
159 | end) |
160 | |
161 | table.insert(spec.sensorLinkNodeData.linkNodes, linkNode) |
162 | end |
163 | end) |
164 | |
165 | if #spec.sensorLinkNodeData.linkNodes > 0 then |
166 | self:linkCropSensor(spec.sensorLinkNodeData) |
167 | end |
168 | |
169 | if configIndex > 1 then |
170 | if g_precisionFarming ~= nil then |
171 | local linkData = g_precisionFarming:getCropSensorLinkageData(self.configFileName) |
172 | if linkData ~= nil then |
173 | self:linkCropSensor(linkData) |
174 | end |
175 | end |
176 | end |
177 | end |
178 | |
179 | spec.isAvailable = #spec.sensorNodes > 0 |
180 | spec.isActive = false |
181 | |
182 | spec.workingWidth = 0 |
183 | |
184 | if spec.isAvailable then |
185 | spec.texts = {} |
186 | spec.texts.toggleCropSensorPos = g_i18n:getText("action_toggleCropSensorPos", self.customEnvironment) |
187 | spec.texts.toggleCropSensorNeg = g_i18n:getText("action_toggleCropSensorNeg", self.customEnvironment) |
188 | spec.texts.warningSensorDaylight = g_i18n:getText("warning_sensorRequiresDaylight", self.customEnvironment) |
189 | |
190 | if g_precisionFarming ~= nil then |
191 | spec.soilMap = g_precisionFarming.soilMap |
192 | spec.coverMap = g_precisionFarming.coverMap |
193 | spec.nitrogenMap = g_precisionFarming.nitrogenMap |
194 | spec.farmlandStatistics = g_precisionFarming.farmlandStatistics |
195 | end |
196 | end |
197 | end |
257 | function CropSensor:onRootVehicleChanged(rootVehicle) |
258 | if self.isServer then |
259 | local spec = self.spec_cropSensor |
260 | if spec.isAvailable then |
261 | local actionController = rootVehicle.actionController |
262 | if actionController ~= nil then |
263 | if spec.controlledAction ~= nil then |
264 | spec.controlledAction:updateParent(actionController) |
265 | return |
266 | end |
267 | |
268 | spec.controlledAction = actionController:registerAction("cropSensorTurnOn", nil, 1) |
269 | spec.controlledAction:setCallback(self, CropSensor.actionControllerToggleEvent) |
270 | spec.controlledAction:setFinishedFunctions(self, function() return spec.isActive end, true, false) |
271 | spec.controlledAction:setIsSaved(true) |
272 | |
273 | spec.controlledAction:addAIEventListener(self, "onAIFieldWorkerStart", 1) |
274 | spec.controlledAction:addAIEventListener(self, "onAIFieldWorkerEnd", -1) |
275 | |
276 | spec.controlledAction:addAIEventListener(self, "onAIImplementStart", 1) |
277 | spec.controlledAction:addAIEventListener(self, "onAIImplementEnd", -1) |
278 | else |
279 | if spec.controlledAction ~= nil then |
280 | spec.controlledAction:remove() |
281 | end |
282 | end |
283 | end |
284 | end |
285 | end |
326 | function CropSensor:setCropSensorActive(state, noEventSend) |
327 | local spec = self.spec_cropSensor |
328 | if state == nil then |
329 | state = not spec.isActive |
330 | end |
331 | |
332 | if state ~= spec.isActive then |
333 | if self.isClient then |
334 | for i=1, #spec.sensorNodes do |
335 | local sensorNode = spec.sensorNodes[i] |
336 | if sensorNode.lightNode ~= nil then |
337 | setVisibility(sensorNode.lightNode, state) |
338 | end |
339 | if sensorNode.staticLight ~= nil then |
340 | setShaderParameter(sensorNode.staticLight, "lightControl", state and 0.2 or 0, 0, 0, 0, false) |
341 | end |
342 | end |
343 | end |
344 | |
345 | if state then |
346 | self:updateCropSensorWorkingWidth() |
347 | end |
348 | |
349 | spec.isActive = state |
350 | |
351 | CropSensor.updateActionEventTexts(self) |
352 | CropSensorStateEvent.sendEvent(self, state, noEventSend) |
353 | end |
354 | end |
386 | function CropSensor:updateSensorNode(sensorNode, radius, pattern, index) |
387 | local spec = self.spec_cropSensor |
388 | |
389 | if index > #pattern then |
390 | index = 1 |
391 | end |
392 | |
393 | for i=index, math.min(index+CropSensor.MAX_UPDATES_PER_FRAME-1, #pattern) do |
394 | local position = pattern[i] |
395 | local xs, _, zs = localToWorld(sensorNode, position[1] * radius, position[2] * radius, position[3] * radius) |
396 | local xw, _, zw = localToWorld(sensorNode, position[4] * radius, position[5] * radius, position[6] * radius) |
397 | local xh, _, zh = localToWorld(sensorNode, position[7] * radius, position[8] * radius, position[9] * radius) |
398 | |
399 | spec.nitrogenMap:updateCropSensorArea(xs, zs, xw, zw, xh, zh) |
400 | |
401 | index = i |
402 | end |
403 | |
404 | return index + 1 |
405 | end |