LUADOC - Farming Simulator 17

Printable Version

Script v1.4.4.0

Engine v7.0.0.2

Foundation Reference

Cutter

Description
This is the specialization for cutters
Functions

initSpecialization

Description
Called on specialization initializing
Definition
initSpecialization()
Code
15function Cutter.initSpecialization()
16 WorkArea.registerAreaType("cutter")
17end

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
23function Cutter.prerequisitesPresent(specializations)
24 return SpecializationUtil.hasSpecialization(WorkArea, specializations)
25end

preLoad

Description
Called before loading
Definition
preLoad(table savegame)
Arguments
tablesavegamesavegame
Code
30function Cutter:preLoad(savegame)
31 self.loadTestAreaFromXML = Cutter.loadTestAreaFromXML
32 self.getIsTestAreaActive = Cutter.getIsTestAreaActive
33 self.loadSpeedRotatingPartFromXML = Utils.overwrittenFunction(self.loadSpeedRotatingPartFromXML, Cutter.loadSpeedRotatingPartFromXML)
34 self.loadWorkAreaFromXML = Utils.overwrittenFunction(self.loadWorkAreaFromXML, Cutter.loadWorkAreaFromXML)
35 self.loadRandomlyMovingPartFromXML = Utils.overwrittenFunction(self.loadRandomlyMovingPartFromXML, Cutter.loadRandomlyMovingPartFromXML)
36 self.getIsRandomlyMovingPartActive = Utils.overwrittenFunction(self.getIsRandomlyMovingPartActive, Cutter.getIsRandomlyMovingPartActive)
37 self.getCombine = Cutter.getCombine
38 self.applyInitialAnimation = Utils.overwrittenFunction(self.applyInitialAnimation, Cutter.applyInitialAnimation)
39 self.getDirectionSnapAngle = Utils.overwrittenFunction(self.getDirectionSnapAngle, Cutter.getDirectionSnapAngle)
40 self.getIsSpeedRotatingPartActive = Utils.overwrittenFunction(self.getIsSpeedRotatingPartActive, Cutter.getIsSpeedRotatingPartActive)
41 self.doCheckSpeedLimit = Utils.overwrittenFunction(self.doCheckSpeedLimit, Cutter.doCheckSpeedLimit)
42 -- events
43 self.onStartReel = SpecializationUtil.callSpecializationsFunction("onStartReel")
44 self.onStopReel = SpecializationUtil.callSpecializationsFunction("onStopReel")
45end

load

