32 | function WeedSpotSpray.initSpecialization() |
33 | g_configurationManager:addConfigurationType("weedSpotSpray", g_i18n:getText("configuration_weedSpotSpray"), "weedSpotSpray", nil, nil, nil, ConfigurationUtil.SELECTOR_MULTIOPTION) |
34 | |
35 | local schema = Vehicle.xmlSchema |
36 | schema:setXMLSpecializationType("WeedSpotSpray") |
37 | |
38 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.weedSpotSpray.weedSpotSprayConfigurations.weedSpotSprayConfiguration(?)") |
39 | |
40 | schema:register(XMLValueType.TIME, "vehicle.weedSpotSpray#effectFadeTime", "Time the effect needs to fade in an out", 0.25) |
41 | |
42 | schema:register(XMLValueType.INT, "vehicle.weedSpotSpray.nozzles(?)#foldingConfigurationIndex", "Folding configuration index to use these nozzles", 1) |
43 | schema:register(XMLValueType.NODE_INDEX, "vehicle.weedSpotSpray.nozzles(?).nozzle(?)#node", "Nozzle Node") |
44 | schema:register(XMLValueType.INT, "vehicle.weedSpotSpray.nozzles(?).nozzle(?)#sectionIndex", "Index of corresponding section") |
45 | schema:register(XMLValueType.FLOAT, "vehicle.weedSpotSpray.nozzles(?).nozzle(?)#zOffset", "Offset in Z direction for detection [m]", 0.5) |
46 | |
47 | schema:setXMLSpecializationType() |
48 | end |
239 | function WeedSpotSpray:onEndWorkAreaProcessing(dt) |
240 | if self:getLastSpeed() > 0.5 then |
241 | local specSprayer = self.spec_sprayer |
242 | if self.isServer then |
243 | if specSprayer.workAreaParameters.isActive then |
244 | local sprayFillType = specSprayer.workAreaParameters.sprayFillType |
245 | if sprayFillType == FillType.HERBICIDE then |
246 | local sprayVehicle = specSprayer.workAreaParameters.sprayVehicle |
247 | local usage = specSprayer.workAreaParameters.usage |
248 | if sprayVehicle ~= nil or self:getIsAIActive() then |
249 | local farmlandStatistics, _, farmlandId = self:getPFStatisticInfo() |
250 | if farmlandStatistics ~= nil then |
251 | if farmlandId ~= nil then |
252 | farmlandStatistics:updateStatistic(farmlandId, "usedHerbicide", usage) |
253 | farmlandStatistics:updateStatistic(farmlandId, "usedHerbicideRegular", self.spec_weedSpotSpray.lastRegularUsage) |
254 | end |
255 | end |
256 | end |
257 | end |
258 | end |
259 | end |
260 | end |
261 | end |
76 | function WeedSpotSpray:onLoad(savegame) |
77 | self.spec_weedSpotSpray = self["spec_" .. WeedSpotSpray.SPEC_NAME] |
78 | local spec = self.spec_weedSpotSpray |
79 | |
80 | local baseName = "vehicle.weedSpotSpray" |
81 | |
82 | local weedSpotSprayConfigurationId = Utils.getNoNil(self.configurations["weedSpotSpray"], 1) |
83 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, baseName .. ".weedSpotSprayConfigurations.weedSpotSprayConfiguration", weedSpotSprayConfigurationId, self.components, self) |
84 | |
85 | spec.effectFadeTime = self.xmlFile:getValue(baseName .. "#effectFadeTime", 0.25) |
86 | |
87 | spec.nozzleNodes = {} |
88 | spec.nozzleNodesToDelete = {} |
89 | self.xmlFile:iterate(baseName .. ".nozzles", function(index, nozzlesKey) |
90 | local foldingConfigIndex = self.xmlFile:getValue(nozzlesKey .. "#foldingConfigurationIndex", 1) |
91 | |
92 | self.xmlFile:iterate(nozzlesKey .. ".nozzle", function(_, key) |
93 | local entry = {} |
94 | entry.node = self.xmlFile:getValue(key .. "#node", nil, self.components, self.i3dMappings) |
95 | if entry.node ~= nil then |
96 | if foldingConfigIndex == self.configurations["folding"] or (self.configurations["folding"] == nil and foldingConfigIndex == 1) then |
97 | entry.sectionIndex = self.xmlFile:getValue(key .. "#sectionIndex") |
98 | entry.zOffset = self.xmlFile:getValue(key .. "#zOffset", 0.5) |
99 | |
100 | entry.effect = {} |
101 | |
102 | if g_precisionFarming ~= nil then |
103 | spec.extendedWeedControl = g_precisionFarming.extendedWeedControl |
104 | |
105 | local effectNode = spec.extendedWeedControl:getClonedSprayerEffectNode() |
106 | if effectNode ~= nil then |
107 | link(entry.node, effectNode) |
108 | setTranslation(effectNode, 0, 0, 0) |
109 | setRotation(effectNode, 0, 0, 0) |
110 | |
111 | local material = g_materialManager:getMaterial(FillType.LIQUIDFERTILIZER, "sprayer", 1) |
112 | if material ~= nil then |
113 | setMaterial(effectNode, material, 0) |
114 | end |
115 | |
116 | entry.effect.node = effectNode |
117 | entry.effect.fadeCur = {-1, 1} |
118 | entry.effect.fadeDir = WeedSpotSpray.EFFECT_DIRECTION_OFF |
119 | entry.effect.state = ShaderPlaneEffect.STATE_OFF |
120 | |
121 | setShaderParameter(entry.effect.node, "fadeProgress", entry.effect.fadeCur[1], entry.effect.fadeCur[2], 0.0, 0.0, false) |
122 | setShaderParameter(entry.effect.node, "offsetUV", math.random(), math.random(), 0.0, 0.0, false) |
123 | end |
124 | end |
125 | |
126 | entry.lastActiveTime = -10000 |
127 | entry.isActive = false |
128 | entry.lastIsActive = false |
129 | |
130 | table.insert(spec.nozzleNodes, entry) |
131 | else |
132 | spec.nozzleNodesToDelete[entry.node] = true |
133 | end |
134 | end |
135 | end) |
136 | end) |
137 | |
138 | for i=1, #spec.nozzleNodes do |
139 | spec.nozzleNodesToDelete[spec.nozzleNodes[i].node] = nil |
140 | end |
141 | |
142 | for i=1, #spec.nozzleNodesToDelete do |
143 | if entityExists(spec.nozzleNodesToDelete[i]) then |
144 | delete(spec.nozzleNodesToDelete[i]) |
145 | end |
146 | end |
147 | spec.nozzleNodesToDelete = {} |
148 | |
149 | spec.nozzleUpdateFrameDelay = math.ceil(#spec.nozzleNodes / WeedSpotSpray.NOZZLE_UPDATES_PER_FRAME) |
150 | |
151 | spec.isAvailable = #spec.nozzleNodes > 0 |
152 | spec.isEnabled = weedSpotSprayConfigurationId > 1 |
153 | spec.currentUpdateIndex = 1 |
154 | spec.effectsDirty = false |
155 | |
156 | spec.lastRegularUsage = 0 |
157 | |
158 | spec.weedDetectionStates = {} |
159 | local weedSystem = g_currentMission.weedSystem |
160 | local replacementData = weedSystem:getHerbicideReplacements() |
161 | if replacementData.weed ~= nil then |
162 | for sourceState, targetState in pairs(replacementData.weed.replacements) do |
163 | if targetState ~= 0 then -- ignore preventative spraying |
164 | spec.weedDetectionStates[sourceState] = true |
165 | end |
166 | end |
167 | end |
168 | |
169 | spec.weedMapId, spec.weedFirstChannel, spec.weedNumChannels = g_currentMission.weedSystem:getDensityMapData() |
170 | spec.groundTypeMapId, spec.groundTypeFirstChannel, spec.groundTypeNumChannels = g_currentMission.fieldGroundSystem:getDensityMapData(FieldDensityMap.GROUND_TYPE) |
171 | spec.sprayTypeMapId, spec.sprayTypeFirstChannel, spec.sprayTypeNumChannels = g_currentMission.fieldGroundSystem:getDensityMapData(FieldDensityMap.SPRAY_TYPE) |
172 | end |
179 | function WeedSpotSpray:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
180 | local spec = self.spec_weedSpotSpray |
181 | if spec.isAvailable then |
182 | local sprayFillType = self.spec_sprayer.workAreaParameters.sprayFillType |
183 | if spec.isEnabled then |
184 | if self:getIsTurnedOn() then |
185 | for i=1, WeedSpotSpray.NOZZLE_UPDATES_PER_FRAME do |
186 | local nozzleNode = spec.nozzleNodes[spec.currentUpdateIndex] |
187 | if sprayFillType == FillType.HERBICIDE then |
188 | local x, y, z = localToWorld(nozzleNode.node, 0, 0, nozzleNode.zOffset) |
189 | local densityBits = getDensityAtWorldPos(spec.weedMapId, x, y, z) |
190 | local weedState = bitAND(bitShiftRight(densityBits, spec.weedFirstChannel), 2 ^ spec.weedNumChannels - 1) |
191 | |
192 | if spec.weedDetectionStates[weedState] then |
193 | nozzleNode.lastActiveTime = g_time + spec.nozzleUpdateFrameDelay * dt * 1.5 |
194 | spec.effectsDirty = true |
195 | end |
196 | else |
197 | local x, y, z = localToWorld(nozzleNode.node, 0, 0, nozzleNode.zOffset * 2) |
198 | local densityBits = getDensityAtWorldPos(spec.sprayTypeMapId, x, y, z) |
199 | local sprayType = bitAND(bitShiftRight(densityBits, spec.sprayTypeFirstChannel), 2 ^ spec.sprayTypeNumChannels - 1) |
200 | |
201 | local densityBitsGround = getDensityAtWorldPos(spec.groundTypeMapId, x, y, z) |
202 | local groundType = bitAND(bitShiftRight(densityBitsGround, spec.groundTypeFirstChannel), 2 ^ spec.groundTypeNumChannels - 1) |
203 | |
204 | if groundType ~= 0 and sprayType ~= FieldSprayType.FERTILIZER then |
205 | nozzleNode.lastActiveTime = g_time + spec.nozzleUpdateFrameDelay * dt * 1.5 |
206 | spec.effectsDirty = true |
207 | end |
208 | end |
209 | |
210 | spec.currentUpdateIndex = spec.currentUpdateIndex + 1 |
211 | if spec.currentUpdateIndex > #spec.nozzleNodes then |
212 | spec.currentUpdateIndex = 1 |
213 | end |
214 | end |
215 | end |
216 | |
217 | self:updateNozzleEffects(dt, false) |
218 | else |
219 | self:updateNozzleEffects(dt, true, sprayFillType == FillType.UNKNOWN) |
220 | end |
221 | end |
222 | end |
317 | function WeedSpotSpray:updateNozzleEffects(dt, useSectionState, forceTurnOff) |
318 | local spec = self.spec_weedSpotSpray |
319 | |
320 | local sections |
321 | if self.spec_variableWorkWidth ~= nil then |
322 | sections = self.spec_variableWorkWidth.sections |
323 | end |
324 | |
325 | local speed = self:getLastSpeed() |
326 | local keepEffectsDirty = false |
327 | for i=1, #spec.nozzleNodes do |
328 | local nozzleNode = spec.nozzleNodes[i] |
329 | local fadeSpeedScale = 1 |
330 | |
331 | if not useSectionState then |
332 | nozzleNode.isActive = (g_time - nozzleNode.lastActiveTime) < 0 and speed > 0.5 |
333 | else |
334 | if sections ~= nil and nozzleNode.sectionIndex ~= nil and sections[nozzleNode.sectionIndex] ~= nil then |
335 | local section = sections[nozzleNode.sectionIndex] |
336 | nozzleNode.isActive = section.isActive |
337 | else |
338 | nozzleNode.isActive = true |
339 | end |
340 | |
341 | nozzleNode.isActive = nozzleNode.isActive and Sprayer.getAreEffectsVisible(self) |
342 | fadeSpeedScale = 3 |
343 | end |
344 | nozzleNode.isActive = nozzleNode.isActive and not forceTurnOff |
345 | |
346 | if nozzleNode.lastIsActive ~= nozzleNode.isActive then |
347 | spec.effectsDirty = true |
348 | end |
349 | |
350 | if spec.effectsDirty then |
351 | local effect = nozzleNode.effect |
352 | if effect.node ~= nil then |
353 | if nozzleNode.lastIsActive ~= nozzleNode.isActive then |
354 | if nozzleNode.isActive then |
355 | if effect.state ~= ShaderPlaneEffect.STATE_ON and effect.state ~= ShaderPlaneEffect.STATE_TURNING_ON then |
356 | effect.state = ShaderPlaneEffect.STATE_TURNING_ON |
357 | effect.fadeDir = WeedSpotSpray.EFFECT_DIRECTION_START |
358 | effect.fadeCur[1] = -1 |
359 | effect.fadeCur[2] = 1 |
360 | end |
361 | else |
362 | if effect.state ~= ShaderPlaneEffect.STATE_OFF and effect.state ~= ShaderPlaneEffect.STATE_TURNING_OFF then |
363 | effect.state = ShaderPlaneEffect.STATE_TURNING_OFF |
364 | effect.fadeDir = WeedSpotSpray.EFFECT_DIRECTION_STOP |
365 | end |
366 | end |
367 | end |
368 | |
369 | if effect.state == ShaderPlaneEffect.STATE_TURNING_OFF or effect.state == ShaderPlaneEffect.STATE_TURNING_ON then |
370 | effect.fadeCur[1] = math.max(math.min(effect.fadeCur[1] + effect.fadeDir[1] * (dt / (spec.effectFadeTime * fadeSpeedScale)), 1), -1) |
371 | effect.fadeCur[2] = math.max(math.min(effect.fadeCur[2] + effect.fadeDir[2] * (dt / (spec.effectFadeTime * fadeSpeedScale)), 1), -1) |
372 | |
373 | setShaderParameter(effect.node, "fadeProgress", effect.fadeCur[1], effect.fadeCur[2], 0.0, 0.0, false) |
374 | |
375 | if effect.state == ShaderPlaneEffect.STATE_TURNING_OFF then |
376 | if effect.fadeCur[1] == 1 and effect.fadeCur[2] == -1 then |
377 | effect.state = ShaderPlaneEffect.STATE_OFF |
378 | end |
379 | elseif effect.state == ShaderPlaneEffect.STATE_TURNING_ON then |
380 | if effect.fadeCur[1] == 1 and effect.fadeCur[2] == 1 then |
381 | effect.state = ShaderPlaneEffect.STATE_ON |
382 | end |
383 | end |
384 | |
385 | keepEffectsDirty = true |
386 | end |
387 | end |
388 | end |
389 | |
390 | nozzleNode.lastIsActive = nozzleNode.isActive |
391 | end |
392 | |
393 | if spec.effectsDirty and not keepEffectsDirty then |
394 | spec.effectsDirty = false |
395 | end |
396 | end |