LUADOC - Farming Simulator 22

Script v1_7_1_0

Engine v1_7_1_0

Foundation Reference

WeedSpotSpray

Description
Specialization for spot spraying of weeds
Functions

getAreEffectsVisible

Description
Definition
getAreEffectsVisible()
Code
265function WeedSpotSpray:getAreEffectsVisible(superFunc)
266 -- effects are fully overwritten by the nozzle effects
267 if self.spec_weedSpotSpray.isAvailable then
268 return false
269 end
270
271 return superFunc(self)
272end

getIsSpotSprayEnabled

Description
Definition
getIsSpotSprayEnabled()
Code
310function WeedSpotSpray:getIsSpotSprayEnabled()
311 local spec = self.spec_weedSpotSpray
312 return spec.isAvailable and spec.isEnabled
313end

getSpotSprayCoverage

Description
Definition
getSpotSprayCoverage()
Code
291function WeedSpotSpray:getSpotSprayCoverage()
292 local spec = self.spec_weedSpotSpray
293 if spec.isAvailable and spec.isEnabled then
294 local numActiveNozzles = 0
295 for i=1, #spec.nozzleNodes do
296 if spec.nozzleNodes[i].isActive then
297 numActiveNozzles = numActiveNozzles + 1
298 end
299 end
300
301 return numActiveNozzles / #spec.nozzleNodes
302 end
303
304 return 1
305end

getSprayerUsage

Description
Definition
getSprayerUsage()
Code
276function WeedSpotSpray:getSprayerUsage(superFunc, fillType, dt)
277 local spec = self.spec_weedSpotSpray
278 if spec.isAvailable and spec.isEnabled then
279 local usage = superFunc(self, fillType, dt)
280 spec.lastRegularUsage = usage
281 return usage * self:getSpotSprayCoverage()
282 else
283 local usage = superFunc(self, fillType, dt)
284 spec.lastRegularUsage = usage
285 return usage
286 end
287end

initSpecialization

Description
Definition
initSpecialization()
Code
32function 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()
48end

onEndWorkAreaProcessing

Description
Definition
onEndWorkAreaProcessing()
Code
239function 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
261end

onLoad

Description
Definition
onLoad()
Code
76function 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)
172end

onTurnedOff

Description
Definition
onTurnedOff()
Code
226function WeedSpotSpray:onTurnedOff()
227 local spec = self.spec_weedSpotSpray
228 for i=1, #spec.nozzleNodes do
229 local nozzleNode = spec.nozzleNodes[i]
230 nozzleNode.isActive = false
231 nozzleNode.lastActiveTime = -1000
232 end
233
234 spec.effectsDirty = true
235end

onUpdate

Description
Called on update
Definition
onUpdate(float dt, boolean isActiveForInput, boolean isSelected)
Arguments
floatdttime since last call in ms
booleanisActiveForInputtrue if vehicle is active for input
booleanisSelectedtrue if vehicle is selected
Code
179function 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
222end

prerequisitesPresent

Description
Checks if all prerequisite specializations are loaded
Definition
prerequisitesPresent(table specializations)
Arguments
tablespecializationsspecializations
Return Values
booleanhasPrerequisitetrue if all prerequisite specializations are loaded
Code
26function WeedSpotSpray.prerequisitesPresent(specializations)
27 return SpecializationUtil.hasSpecialization(Sprayer, specializations) and SpecializationUtil.hasSpecialization(PrecisionFarmingStatistic, specializations)
28end

registerEventListeners

Description
Definition
registerEventListeners()
Code
67function WeedSpotSpray.registerEventListeners(vehicleType)
68 SpecializationUtil.registerEventListener(vehicleType, "onLoad", WeedSpotSpray)
69 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", WeedSpotSpray)
70 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOff", WeedSpotSpray)
71 SpecializationUtil.registerEventListener(vehicleType, "onEndWorkAreaProcessing", WeedSpotSpray)
72end

registerFunctions

Description
Definition
registerFunctions()
Code
52function WeedSpotSpray.registerFunctions(vehicleType)
53 SpecializationUtil.registerFunction(vehicleType, "getSpotSprayCoverage", WeedSpotSpray.getSpotSprayCoverage)
54 SpecializationUtil.registerFunction(vehicleType, "getIsSpotSprayEnabled", WeedSpotSpray.getIsSpotSprayEnabled)
55 SpecializationUtil.registerFunction(vehicleType, "updateNozzleEffects", WeedSpotSpray.updateNozzleEffects)
56end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
60function WeedSpotSpray.registerOverwrittenFunctions(vehicleType)
61 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAreEffectsVisible", WeedSpotSpray.getAreEffectsVisible)
62 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getSprayerUsage", WeedSpotSpray.getSprayerUsage)
63end

updateNozzleEffects

Description
Definition
updateNozzleEffects()
Code
317function 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
396end