Description
Called on loading
Definition
load(table savegame)
Arguments
tablesavegamesavegame
Code
50function Cutter:load(savegame)
51
52 self.processCutterAreas = Cutter.processCutterAreas
53 self.getDoConsumePtoPower = Utils.overwrittenFunction(self.getDoConsumePtoPower, Cutter.getDoConsumePtoPower)
54
55 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.reel#index", "vehicle.turnedOnRotationNodes.turnedOnRotationNode (type: cutter)")
56 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.reelspikes#count", "vehicle.cutter.reelspikes#count")
57 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.reelspikes#index", "vehicle.cutter.reelspikes#index")
58 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.reelspikes.spike", "vehicle.cutter.reelspikes.spike")
59 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.fruitExtraObjects.fruitExtraObject", "vehicle.cutter.fruitExtraObjects.fruitExtraObject")
60 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.reelspikes#turnedOnRotNodeRef", "vehicle.cutter.reelspikes#turnedOnRotNodeRef")
61 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterThreshingUVScrollParts", "vehicle.cutter.turnedOnScrollers.turnedOnScroller")
62 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterTurnedOnScrollers.cutterTurnedOnScroller", "vehicle.cutter.turnedOnScrollers.turnedOnScroller")
63 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.rolls", "vehicle.turnedOnRotationNodes.turnedOnRotationNode (type: cutter)")
64 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterSpeedRotatingParts", "vehicle.speedRotatingParts.speedRotatingPart")
65 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterSpeedLimit", nil)
66 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterFruitTypes#fruitTypes", "vehicle.cutter#fruitTypes")
67 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterFruitTypes#fruitTypeCategories", "vehicle.cutter#fruitTypeCategories")
68 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterFruitTypes#useWindrowed", "vehicle.cutter#useWindrowed")
69 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.convertedFillTypes#category", "vehicle.cutter#convertedFillTypeCategories")
70 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterStartAnimation#name", "vehicle.cutter#startAnimationName")
71 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterStartAnimation#speedScale", "vehicle.cutter#startAnimationSpeedScale")
72 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterStartAnimation#initialIsStarted", "vehicle.cutter#startAnimationInitialIsStarted")
73 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterEffects#baseNode", "vehicle.cutter.effect#baseNode")
74 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterEffect", "vehicle.cutter.effect")
75 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterParticleSystems.emitterShape", "vehicle.cutter.particleSystems.emitterShape")
76 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterTestAreas.cutterTestArea", "vehicle.cutter.testAreas.testArea")
77 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterAllowCuttingWhileRaised", "vehicle.cutter#allowsForageGrowhtState")
78 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutterMovingDirection", "vehicle.cutter#movingDirection")
79 Utils.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.threshingParticleSystem", "vehicle.cutter.threshingParticleSystems.threshingParticleSystem")
80
81 local fruitTypes = nil
82 local fruitTypeNames = getXMLString(self.xmlFile, "vehicle.cutter#fruitTypes")
83 local fruitTypeCategories = getXMLString(self.xmlFile, "vehicle.cutter#fruitTypeCategories")
84 if fruitTypeCategories ~= nil and fruitTypeNames == nil then
85 fruitTypes = FruitUtil.getFruitTypeByCategoryName(fruitTypeCategories, "Warning ("..self.configFileName.."): Cutter has invalid fruitTypeCategory '%s'.")
86 elseif fruitTypeCategories == nil and fruitTypeNames ~= nil then
87 fruitTypes = FruitUtil.getFruitTypesByNames(fruitTypeNames, "Warning ("..self.configFileName.."): Cutter has invalid fruitType '%s'.")
88 else
89 print("Warning ("..self.configFileName.."): Cutter needs either the 'cutterFruitTypes#fruitTypeCategories' or 'cutterFruitTypes#fruitTypes' attribute.")
90 end
91
92 self.fruitTypes = {}
93 if fruitTypes ~= nil then
94 for _,fruitType in pairs(fruitTypes) do
95 self.fruitTypes[fruitType] = true
96 end
97 end
98
99 self.fruitTypesUseWindrowed = getXMLBool(self.xmlFile, "vehicle.cutter#useWindrowed")
100 self.windrowFillTypes = {}
101 if self.fruitTypesUseWindrowed then
102 for fruitType, state in pairs(self.fruitTypes) do
103 local windrowFillType = FruitUtil.fruitTypeToWindrowFillType[fruitType]
104 self.windrowFillTypes[windrowFillType] = true
105 end
106 end
107
108
109 self.fillTypeConverters = {}
110 local category = getXMLString(self.xmlFile, "vehicle.cutter#convertedFillTypeCategories")
111 if category ~= nil then
112 local converter = FruitUtil.converterNameToInt[category]
113 if converter ~= nil and FruitUtil.converterToFillTypes[converter] ~= nil then
114 for input, converter in pairs(FruitUtil.converterToFillTypes[converter]) do
115 self.fillTypeConverters[input] = converter
116 end
117 end
118 end
119
120 if self.isClient then
121 self.cutterTurnedOnRotationNodes = Utils.loadRotationNodes(self.xmlFile, {}, "vehicle.turnedOnRotationNodes.turnedOnRotationNode", "cutter", self.components)
122 self.cutterTurnedOnScrollers = Utils.loadScrollers(self.components, self.xmlFile, "vehicle.cutter.turnedOnScrollers.turnedOnScroller", {}, false)
123
124 self.spikesCount = getXMLInt(self.xmlFile, "vehicle.cutter.reelspikes#count")
125 local indexSpikesStr = getXMLString(self.xmlFile, "vehicle.cutter.reelspikes#index")
126 self.spikesRootNode = Utils.indexToObject(self.components, indexSpikesStr)
127
128 self.spikeAnimNodes = {}
129 local i = 0
130 while true do
131 local key = string.format("vehicle.cutter.reelspikes.spike(%d)", i)
132 if not hasXMLProperty(self.xmlFile, key) then
133 break
134 end
135
136 local node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, key.."#node"))
137 local animCurve = AnimCurve:new(linearInterpolator1)
138 local keyI = 0
139 while true do
140 local animKey = key .. string.format(".key(%d)", keyI)
141 if not hasXMLProperty(self.xmlFile, animKey) then
142 break
143 end
144 local keyTime = getXMLFloat(self.xmlFile, animKey.."#time")
145 local xRot = math.rad(Utils.getNoNil(getXMLFloat(self.xmlFile, animKey.."#rotX"), 0))
146
147 animCurve:addKeyframe({v=xRot, time=keyTime})
148 keyI = keyI +1
149 end
150 table.insert(self.spikeAnimNodes, {node=node, animCurve = animCurve})
151 i = i + 1
152 end
153
154 if table.getn(self.spikeAnimNodes) == 0 then
155 self.spikeAnimNodes = nil
156 end
157
158 local turnedOnRotNodeRef = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.cutter.reelspikes#turnedOnRotNodeRef"), 1)
159 if self.cutterTurnedOnRotationNodes.nodes[turnedOnRotNodeRef] == nil and (self.spikesRootNode ~= nil or self.spikeAnimNodes ~= nil) then
160 print("Warning ("..self.configFileName.."): No 'turnedOnRotNodeRef' defined for reelspikes in '"..self.configFileName.."'")
161 self.spikesRootNode = nil
162 self.spikeAnimNodes = nil
163 else
164 self.spikesRef = self.cutterTurnedOnRotationNodes.nodes[turnedOnRotNodeRef]
165 end
166
167 self.fruitExtraObjects = {}
168 local i = 0
169 while true do
170 local key = string.format("vehicle.cutter.fruitExtraObjects.fruitExtraObject(%d)", i)
171 local t = getXMLString(self.xmlFile, key.."#fruitType")
172 local index = getXMLString(self.xmlFile, key.."#index")
173 local anim = getXMLString(self.xmlFile, key.."#anim")
174 if t==nil or (index==nil and anim==nil) then
175 break
176 end
177
178 local node = Utils.indexToObject(self.components, index)
179 if node ~= nil then
180 setVisibility(node, false)
181 end
182 self.fruitExtraObjects[t] = {node=node, anim=anim}
183 i = i +1
184 end
185
186 self.cutterStartAnimation = getXMLString(self.xmlFile, "vehicle.cutter#startAnimationName")
187 self.cutterStartAnimationSpeedScale = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.cutter#startAnimationSpeedScale"), 1)
188 self.cutterStartAnimationInitialIsStarted = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.cutter#startAnimationInitialIsStarted"), false)
189
190 self.threshingParticleSystems = {}
191 local i = 0
192 while true do
193 local keyPS = string.format("vehicle.cutter.threshingParticleSystems.threshingParticleSystem(%d)", i)
194 if not hasXMLProperty(self.xmlFile, keyPS) then
195 break
196 end
197 local currentPS = {}
198 ParticleUtil.loadParticleSystem(self.xmlFile, currentPS, keyPS, self.components, false, nil, self.baseDirectory)
199 table.insert(self.threshingParticleSystems, currentPS)
200 i = i + 1
201 end
202
203 local i = 0
204 while true do
205 local key = string.format("vehicle.cutter.threshingParticleSystems.emitterShape(%d)", i)
206 if not hasXMLProperty(self.xmlFile, key) then
207 break
208 end
209
210 local emitterShape = Utils.indexToObject(self.components, getXMLString(self.xmlFile, key.."#node"))
211 local particleType = getXMLString(self.xmlFile, key.."#particleType")
212 if emitterShape ~= nil then
213 local fillType = FillUtil.FILLTYPE_UNKNOWN
214 local fillTypeStr = getXMLString(self.xmlFile, key.."#fillType")
215 if fillTypeStr ~= nil and FillUtil.fillTypeNameToInt[fillTypeStr] ~= nil then
216 fillType = FillUtil.fillTypeNameToInt[fillTypeStr]
217 end
218
219 local particleSystem = MaterialUtil.getParticleSystem(fillType, particleType)
220 if particleSystem ~= nil then
221 table.insert(self.threshingParticleSystems, ParticleUtil.copyParticleSystem(self.xmlFile, key, particleSystem, emitterShape))
222 end
223 end
224 i = i + 1
225 end
226
227 self.cutterFillEffects = EffectManager:loadEffect(self.xmlFile, "vehicle.cutter.fillEffect", self.components, self)
228
229 self.cutterFillParticleSystems = {}
230 self.currentCutterFillParticleSystem = nil
231 local i = 0
232 while true do
233 local key = string.format("vehicle.cutter.fillParticleSystems.emitterShape(%d)", i)
234 if not hasXMLProperty(self.xmlFile, key) then
235 break
236 end
237
238 local emitterShape = Utils.indexToObject(self.components, getXMLString(self.xmlFile, key.."#node"))
239 local particleType = getXMLString(self.xmlFile, key.."#particleType")
240 if emitterShape ~= nil then
241 for fruitTypeIndex,_ in pairs(self.fruitTypes) do
242 local fillType = FruitUtil.fruitTypeToFillType[fruitTypeIndex]
243
244 if self.fruitTypesUseWindrowed then
245 fillType = FruitUtil.fruitTypeToWindrowFillType[fillType]
246 end
247 local particleSystem = MaterialUtil.getParticleSystem(fillType, particleType)
248 if particleSystem ~= nil then
249 if self.cutterFillParticleSystems[fillType] == nil then
250 self.cutterFillParticleSystems[fillType] = {}
251 end
252 table.insert(self.cutterFillParticleSystems[fillType], ParticleUtil.copyParticleSystem(self.xmlFile, key, particleSystem, emitterShape))
253 end
254 end
255 end
256 i = i + 1
257 end
258
259 self.cutterEffectBaseNode = Utils.getNoNil(Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.cutter.effect#baseNode")), self.rootNode)
260 self.cutterEffects = {}
261 self.currentCutterEffect = nil
262 if hasXMLProperty(self.xmlFile, "vehicle.cutter.effect") then
263 for fruitTypeIndex,_ in pairs(self.fruitTypes) do
264 self.cutterEffects[fruitTypeIndex] = {}
265 local i = 0
266 while true do
267 local key = string.format("vehicle.cutter.effect.effectNode(%d)", i)
268 if not hasXMLProperty(self.xmlFile, key) then
269 break
270 end
271 local effect = CutterEffect:new()
272 effect:load(self.xmlFile, key, self.components, self, fruitTypeIndex)
273 table.insert(self.cutterEffects[fruitTypeIndex], effect)
274 i = i + 1
275 end
276 end
277 end
278
279 self.cutterParticleSystems = {}
280 local i = 0
281 while true do
282 local key = string.format("vehicle.cutter.particleSystems.emitterShape(%d)", i)
283 if not hasXMLProperty(self.xmlFile, key) then
284 break
285 end
286
287 local emitterShape = Utils.indexToObject(self.components, getXMLString(self.xmlFile, key.."#node"))
288 local particleType = getXMLString(self.xmlFile, key.."#particleType")
289 if emitterShape ~= nil then
290 for fruitTypeIndex,_ in pairs(self.fruitTypes) do
291 local fillType = FruitUtil.fruitTypeToFillType[fruitTypeIndex]
292 local particleSystem = MaterialUtil.getParticleSystem(fillType, particleType)
293 if particleSystem ~= nil then
294 self.cutterParticleSystems[fillType] = ParticleUtil.copyParticleSystem(self.xmlFile, key, particleSystem, emitterShape)
295 end
296 end
297 end
298 i = i + 1
299 end
300
301 self.cutterTestAreas = {}
302 local i = 0
303 while true do
304 local key = string.format("vehicle.cutter.testAreas.testArea(%d)", i)
305 if not hasXMLProperty(self.xmlFile, key) then
306 break
307 end
308 local testArea = {}
309 if self:loadTestAreaFromXML(testArea, self.xmlFile, key) then
310 table.insert(self.cutterTestAreas, testArea)
311 end
312 i = i + 1
313 end
314 end
315
316 self.allowsForageGrowhtState = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.cutter#allowsForageGrowhtState"), false)
317 self.cutterAllowCuttingWhileRaised = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.cutter#allowCuttingWhileRaised"), false)
318 self.cutterMovingDirection = Utils.sign(Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.cutter#movingDirection"), 1))
319 self.currentInputFruitType = FruitUtil.FRUITTYPE_UNKNOWN
320 self.reelStarted = false
321 self.isCutterSpeedLimitActive = false
322
323 self.showFieldNotOwnedWarning = false
324
325 self.fillEffectTimer = 0
326 self.lastCutterArea = 0
327 self.lastCutterAreaBiggerZero = self.lastCutterArea > 0
328 self.lastCutterAreaBiggerZeroTime = -1
329 self.cutterGroundFlag = self:getNextDirtyFlag()
330end

applyInitialAnimation

Description
Called at the end of loading to apply an initial animation state
Definition
applyInitialAnimation()
Code
334function Cutter:applyInitialAnimation(superFunc)
335 if self.cutterStartAnimation ~= nil and self.playAnimation ~= nil and self.cutterStartAnimationInitialIsStarted then
336 self:playAnimation(self.cutterStartAnimation, -self.cutterStartAnimationSpeedScale, nil, true)
337 AnimatedVehicle.updateAnimations(self, 99999999)
338 end
339 if superFunc ~= nil then
340 superFunc(self)
341 end
342end

delete

Description
Called on deleting
Definition
delete()
Code
346function Cutter:delete()
347 if self.isClient then
348 ParticleUtil.deleteParticleSystems(self.threshingParticleSystems)
349 for _, effectAttribute in pairs(self.cutterEffects) do
350 EffectManager:deleteEffects(effectAttribute.effect)
351 end
352 EffectManager:deleteEffects(self.cutterFillEffects)
353 for _,v in pairs(self.cutterFillParticleSystems) do
354 ParticleUtil.deleteParticleSystems(v)
355 end
356 ParticleUtil.deleteParticleSystems(self.cutterParticleSystems)
357 end
358end

readStream

Description
Called on client side on join
Definition
readStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
364function Cutter:readStream(streamId, connection)
365 local reelStarted = streamReadBool(streamId)
366 if reelStarted then
367 self:onStartReel()
368 end
369
370 self.lastCutterAreaBiggerZero = streamReadBool(streamId)
371 if self.lastCutterAreaBiggerZero then
372 self.lastCutterAreaBiggerZeroTime = g_currentMission.time
373 self.currentInputFruitTypeGrowthState = streamReadUIntN(streamId, 3);
374 end
375
376 for _, testArea in ipairs(self.cutterTestAreas) do
377 testArea.hasFruitContact = streamReadBool(streamId)
378 end
379end

writeStream

Description
Called on server side on join
Definition
writeStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
385function Cutter:writeStream(streamId, connection)
386 streamWriteBool(streamId, self.reelStarted)
387 streamWriteBool(streamId, self.lastCutterAreaBiggerZero)
388
389 if self.lastCutterAreaBiggerZero then
390 local growthState = Utils.getNoNil(self.currentInputFruitTypeGrowthState, 0)
391 streamWriteUIntN(streamId, growthState, 3);
392 end
393 for _, testArea in ipairs(self.cutterTestAreas) do
394 streamWriteBool(streamId, testArea.hasFruitContact)
395 end
396end

readUpdateStream

Description
Called on on update
Definition
readUpdateStream(integer streamId, integer timestamp, table connection)
Arguments
integerstreamIdstream ID
integertimestamptimestamp
tableconnectionconnection
Code
403function Cutter:readUpdateStream(streamId, timestamp, connection)
404 if connection:getIsServer() then
405 self.showFieldNotOwnedWarning = streamReadBool(streamId)
406 self.lastCutterAreaBiggerZero = streamReadBool(streamId)
407
408 if self.lastCutterAreaBiggerZero then
409 self.lastCutterAreaBiggerZeroTime = g_currentMission.time
410 self.currentInputFruitTypeGrowthState = streamReadUIntN(streamId, 3);
411 end
412 for k, testArea in ipairs(self.cutterTestAreas) do
413 testArea.hasFruitContact = streamReadBool(streamId)
414 end
415 end
416end

writeUpdateStream

Description
Called on on update
Definition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)
Arguments
integerstreamIdstream ID
tableconnectionconnection
integerdirtyMaskdirty mask
Code
423function Cutter:writeUpdateStream(streamId, connection, dirtyMask)
424 if not connection:getIsServer() then
425 streamWriteBool(streamId, self.showFieldNotOwnedWarning)
426 streamWriteBool(streamId, self.lastCutterAreaBiggerZero)
427
428 if self.lastCutterAreaBiggerZero then
429 local growthState = Utils.getNoNil(self.currentInputFruitTypeGrowthState, 0)
430 streamWriteUIntN(streamId, growthState, 3);
431 end
432 for k, testArea in ipairs(self.cutterTestAreas) do
433 streamWriteBool(streamId, testArea.hasFruitContact)
434 end
435 end
436end

updateTick

Description
Called on update tick
Definition
updateTick(float dt)
Arguments
floatdttime since last call in ms
Code
450function Cutter:updateTick(dt)
451 self.isCutterSpeedLimitActive = false
452 if self.isServer then
453 self.lastCutterArea = 0
454 self.lastCutterAreaBiggerZero = false
455 end
456
457 local showFieldNotOwnedWarning = false
458
459 if self.reelStarted and self.movingDirection == self.cutterMovingDirection and (self.cutterAllowCuttingWhileRaised or self:isLowered(true)) then
460 if self.isClient then
461 local minEffectValue = math.huge
462 local maxEffectValue = -math.huge
463 for k, testArea in pairs(self.cutterTestAreas) do
464 local x,y,z = getWorldTranslation(testArea.start)
465 local x1,y1,z1 = getWorldTranslation(testArea.width)
466 local x2,_,z2 = getWorldTranslation(testArea.height)
467
468 if self.isServer and self:getLastSpeed() > 0.5 and self.currentInputFruitType ~= nil then
469 if self:getIsTestAreaActive(testArea) then
470 local fruitValue, _, growthState = Utils.getFruitArea(self.currentInputFruitType, x, z, x1, z1, x2, z2, nil, self.allowsForageGrowhtState)
471 testArea.hasFruitContact = fruitValue > 0
472 if fruitValue > 0 then
473 self.currentInputFruitTypeGrowthState = growthState;
474 end;
475 end
476 end
477
478 if testArea.hasFruitContact then
479 local lx1,_,_ = worldToLocal(self.cutterEffectBaseNode, x,y,z)
480 local lx2,_,_ = worldToLocal(self.cutterEffectBaseNode, x1,y1,z1)
481 minEffectValue = math.min(minEffectValue, lx1, lx2)
482 maxEffectValue = math.max(maxEffectValue, lx1, lx2)
483 end
484 end
485
486 local reset = false
487 if minEffectValue == math.huge and maxEffectValue == -math.huge then
488 minEffectValue = 0
489 maxEffectValue = 0
490 reset = true
491 end
492 if self.cutterMovingDirection > 0 then
493 minEffectValue = minEffectValue * -1
494 maxEffectValue = maxEffectValue * -1
495 if maxEffectValue < minEffectValue then
496 local t = minEffectValue
497 minEffectValue = maxEffectValue
498 maxEffectValue = t
499 end
500 end
501
502 if self.currentInputFruitType ~= nil then
503 local newEffect = self.cutterEffects[self.currentInputFruitType]
504 if newEffect ~= nil then
505 if self.currentCutterEffect ~= newEffect then
506 EffectManager:resetEffects(self.currentCutterEffect)
507 end
508 self.currentCutterEffect = newEffect
509 for _, effect in pairs(self.currentCutterEffect) do
510 if effect.setGrowthState ~= nil then
511 effect:setGrowthState(self.currentInputFruitTypeGrowthState)
512 end
513 if effect.setMinMax ~= nil then
514 effect:setMinMax(minEffectValue, maxEffectValue, reset)
515 end
516 end
517 else
518 if self.currentCutterEffect ~= nil then
519 EffectManager:resetEffects(self.currentCutterEffect)
520 end
521 end
522
523 local particle = self.cutterParticleSystems[self.currentInputFruitType]
524 if particle ~= self.currentCutterParticles and self.currentCutterParticles ~= nil then
525 ParticleUtil.setEmittingState(self.currentCutterParticles, false)
526 end
527
528 self.currentCutterParticles = particle
529 if self.currentCutterParticles ~= nil then
530 local widthX = math.abs(minEffectValue-maxEffectValue)
531
532 ParticleUtil.setEmittingState(self.currentCutterParticles, widthX > 0)
533 if self.currentCutterParticles.emitterShape ~= nil and self.currentCutterParticles.emitterShape ~= 0 then
534 local sx,sy,sz = getScale(self.currentCutterParticles.emitterShape)
535 setScale(self.currentCutterParticles.emitterShape, widthX, sy, sz)
536 local _,y,z = getTranslation(self.currentCutterParticles.emitterShape)
537 setTranslation(self.currentCutterParticles.emitterShape, -(minEffectValue+(widthX/2)), y,z)
538 end
539 ParticleUtil.setEmitCountScale(self.currentCutterParticles, widthX)
540 end
541 end
542 end
543
544 self.isCutterSpeedLimitActive = true
545
546 local oldInputFruitType = self.currentInputFruitType
547 if self.isServer then
548 local combine = self:getCombine()
549 if combine ~= nil and combine:getIsThreshingAllowed(false) and combine.allowsThreshing then
550 local workAreas, showWarning, _ = self:getTypedNetworkAreas(WorkArea.AREATYPE_CUTTER, true)
551 showFieldNotOwnedWarning = showWarning
552
553 if (table.getn(workAreas) > 0) then
554 local fruitTypesToUse = self.fruitTypes;
555 if combine.aiIsStarted then
556 if self.currentInputFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then
557 fruitTypesToUse = {};
558 fruitTypesToUse[self.currentInputFruitType] = true;
559 end
560 end
561
562 for fruitType,_ in pairs(fruitTypesToUse) do
563
564 local usedFruitType = fruitType
565 local outputFillType = FruitUtil.fruitTypeToFillType[usedFruitType]
566
567 local conversionFactor = 1.0
568 if self.fillTypeConverters[usedFruitType] ~= nil then
569 outputFillType = self.fillTypeConverters[usedFruitType].fillTypeTarget
570 conversionFactor = self.fillTypeConverters[usedFruitType].conversionFactor
571 end
572
573 if combine:allowFillType(outputFillType, false) then
574 local lastCutterArea = 0
575 local lastRealArea = 0
576 local lastGrowthState = nil
577
578 if self.fruitTypesUseWindrowed then
579 local totalArea, actualFruitType = ForageWagon.processForageWagonAreas(self, workAreas, self.windrowFillTypes, false, usedFruitType)
580 lastCutterArea = totalArea
581 lastRealArea = totalArea
582
583
584 usedFruitType = actualFruitType
585 if self.fillTypeConverters[usedFruitType] ~= nil then
586 outputFillType = self.fillTypeConverters[usedFruitType].fillTypeTarget
587 conversionFactor = self.fillTypeConverters[usedFruitType].windrowConversionFactor
588 end
589 else
590 local cutterArea, realArea, growthState = self:processCutterAreas(workAreas, usedFruitType)
591 lastCutterArea = cutterArea
592 lastRealArea = realArea
593 lastGrowthState = growthState
594 end
595
596 if lastCutterArea > 0 then
597 self.lastCutterArea = lastCutterArea
598 self.lastCutterAreaBiggerZero = (self.lastCutterArea > 0)
599 if self.lastCutterAreaBiggerZero then
600 self.lastCutterAreaBiggerZeroTime = g_currentMission.time
601 end
602 lastCutterArea = lastCutterArea * conversionFactor
603 combine:addCutterArea(self, lastCutterArea, lastRealArea, usedFruitType, outputFillType)
604
605 self.currentInputFruitType = usedFruitType
606 self.currentInputFruitTypeGrowthState = lastGrowthState
607
608 if self.lastCutterAreaBiggerZero ~= self.lastCutterAreaBiggerZeroSent then
609 self:raiseDirtyFlags(self.cutterGroundFlag)
610 self.lastCutterAreaBiggerZeroSent = self.lastCutterAreaBiggerZero
611 end
612
613 local ha = Utils.areaToHa(lastRealArea, g_currentMission:getFruitPixelsToSqm()) -- 4096px are mapped to 2048m
614 g_currentMission.missionStats:updateStats("threshedHectares", ha)
615 g_currentMission.missionStats:updateStats("workedHectares", ha)
616
617 -- Stop the loop over all fruit types
618 break
619 end
620 end
621 end
622 end
623 end
624
625 else
626 local combine = self:getCombine()
627 if combine ~= nil and combine.lastValidInputFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then
628 self.currentInputFruitType = combine.lastValidInputFruitType
629 end
630 end
631
632 if self.currentInputFruitType ~= oldInputFruitType and self.isClient then
633 Cutter.updateExtraObjects(self)
634 end
635
636 self.aiRequiredFruitType = self.currentInputFruitType
637 else
638 if self.currentCutterEffect ~= nil then
639 for _, effect in pairs(self.currentCutterEffect) do
640 if effect.setMinMax ~= nil then
641 effect:setMinMax(0, 0, true)
642 end
643 end
644 end
645 if self.currentCutterParticles ~= nil then
646 ParticleUtil.setEmittingState(self.currentCutterParticles, false)
647 end
648 end
649
650 if self.isServer then
651 if showFieldNotOwnedWarning ~= self.showFieldNotOwnedWarning then
652 self.showFieldNotOwnedWarning = showFieldNotOwnedWarning
653 self:raiseDirtyFlags(self.cutterGroundFlag)
654 end
655 end
656
657 if self.isClient then
658 for _, ps in pairs(self.threshingParticleSystems) do
659 ParticleUtil.setEmittingState(ps, (self.reelStarted and self.lastCutterAreaBiggerZero))
660 end
661
662 if self.currentInputFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN and self.reelStarted and self.lastCutterAreaBiggerZero then
663 self.fillEffectTimer = 500
664 else
665 if self.fillEffectTimer > 0 then
666 self.fillEffectTimer = self.fillEffectTimer - dt
667 end
668 end
669
670 local fillType = FruitUtil.fruitTypeToFillType[self.currentInputFruitType]
671 if self.fruitTypesUseWindrowed then
672 fillType = FruitUtil.fruitTypeToWindrowFillType[self.currentInputFruitType]
673 end
674
675 if fillType ~= nil then
676 if self.cutterFillEffects ~= nil then
677 if self.fillEffectTimer > 0 then
678 EffectManager:setFillType(self.cutterFillEffects, fillType)
679 EffectManager:startEffects(self.cutterFillEffects)
680 else
681 EffectManager:stopEffects(self.cutterFillEffects)
682 end
683 end
684
685 local currentCutterFillParticleSystem = self.cutterFillParticleSystems[fillType]
686 if currentCutterFillParticleSystem ~= self.currentCutterFillParticleSystem then
687 if self.currentCutterFillParticleSystem ~= nil then
688 for _, ps in pairs(self.currentCutterFillParticleSystem) do
689 ParticleUtil.setEmittingState(ps, false)
690 end
691 self.currentCutterFillParticleSystem = nil
692 end
693 self.currentCutterFillParticleSystem = currentCutterFillParticleSystem
694 end
695 end
696
697 if self.currentCutterFillParticleSystem ~= nil then
698 for _, ps in pairs(self.currentCutterFillParticleSystem) do
699 ParticleUtil.setEmittingState(ps, self.fillEffectTimer > 0)
700 end
701 end
702
703 Utils.updateRotationNodes(self, self.cutterTurnedOnRotationNodes, dt, self:getIsActive() and self.reelStarted)
704 Utils.updateScrollers(self.cutterTurnedOnScrollers, dt, self:getIsActive() and self.reelStarted)
705
706 if self.cutterTurnedOnRotationNodes.isRunning then
707 if self.spikesRootNode ~= nil then
708 --correct spikes, so that they always look down
709 local atx, aty, atz = getRotation(self.spikesRef.node)
710 for i=1, self.spikesCount do
711 local spike = getChildAt(self.spikesRootNode, i-1)
712 setRotation(spike, -atx, aty, atz)
713 end
714 end
715
716 if self.spikeAnimNodes ~= nil then
717 local x,y,z = getRotation(self.spikesRef.node)
718 local ref = x
719 if self.spikesRef.rotAxis == 2 then
720 ref = y
721 elseif self.spikesRef.rotAxis == 3 then
722 ref = z
723 end
724 local t = (ref % (2*math.pi)) / (2*math.pi)
725 for k, spike in pairs(self.spikeAnimNodes) do
726 local rotX = spike.animCurve:get(t)
727 setRotation(spike.node, rotX, 0, 0)
728 end
729 end
730 end
731 end
732end

draw

Description
Called on draw
Definition
draw()
Code
736function Cutter:draw()
737 if self.showFieldNotOwnedWarning then
738 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_youDontOwnThisField"))
739 end
740end

onDeactivate

Description
Called on deactivate
Definition
onDeactivate()
Code
744function Cutter:onDeactivate()
745 self:onStopReel()
746 if self.isClient then
747 for _, ps in pairs(self.threshingParticleSystems) do
748 ParticleUtil.setEmittingState(ps, false)
749 end
750 if self.cutterFillEffects ~= nil then
751 EffectManager:stopEffects(self.cutterFillEffects)
752 end
753 if self.currentCutterFillParticleSystem ~= nil then
754 for _, ps in pairs(self.currentCutterFillParticleSystem) do
755 ParticleUtil.setEmittingState(ps, false)
756 end
757 end
758 end
759end

onStartReel

Description
Start reel
Definition
onStartReel()
Code
763function Cutter:onStartReel()
764 if not self.reelStarted then
765 self.reelStarted = true
766
767 if self.isClient then
768 if self.cutterStartAnimation ~= nil and self.playAnimation ~= nil then
769 self:playAnimation(self.cutterStartAnimation, self.cutterStartAnimationSpeedScale, nil, true)
770 end
771 end
772 end
773end

onStopReel

Description
Stop reel
Definition
onStopReel()
Code
777function Cutter:onStopReel()
778 if self.reelStarted then
779 self.reelStarted = false
780 if self.isClient then
781 for _, ps in pairs(self.threshingParticleSystems) do
782 ParticleUtil.setEmittingState(ps, false)
783 end
784
785 if self.cutterStartAnimation ~= nil and self.playAnimation ~= nil then
786 self:playAnimation(self.cutterStartAnimation, -self.cutterStartAnimationSpeedScale, nil, true)
787 end
788
789 if self.currentCutterEffect ~= nil then
790 EffectManager:resetEffects(self.currentCutterEffect)
791 end
792
793 if self.currentCutterParticles ~= nil then
794 ParticleUtil.setEmittingState(self.currentCutterParticles, false)
795 end
796
797 if self.cutterFillEffects ~= nil then
798 EffectManager:stopEffects(self.cutterFillEffects)
799 end
800
801 if self.currentCutterFillParticleSystem ~= nil then
802 for _, ps in pairs(self.currentCutterFillParticleSystem) do
803 ParticleUtil.setEmittingState(ps, false)
804 end
805 end
806 end
807 self.currentInputFruitType = FruitUtil.FRUITTYPE_UNKNOWN;
808 end
809end

getDirectionSnapAngle

Description
Returns direction snap angle
Definition
getDirectionSnapAngle()
Return Values
floatdirectionSnapAngledirection snap angle
Code
814function Cutter:getDirectionSnapAngle(superFunc)
815 local snapAngle = 0
816 for fruitType,_ in pairs(self.fruitTypes) do
817 local desc = FruitUtil.fruitIndexToDesc[fruitType]
818 if desc ~= nil then
819 snapAngle = math.max(snapAngle, desc.directionSnapAngle)
820 end
821 end
822 return math.max(snapAngle, superFunc(self))
823end

getCombine

Description
Returns attacher combine
Definition
getCombine()
Return Values
tablecombinecombine
Code
828function Cutter:getCombine()
829 if self.getIsThreshingAllowed ~= nil and self.getIsCutterFruitTypeAllowed ~= nil and self.fillUnits ~= nil and self.fillUnits[1].fillLevel ~= nil then
830 return self
831 else
832 local c = self.attacherVehicle
833 if c ~= nil and c.getIsThreshingAllowed ~= nil and c.getIsCutterFruitTypeAllowed ~= nil and c.fillUnits ~= nil and c.fillUnits[1].fillLevel ~= nil then
834 return c
835 end
836 end
837 return nil
838end

doCheckSpeedLimit

Description
Returns if speed limit should be checked
Definition
doCheckSpeedLimit()
Return Values
booleancheckSpeedlimitcheck speed limit
Code
843function Cutter:doCheckSpeedLimit(superFunc)
844 local parent = false
845 if superFunc ~= nil then
846 parent = superFunc(self)
847 end
848
849 return parent or self.isCutterSpeedLimitActive
850end

getDoConsumePtoPower

Description
Returns if should consume pto power
Definition
getDoConsumePtoPower()
Return Values
booleanconsumeconsumePtoPower
Code
855function Cutter:getDoConsumePtoPower(superFunc)
856 if superFunc ~= nil then
857 if superFunc(self) then
858 return true
859 end
860 end
861 return self.reelStarted
862end

loadSpeedRotatingPartFromXML

Description
Loads speed rotating parts from xml
Definition
loadSpeedRotatingPartFromXML(table speedRotatingPart, integer xmlFile, string key)
Arguments
tablespeedRotatingPartspeedRotatingPart
integerxmlFileid of xml object
stringkeykey
Return Values
booleansuccesssuccess
Code
870function Cutter:loadSpeedRotatingPartFromXML(superFunc, speedRotatingPart, xmlFile, key)
871 if superFunc ~= nil then
872 if not superFunc(self, speedRotatingPart, xmlFile, key) then
873 return false
874 end
875 end
876
877 speedRotatingPart.rotateIfTurnedOn = Utils.getNoNil(getXMLBool(xmlFile, key .. "#rotateIfTurnedOn"), false)
878
879 return true
880end

getIsSpeedRotatingPartActive

Description
Returns true if speed rotating part is active
Definition
getIsSpeedRotatingPartActive(table speedRotatingPart)
Arguments
tablespeedRotatingPartspeedRotatingPart
Return Values
booleanisActivespeed rotating part is active
Code
886function Cutter:getIsSpeedRotatingPartActive(superFunc, speedRotatingPart)
887 if (not self.cutterAllowCuttingWhileRaised and not self:isLowered(true)) or (speedRotatingPart.rotateIfTurnedOn and not self.reelStarted) then
888 return false
889 end
890
891 if superFunc ~= nil then
892 return superFunc(self, speedRotatingPart)
893 end
894 return true
895end

loadWorkAreaFromXML

Description
Loads work areas from xml
Definition
loadWorkAreaFromXML(table workArea, integer xmlFile, string key)
Arguments
tableworkAreaworkArea
integerxmlFileid of xml object
stringkeykey
Return Values
booleansuccesssuccess
Code
903function Cutter:loadWorkAreaFromXML(superFunc, workArea, xmlFile, key)
904 local retValue = true
905 if superFunc ~= nil then
906 retValue = superFunc(self, workArea, xmlFile, key)
907 end
908
909 if workArea.type == WorkArea.AREATYPE_DEFAULT then
910 workArea.type = WorkArea.AREATYPE_CUTTER
911 end
912
913 return retValue
914end

loadRandomlyMovingPartFromXML

Description
Load randomly moving part from xml
Definition
loadRandomlyMovingPartFromXML(table part, integer xmlFile, string key)
Arguments
tablepartpart
integerxmlFileid of xml object
stringkeykey
Return Values
boolean
Code
922function Cutter:loadRandomlyMovingPartFromXML(superFunc, part, xmlFile, key)
923 local retValue = true
924 if superFunc ~= nil then
925 retValue = superFunc(self, part, xmlFile, key)
926 end
927
928 part.moveOnlyIfCutted = Utils.getNoNil(getXMLBool(xmlFile, key .. "#moveOnlyIfCutted"), false)
929
930 return retValue
931end

getIsRandomlyMovingPartActive

Description
Returns if randomly moving part is active
Definition
getIsRandomlyMovingPartActive(table part)
Arguments
tablepartpart to check
Return Values
booleanisActiveis active
Code
937function Cutter:getIsRandomlyMovingPartActive(superFunc, part)
938 local retValue = false
939 if superFunc ~= nil then
940 retValue = superFunc(self, part)
941 end
942
943 if part.moveOnlyIfCutted then
944 retValue = retValue and (self.lastCutterAreaBiggerZeroTime >= (g_currentMission.time - 150))
945 end
946
947 return retValue
948end

updateExtraObjects

Description
Update fruit extra object
Definition
updateExtraObjects()
Code
952function Cutter.updateExtraObjects(self)
953 if self.currentExtraObject ~= nil then
954 if self.currentExtraObject.node ~= nil then
955 setVisibility(self.currentExtraObject.node, false)
956 end
957 if self.currentExtraObject.anim ~= nil and self.playAnimation ~= nil then
958 self:playAnimation(self.currentExtraObject.anim, -1, self:getAnimationTime(self.currentExtraObject.anim), true)
959 end
960 self.currentExtraObject = nil
961 end
962 if self.currentInputFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then
963 local name = FruitUtil.fruitIndexToDesc[self.currentInputFruitType].name
964 local extraObject = self.fruitExtraObjects[name]
965 if extraObject ~= nil then
966 if extraObject.node ~= nil then
967 setVisibility(extraObject.node, true)
968 end
969 if extraObject.anim ~= nil and self.playAnimation ~= nil then
970 self:playAnimation(extraObject.anim, 1, self:getAnimationTime(extraObject.anim), true)
971 end
972 self.currentExtraObject = extraObject
973 end
974 end
975end

loadTestAreaFromXML

Description
Loading test area from xml
Definition
loadTestAreaFromXML(table testArea, integer xmlFile, string key)
Arguments
tabletestAreatestArea
integerxmlFileid of xml object
stringkeykey
Code
982function Cutter:loadTestAreaFromXML(testArea, xmlFile, key)
983 local start = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#startIndex"))
984 local width = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#widthIndex"))
985 local height = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#heightIndex"))
986
987 if start ~= nil and width ~= nil and height ~= nil then
988 testArea.start = start
989 testArea.width = width
990 testArea.height = height
991 testArea.hasFruitContact = false
992 return true
993 end
994 return false
995end

getIsTestAreaActive

Description
Returns if test area is active
Definition
getIsTestAreaActive()
Return Values
booleanisActivetest areas is active
Code
1000function Cutter:getIsTestAreaActive(area)
1001 return true
1002end

getDefaultSpeedLimit

Description
Returns default speed limit
Definition
getDefaultSpeedLimit()
Return Values
floatspeedLimitspeed limit
Code
1007function Cutter.getDefaultSpeedLimit()
1008 return 10
1009end

processCutterAreas

Description
Process cutter areas
Definition
processCutterAreas(table workAreas, integer fruitType)
Arguments
tableworkAreaswork areas to process
integerfruitTypefruit type to process
Return Values
floatvolumeSumvolume sum
floatareaSumarea sum
integercurrentGrowthStatecurrent growth state
Code
1018function Cutter:processCutterAreas(workAreas, fruitType)
1019
1020 local numAreas = table.getn(workAreas)
1021
1022 local currentGrowthState = nil
1023 local currentGrowthStateArea = 0
1024 local volumeSum = 0
1025 local areaSum = 0
1026 for i=1, numAreas do
1027 local x = workAreas[i][1]
1028 local z = workAreas[i][2]
1029 local x1 = workAreas[i][3]
1030 local z1 = workAreas[i][4]
1031 local x2 = workAreas[i][5]
1032 local z2 = workAreas[i][6]
1033
1034 local volume, area, sprayFactor, ploughFactor, growthState, growthStateArea = Utils.cutFruitArea(fruitType, x, z, x1, z1, x2, z2, true, true, self.allowsForageGrowhtState)
1035
1036 if volume > 0 then
1037 if growthStateArea > currentGrowthStateArea then
1038 currentGrowthState = growthState
1039 currentGrowthStateArea = growthStateArea
1040 end
1041
1042 local multi = g_currentMission:getHarvestScaleMultiplier(sprayFactor, ploughFactor)
1043 volumeSum = volumeSum + volume*multi
1044 areaSum = areaSum + area
1045 end
1046 end
1047 return volumeSum, areaSum, currentGrowthState
1048end