LUADOC - Farming Simulator 19

Script v1.7.1.0

Engine v1.7.1.0

Foundation Reference

Cutter

Description
Specialization for cutters providing cutter effects; requires WorkArea specialization
Functions

doCheckSpeedLimit

Description
Definition
doCheckSpeedLimit()
Code
882function Cutter:doCheckSpeedLimit(superFunc)
883 return superFunc(self) or (self:getIsTurnedOn() and (self.getIsLowered == nil or self:getIsLowered()))
884end

getAllowCutterAIFruitRequirements

Description
Definition
getAllowCutterAIFruitRequirements()
Code
533function Cutter:getAllowCutterAIFruitRequirements()
534 return true
535end

getCombine

Description
Definition
getCombine()
Code
507function Cutter:getCombine()
508 local spec = self.spec_cutter
509
510 local outputFillType = g_fruitTypeManager:getFillTypeIndexByFruitTypeIndex(spec.currentInputFruitType)
511 if spec.fruitTypeConverters[spec.currentInputFruitType] ~= nil then
512 outputFillType = spec.fruitTypeConverters[spec.currentInputFruitType].fillTypeIndex
513 end
514
515 if self.verifyCombine ~= nil then
516 return self:verifyCombine(spec.currentInputFruitType, outputFillType)
517 else
518 if self.getAttacherVehicle ~= nil then
519 local attacherVehicle = self:getAttacherVehicle()
520 if attacherVehicle ~= nil then
521 if attacherVehicle.verifyCombine ~= nil then
522 return attacherVehicle:verifyCombine(spec.currentInputFruitType, outputFillType)
523 end
524 end
525 end
526 end
527
528 return nil
529end

getCutterLoad

Description
Definition
getCutterLoad()
Code
819function Cutter:getCutterLoad()
820 return self.spec_cutter.cutterLoad
821end

getDefaultSpeedLimit

Description
Definition
getDefaultSpeedLimit()
Code
1023function Cutter.getDefaultSpeedLimit()
1024 return 10
1025end

getDirtMultiplier

Description
Definition
getDirtMultiplier()
Code
898function Cutter:getDirtMultiplier(superFunc)
899 local spec = self.spec_cutter
900
901 if spec.isWorking then
902 return superFunc(self) + self:getWorkDirtMultiplier() * self:getLastSpeed() / self.speedLimit
903 end
904
905 return superFunc(self)
906end

getIsRandomlyMovingPartActive

Description
Definition
getIsRandomlyMovingPartActive()
Code
857function Cutter:getIsRandomlyMovingPartActive(superFunc, part)
858 local retValue = superFunc(self, part)
859
860 if part.moveOnlyIfCutted then
861 retValue = retValue and (self.spec_cutter.lastAreaBiggerZeroTime >= (g_currentMission.time - 150))
862 end
863
864 return retValue
865end

getIsSpeedRotatingPartActive

Description
Definition
getIsSpeedRotatingPartActive()
Code
836function Cutter:getIsSpeedRotatingPartActive(superFunc, speedRotatingPart)
837 local spec = self.spec_cutter
838 if (not spec.allowCuttingWhileRaised and not self:getIsLowered(true)) or (speedRotatingPart.rotateIfTurnedOn and not self:getIsTurnedOn()) then
839 return false
840 end
841
842 return superFunc(self, speedRotatingPart)
843end

getIsTestAreaActive

Description
Definition
getIsTestAreaActive()
Code
813function Cutter:getIsTestAreaActive(area)
814 return true
815end

getIsWorkAreaActive

Description
Definition
getIsWorkAreaActive()
Code
869function Cutter:getIsWorkAreaActive(superFunc, workArea)
870 local spec = self.spec_cutter
871 if self.getAllowsLowering == nil or self:getAllowsLowering() then
872 if not spec.allowCuttingWhileRaised and not self:getIsLowered(true) then
873 return false
874 end
875 end
876
877 return superFunc(self, workArea)
878end

getWearMultiplier

Description
Definition
getWearMultiplier()
Code
910function Cutter:getWearMultiplier(superFunc)
911 local spec = self.spec_cutter
912
913 if spec.isWorking then
914 return superFunc(self) + self:getWorkWearMultiplier() * self:getLastSpeed() / self.speedLimit
915 end
916
917 return superFunc(self)
918end

initSpecialization

Description
Definition
initSpecialization()
Code
15function Cutter.initSpecialization()
16 g_workAreaTypeManager:addWorkAreaType("cutter", false)
17end

loadRandomlyMovingPartFromXML

Description
Definition
loadRandomlyMovingPartFromXML()
Code
847function Cutter:loadRandomlyMovingPartFromXML(superFunc, part, xmlFile, key)
848 local retValue = superFunc(self, part, xmlFile, key)
849
850 part.moveOnlyIfCutted = Utils.getNoNil(getXMLBool(xmlFile, key .. "#moveOnlyIfCutted"), false)
851
852 return retValue
853end

loadSpeedRotatingPartFromXML

Description
Definition
loadSpeedRotatingPartFromXML()
Code
825function Cutter:loadSpeedRotatingPartFromXML(superFunc, speedRotatingPart, xmlFile, key)
826 if not superFunc(self, speedRotatingPart, xmlFile, key) then
827 return false
828 end
829 speedRotatingPart.rotateIfTurnedOn = Utils.getNoNil(getXMLBool(xmlFile, key .. "#rotateIfTurnedOn"), false)
830
831 return true
832end

loadTestAreaFromXML

Description
Definition
loadTestAreaFromXML()
Code
790function Cutter:loadTestAreaFromXML(testArea, xmlFile, key)
791 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key .. "#startIndex", key .. "#startNode") --FS17 to FS19
792 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key .. "#widthIndex", key .. "#widthNode") --FS17 to FS19
793 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key .. "#heightIndex", key .. "#heightNode") --FS17 to FS19
794
795 local start = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#startNode"), self.i3dMappings)
796 local width = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#widthNode"), self.i3dMappings)
797 local height = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#heightNode"), self.i3dMappings)
798
799 if start ~= nil and width ~= nil and height ~= nil then
800 testArea.start = start
801 testArea.width = width
802 testArea.height = height
803 testArea.hasFruitContact = false
804 testArea.hasFruitContactSent = false
805 return true
806 end
807
808 return false
809end

loadWorkAreaFromXML

Description
Definition
loadWorkAreaFromXML()
Code
888function Cutter:loadWorkAreaFromXML(superFunc, workArea, xmlFile, key)
889 local retValue = superFunc(self, workArea, xmlFile, key)
890
891 workArea.chopperAreaIndex = getXMLInt(xmlFile, key..".chopperArea#index")
892
893 return retValue
894end

onAIImplementStart

Description
Definition
onAIImplementStart()
Code
966function Cutter:onAIImplementStart()
967 -- clear ai fruit requirements on start of ai vehicle to get the newest fruit type in front of the cutter
968 if self:getAllowCutterAIFruitRequirements() then
969 self:clearAIFruitRequirements()
970
971 -- default require all fruit types the cutter can handle so we don't get other fruit types
972 local spec = self.spec_cutter
973 for fruitTypeIndex, _ in pairs(spec.fruitTypes) do
974 local fruitType = g_fruitTypeManager:getFruitTypeByIndex(fruitTypeIndex)
975 if fruitType ~= nil then
976 local minState = spec.allowsForageGrowthState and fruitType.minForageGrowthState or fruitType.minHarvestingGrowthState
977 self:addAIFruitRequirement(fruitType.index, minState, fruitType.maxHarvestingGrowthState)
978 end
979 end
980 end
981end

onDelete

Description
Definition
onDelete()
Code
245function Cutter:onDelete()
246 if self.isClient then
247 local spec = self.spec_cutter
248
249 for _, effectAttribute in pairs(spec.cutterEffects) do
250 g_effectManager:deleteEffects(effectAttribute.effect)
251 end
252 g_effectManager:deleteEffects(spec.fillEffects)
253 g_animationManager:deleteAnimations(spec.animationNodes)
254 end
255end

onEndWorkAreaProcessing

Description
Definition
onEndWorkAreaProcessing()
Code
683function Cutter:onEndWorkAreaProcessing(dt, hasProcessed)
684 if self.isServer then
685 local spec = self.spec_cutter
686
687 local lastRealArea = spec.workAreaParameters.lastRealArea
688 local lastThreshedArea = spec.workAreaParameters.lastThreshedArea
689 local lastStatsArea = spec.workAreaParameters.lastStatsArea
690 local lastArea = spec.workAreaParameters.lastArea
691
692 if lastRealArea > 0 then
693 if spec.workAreaParameters.combineVehicle ~= nil then
694 local inputFruitType = spec.workAreaParameters.lastFruitType
695
696 -- always use the same input fruit type while the ai is active to prevent situations where the combine is unable to unload a different fruit type
697 if self:getIsAIActive() then
698 local requirements = self:getAIFruitRequirements()
699 local requirement = requirements[1]
700 if #requirements == 1 and requirement ~= nil and requirement.fruitType ~= FruitType.UNKNOWN then
701 inputFruitType = requirement.fruitType
702 end
703 end
704
705 -- take fill type conversion into consideration
706 local conversionFactor = 1.0
707 local outputFillType = g_fruitTypeManager:getFillTypeIndexByFruitTypeIndex(inputFruitType)
708
709 if spec.fruitTypeConverters[inputFruitType] ~= nil then
710 outputFillType = spec.fruitTypeConverters[inputFruitType].fillTypeIndex
711 conversionFactor = spec.fruitTypeConverters[inputFruitType].conversionFactor
712 end
713
714 if spec.lastOutputFillTypes[outputFillType] == nil then
715 spec.lastOutputFillTypes[outputFillType] = lastRealArea
716 else
717 spec.lastOutputFillTypes[outputFillType] = spec.lastOutputFillTypes[outputFillType] + lastRealArea
718 end
719
720 if spec.lastPrioritizedOutputType ~= FillType.UNKNOWN then
721 outputFillType = spec.lastPrioritizedOutputType
722 end
723
724 lastRealArea = lastRealArea * conversionFactor
725 local farmId = self:getLastTouchedFarmlandFarmId()
726 local appliedDelta = spec.workAreaParameters.combineVehicle:addCutterArea(lastArea, lastRealArea, inputFruitType, outputFillType, spec.strawRatio, farmId)
727 if appliedDelta > 0 then
728 spec.lastValidInputFruitType = inputFruitType
729 end
730 end
731
732 local ha = MathUtil.areaToHa(lastStatsArea, g_currentMission:getFruitPixelsToSqm()) -- 4096px are mapped to 2048m
733 local stats = g_currentMission:farmStats(self:getLastTouchedFarmlandFarmId())
734 stats:updateStats("threshedHectares", ha)
735 stats:updateStats("workedHectares", ha)
736
737 spec.lastAreaBiggerZero = lastArea > 0
738 if spec.lastAreaBiggerZero then
739 spec.lastAreaBiggerZeroTime = g_currentMission.time
740 end
741 if spec.lastAreaBiggerZero ~= spec.lastAreaBiggerZeroSent then
742 self:raiseDirtyFlags(spec.dirtyFlag)
743 spec.lastAreaBiggerZeroSent = spec.lastAreaBiggerZero
744 end
745
746 if spec.currentInputFruitType ~= spec.currentInputFruitTypeSent then
747 self:raiseDirtyFlags(spec.effectDirtyFlag)
748 spec.currentInputFruitTypeSent = spec.currentInputFruitType
749 end
750
751 if self:getAllowCutterAIFruitRequirements() then
752 if self.setAIFruitRequirements ~= nil then
753 -- we do not allow changes of the required type while working, just on ai start (if the cutter has more than one requirement or no requirement -> only one requirement allowed at the time)
754 -- prevents fruit type changes on field borders
755 local requirements = self:getAIFruitRequirements()
756 local requirement = requirements[1]
757 if #requirements > 1 or requirement == nil or requirement.fruitType == FruitType.UNKNOWN then
758 local fruitType = g_fruitTypeManager:getFruitTypeByIndex(spec.currentInputFruitTypeAI)
759 if fruitType ~= nil then
760 local minState = spec.allowsForageGrowthState and fruitType.minForageGrowthState or fruitType.minHarvestingGrowthState
761 self:setAIFruitRequirements(spec.currentInputFruitTypeAI, minState, fruitType.maxHarvestingGrowthState)
762 end
763 end
764 end
765
766 spec.aiNoValidGroundTimer = 0
767 end
768 else
769 if self:getAllowCutterAIFruitRequirements() then
770 if hasProcessed then
771 if self:getIsAIActive() and self:getRootVehicle():getAILastAllowedToDrive() then
772 spec.aiNoValidGroundTimer = spec.aiNoValidGroundTimer + dt
773 if spec.aiNoValidGroundTimer > 5000 then
774 local rootVehicle = self:getRootVehicle()
775 if rootVehicle.stopAIVehicle ~= nil then
776 rootVehicle:stopAIVehicle(AIVehicle.STOP_REASON_UNKOWN)
777 end
778 end
779 else
780 spec.aiNoValidGroundTimer = 0
781 end
782 end
783 end
784 end
785 end
786end

onLoad

Description
Definition
onLoad()
Code
75function Cutter:onLoad(savegame)
76 local spec = self.spec_cutter
77
78 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.turnedOnRotationNodes.turnedOnRotationNode#type", "vehicle.cutter.animationNodes.animationNode", "cutter") --FS17 to FS19
79 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.turnedOnScrollers", "vehicle.cutter.animationNodes.animationNode") --FS17 to FS19
80 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutter.turnedOnScrollers", "vehicle.cutter.animationNodes.animationNode") --FS17 to FS19
81 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutter.reelspikes", "vehicle.cutter.rotationNodes.rotationNode or vehicle.turnOnVehicle.turnedOnAnimation") --FS17 to FS19
82 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutter.threshingParticleSystems.threshingParticleSystem", "vehicle.cutter.fillEffect.effectNode") --FS17 to FS19
83 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutter.threshingParticleSystems.emitterShape", "vehicle.cutter.fillEffect.effectNode") --FS17 to FS19
84 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutter#convertedFillTypeCategories", "vehicle.cutter#fruitTypeConverter") --FS17 to FS19
85 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cutter#startAnimationName", "vehicle.turnOnVehicle.turnOnAnimation#name") --FS17 to FS19
86
87 -- load fruitTypes
88 local fruitTypes = nil
89 local fruitTypeNames = getXMLString(self.xmlFile, "vehicle.cutter#fruitTypes")
90 local fruitTypeCategories = getXMLString(self.xmlFile, "vehicle.cutter#fruitTypeCategories")
91 if fruitTypeCategories ~= nil and fruitTypeNames == nil then
92 fruitTypes = g_fruitTypeManager:getFruitTypesByCategoryNames(fruitTypeCategories, "Warning: Cutter has invalid fruitTypeCategory '%s' in '"..self.configFileName.."'")
93 elseif fruitTypeCategories == nil and fruitTypeNames ~= nil then
94 fruitTypes = g_fruitTypeManager:getFruitTypesByNames(fruitTypeNames, "Warning: Cutter has invalid fruitType '%s' in '"..self.configFileName.."'")
95 else
96 g_logManager:xmlWarning(self.configFileName, "Cutter needs either the 'fruitTypeCategories' or 'fruitTypes' attribute!")
97 end
98
99 if fruitTypes ~= nil then
100 spec.fruitTypes = {}
101 for _,fruitType in pairs(fruitTypes) do
102 spec.fruitTypes[fruitType] = true
103 end
104 end
105
106 spec.fruitTypeConverters = {}
107 local category = getXMLString(self.xmlFile, "vehicle.cutter#fruitTypeConverter")
108 if category ~= nil then
109 local data = g_fruitTypeManager:getConverterDataByName(category)
110 if data ~= nil then
111 for input, converter in pairs(data) do
112 spec.fruitTypeConverters[input] = converter
113 end
114 end
115 end
116
117 if self.isClient then
118 spec.animationNodes = g_animationManager:loadAnimations(self.xmlFile, "vehicle.cutter.animationNodes", self.components, self, self.i3dMappings)
119
120 spec.fruitExtraObjects = {}
121 local i = 0
122 while true do
123 local key = string.format("vehicle.cutter.fruitExtraObjects.fruitExtraObject(%d)", i)
124
125 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, key.."#index", key.."#node")
126
127 local node = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key.."#node"), self.i3dMappings)
128 local anim = getXMLString(self.xmlFile, key.."#anim")
129 local isDefault = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#isDefault"), false)
130 local fruitType = g_fruitTypeManager:getFruitTypeByName(getXMLString(self.xmlFile, key.."#fruitType"))
131
132 if fruitType == nil or (node == nil and anim == nil) then
133 break
134 end
135
136 if node ~= nil then
137 setVisibility(node, false)
138 end
139
140 local extraObject = {node=node, anim=anim}
141 spec.fruitExtraObjects[fruitType.index] = extraObject
142 if isDefault then
143 spec.fruitExtraObjects[FruitType.UNKNOWN] = extraObject
144 end
145
146 i = i + 1
147 end
148 spec.hideExtraObjectsOnDetach = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.cutter.fruitExtraObjects#hideOnDetach"), false)
149
150 spec.testAreas = {}
151 spec.testAreaBase = Utils.getNoNil(I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.cutter.testAreas#baseNode"), self.i3dMappings), self.rootNode)
152 i = 0
153 while true do
154 local key = string.format("vehicle.cutter.testAreas.testArea(%d)", i)
155 if not hasXMLProperty(self.xmlFile, key) then
156 break
157 end
158
159 local testArea = {}
160 if self:loadTestAreaFromXML(testArea, self.xmlFile, key) then
161 table.insert(spec.testAreas, testArea)
162 end
163
164 i = i + 1
165 end
166
167 -- cutter
168 spec.cutterEffects = {}
169 spec.currentCutterEffect = nil
170 if hasXMLProperty(self.xmlFile, "vehicle.cutter.effect") then
171 for fruitTypeIndex,_ in pairs(spec.fruitTypes) do
172 spec.cutterEffects[fruitTypeIndex] = {}
173 i = 0
174 while true do
175 local key = string.format("vehicle.cutter.effect.effectNode(%d)", i)
176 if not hasXMLProperty(self.xmlFile, key) then
177 break
178 end
179 local effect = CutterEffect:new()
180 effect:load(self.xmlFile, key, self.components, self, fruitTypeIndex, self.i3dMappings)
181 table.insert(spec.cutterEffects[fruitTypeIndex], effect)
182 i = i + 1
183 end
184 end
185 end
186
187 spec.fillEffects = g_effectManager:loadEffect(self.xmlFile, "vehicle.cutter.fillEffect", self.components, self, self.i3dMappings)
188 end
189
190 spec.allowsForageGrowthState = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.cutter#allowsForageGrowthState"), false)
191 spec.allowCuttingWhileRaised = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.cutter#allowCuttingWhileRaised"), false)
192 spec.movingDirection = MathUtil.sign(Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.cutter#movingDirection"), 1))
193 spec.strawRatio = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.cutter#strawRatio"), 1)
194
195 spec.useWindrow = false
196 spec.currentInputFillType = FillType.UNKNOWN
197 spec.currentInputFruitType = FruitType.UNKNOWN
198 spec.currentInputFruitTypeAI = FruitType.UNKNOWN
199 spec.lastValidInputFruitType = FruitType.UNKNOWN
200 spec.currentInputFruitTypeSent = FruitType.UNKNOWN
201 spec.currentGrowthStateTime = 0
202 spec.currentGrowthStateTimer = 0
203 spec.currentGrowthState = 0
204
205 spec.lastAreaBiggerZero = false
206 spec.lastAreaBiggerZeroSent = false
207 spec.lastAreaBiggerZeroTime = -1
208
209 spec.workAreaParameters = {}
210 spec.workAreaParameters.lastRealArea = 0
211 spec.workAreaParameters.lastArea = 0
212 spec.workAreaParameters.lastGrowthState = 0
213 spec.workAreaParameters.lastGrowthStateArea = 0
214
215 spec.lastOutputFillTypes = {}
216 spec.lastPrioritizedOutputType = FillType.UNKNOWN
217 spec.lastOutputTime = 0
218
219 spec.cutterLoad = 0
220 spec.isWorking = false
221
222 spec.workAreaParameters.countArea = true
223 spec.aiNoValidGroundTimer = 0
224
225 spec.dirtyFlag = self:getNextDirtyFlag()
226 spec.effectDirtyFlag = self:getNextDirtyFlag()
227
228 self:doCollisionMaskCheck(MathUtil.bitsToMask(26), "vehicle.cutter#ignoreCollisionMask")
229end

onPostDetach

Description
Called if vehicle gets detached
Definition
onPostDetach(table attacherVehicle, table implement)
Arguments
tableattacherVehicleattacher vehicle
tableimplementimplement
Code
935function Cutter:onPostDetach(attacherVehicle, implement)
936 if self.isClient then
937 Cutter.updateExtraObjects(self)
938 end
939end

onPostLoad

Description
Definition
onPostLoad()
Code
233function Cutter:onPostLoad(savegame)
234 if self.addCutterToCombine ~= nil then
235 self:addCutterToCombine(self)
236 end
237
238 if self.isClient then
239 Cutter.updateExtraObjects(self)
240 end
241end

onPreAttach

Description
Called if vehicle gets attached
Definition
onPreAttach(table attacherVehicle, integer inputJointDescIndex, integer jointDescIndex)
Arguments
tableattacherVehicleattacher vehicle
integerinputJointDescIndexindex of input attacher
integerjointDescIndexindex if attacher at the attacher vehicle
Code
925function Cutter:onPreAttach(attacherVehicle, inputJointDescIndex, jointDescIndex)
926 if self.isClient then
927 Cutter.updateExtraObjects(self)
928 end
929end

onReadStream

Description
Definition
onReadStream()
Code
259function Cutter:onReadStream(streamId, connection)
260 self:readTestAreasStream(streamId, connection)
261
262 local spec = self.spec_cutter
263 spec.lastAreaBiggerZero = streamReadBool(streamId)
264 if spec.lastAreaBiggerZero then
265 spec.lastAreaBiggerZeroTime = g_currentMission.time
266 end
267end

onReadUpdateStream

Description
Definition
onReadUpdateStream()
Code
280function Cutter:onReadUpdateStream(streamId, timestamp, connection)
281 if connection:getIsServer() then
282 local spec = self.spec_cutter
283
284 if streamReadBool(streamId) then
285 self:readTestAreasStream(streamId, connection)
286 end
287
288 spec.lastAreaBiggerZero = streamReadBool(streamId)
289 if spec.lastAreaBiggerZero then
290 spec.lastAreaBiggerZeroTime = g_currentMission.time
291 end
292 end
293end

onStartWorkAreaProcessing

Description
Definition
onStartWorkAreaProcessing()
Code
657function Cutter:onStartWorkAreaProcessing(dt)
658 local spec = self.spec_cutter
659
660 local combineVehicle = self:getCombine()
661
662 spec.workAreaParameters.combineVehicle = combineVehicle
663 spec.workAreaParameters.lastRealArea = 0
664 spec.workAreaParameters.lastThreshedArea = 0
665 spec.workAreaParameters.lastStatsArea = 0
666 spec.workAreaParameters.lastArea = 0
667 spec.workAreaParameters.lastGrowthState = 0
668 spec.workAreaParameters.lastGrowthStateArea = 0
669
670 if spec.workAreaParameters.lastFruitType == nil then
671 spec.workAreaParameters.fruitTypesToUse = spec.fruitTypes
672 else
673 spec.workAreaParameters.fruitTypesToUse = {}
674 spec.workAreaParameters.fruitTypesToUse[spec.workAreaParameters.lastFruitType] = true
675 end
676
677 spec.workAreaParameters.lastFruitType = nil
678 spec.isWorking = false
679end

onTurnedOff

Description
Definition
onTurnedOff()
Code
952function Cutter:onTurnedOff()
953 local spec = self.spec_cutter
954 if self.isClient then
955 g_animationManager:stopAnimations(spec.animationNodes)
956 g_effectManager:resetEffects(spec.currentCutterEffect)
957
958 spec.currentInputFruitType = FruitType.UNKNOWN
959 spec.currentInputFruitTypeAI = FruitType.UNKNOWN
960 spec.currentInputFillType = FillType.UNKNOWN
961 end
962end

onTurnedOn

Description
Definition
onTurnedOn()
Code
943function Cutter:onTurnedOn()
944 if self.isClient then
945 local spec = self.spec_cutter
946 g_animationManager:startAnimations(spec.animationNodes)
947 end
948end

onUpdateTick

Description
Definition
onUpdateTick()
Code
364function Cutter:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
365 local spec = self.spec_cutter
366 local isTurnedOn = self:getIsTurnedOn()
367
368 local isEffectActive = isTurnedOn and self.movingDirection == spec.movingDirection and self:getLastSpeed() > 0.5 and (spec.allowCuttingWhileRaised or self:getIsLowered(true))
369
370 if isEffectActive then
371 local minEffectValue = math.huge
372 local maxEffectValue = -math.huge
373
374 local chargedAreas = 0
375 for _, area in ipairs(spec.testAreas) do
376 local x,y,z = getWorldTranslation(area.start)
377 local x1,y1,z1 = getWorldTranslation(area.width)
378 local x2,_,z2 = getWorldTranslation(area.height)
379
380 if self.isServer and spec.currentInputFruitType ~= nil then
381 if self:getIsTestAreaActive(area) then
382 local fruitValue, _, _, _ = FSDensityMapUtil.getFruitArea(spec.currentInputFruitType, x, z, x1, z1, x2, z2, nil, spec.allowsForageGrowthState)
383 area.hasFruitContact = fruitValue > 0
384
385 if area.hasFruitContactSent ~= area.hasFruitContact then
386 self:raiseDirtyFlags(spec.effectDirtyFlag)
387 area.hasFruitContactSent = area.hasFruitContact
388 end
389 end
390 end
391
392 if area.hasFruitContact then
393 local lx1,_,_ = worldToLocal(spec.testAreaBase, x,y,z)
394 local lx2,_,_ = worldToLocal(spec.testAreaBase, x1,y1,z1)
395 minEffectValue = math.min(minEffectValue, lx1, lx2)
396 maxEffectValue = math.max(maxEffectValue, lx1, lx2)
397 chargedAreas = chargedAreas + 1
398 end
399 end
400
401 if not spec.useWindrow and #spec.testAreas > 0 then
402 local currentLoad = chargedAreas / #spec.testAreas
403 spec.cutterLoad = spec.cutterLoad * 0.95 + currentLoad * 0.05
404 end
405
406 local reset = false
407 if minEffectValue == math.huge and maxEffectValue == -math.huge then
408 minEffectValue = 0
409 maxEffectValue = 0
410 reset = true
411 end
412
413 if spec.movingDirection > 0 then
414 minEffectValue = minEffectValue * -1
415 maxEffectValue = maxEffectValue * -1
416 if maxEffectValue < minEffectValue then
417 local t = minEffectValue
418 minEffectValue = maxEffectValue
419 maxEffectValue = t
420 end
421 end
422
423 local inputFruitType = spec.currentInputFruitType
424 if inputFruitType ~= spec.lastValidInputFruitType then
425 -- if we pickup a different fruit type than we are able to proceed to the combine we won't display the effect
426 inputFruitType = nil
427 end
428
429 if inputFruitType ~= nil then
430 local newEffect = spec.cutterEffects[inputFruitType]
431 if newEffect ~= nil then
432 if spec.currentCutterEffect ~= newEffect then
433 g_effectManager:resetEffects(spec.currentCutterEffect)
434 end
435 spec.currentCutterEffect = newEffect
436 for _, effect in ipairs(spec.currentCutterEffect) do
437 if effect.setGrowthState ~= nil then
438 effect:setGrowthState(spec.currentGrowthState)
439 end
440 if effect.setMinMaxWidth ~= nil then
441 effect:setMinMaxWidth(minEffectValue, maxEffectValue, reset)
442 end
443 end
444 else
445 if spec.currentCutterEffect ~= nil then
446 g_effectManager:resetEffects(spec.currentCutterEffect)
447 end
448 end
449
450 Cutter.updateExtraObjects(self)
451 end
452
453 local isCollecting = spec.lastAreaBiggerZeroTime + 300 > g_currentMission.time
454 local fillType = spec.currentInputFillType
455
456 if spec.useWindrow then
457 if isCollecting then
458 spec.cutterLoad = spec.cutterLoad * 0.95 + 0.05
459 else
460 spec.cutterLoad = spec.cutterLoad * 0.9
461 end
462 end
463
464 if fillType ~= FillType.UNKNOWN and isCollecting then
465 g_effectManager:setFillType(spec.fillEffects, fillType)
466 g_effectManager:setMinMaxWidth(spec.fillEffects, minEffectValue, maxEffectValue, reset)
467 g_effectManager:startEffects(spec.fillEffects)
468 else
469 g_effectManager:stopEffects(spec.fillEffects)
470 end
471 else
472 if spec.currentCutterEffect ~= nil then
473 for _, effect in pairs(spec.currentCutterEffect) do
474 if effect.setMinMaxWidth ~= nil then
475 effect:setMinMaxWidth(0, 0, true)
476 end
477 end
478 end
479 g_effectManager:stopEffects(spec.fillEffects)
480
481 spec.cutterLoad = spec.cutterLoad * 0.9
482 end
483
484 -- lastPrioritizedOutputType is always the fill type that was the most significant fill type of the last 500ms
485 -- this fill type is transfered to the combine to avoid quick changes of the fill type if we harvester 2 different fruit types at the same time
486 -- e.g. on field borders if can happen that a bit of grass if collected from the cutter
487 spec.lastOutputTime = spec.lastOutputTime + dt
488 if spec.lastOutputTime > 500 then
489 spec.lastPrioritizedOutputType = FillType.UNKNOWN
490
491 local max = 0
492 for i, _ in pairs(spec.lastOutputFillTypes) do
493 if spec.lastOutputFillTypes[i] > max then
494 spec.lastPrioritizedOutputType = i
495 max = spec.lastOutputFillTypes[i]
496 end
497
498 spec.lastOutputFillTypes[i] = 0
499 end
500
501 spec.lastOutputTime = 0
502 end
503end

onWriteStream

Description
Definition
onWriteStream()
Code
271function Cutter:onWriteStream(streamId, connection)
272 self:writeTestAreasStream(streamId, connection)
273
274 local spec = self.spec_cutter
275 streamWriteBool(streamId, spec.lastAreaBiggerZeroSent)
276end

onWriteUpdateStream

Description
Definition
onWriteUpdateStream()
Code
297function Cutter:onWriteUpdateStream(streamId, connection, dirtyMask)
298 if not connection:getIsServer() then
299 local spec = self.spec_cutter
300
301 if streamWriteBool(streamId, bitAND(dirtyMask, spec.effectDirtyFlag) ~= 0) then
302 self:writeTestAreasStream(streamId, connection)
303 end
304
305 streamWriteBool(streamId, spec.lastAreaBiggerZeroSent)
306 end
307end

prerequisitesPresent

Description
Definition
prerequisitesPresent()
Code
21function Cutter.prerequisitesPresent(specializations)
22 return SpecializationUtil.hasSpecialization(WorkArea, specializations)
23end

processCutterArea

Description
Definition
processCutterArea()
Code
539function Cutter:processCutterArea(workArea, dt)
540 local spec = self.spec_cutter
541
542 if spec.workAreaParameters.combineVehicle ~= nil then
543 local xs,_,zs = getWorldTranslation(workArea.start)
544 local xw,_,zw = getWorldTranslation(workArea.width)
545 local xh,_,zh = getWorldTranslation(workArea.height)
546
547 local lastRealArea = 0
548 local lastThreshedArea = 0
549 local lastArea = 0
550 for fruitType, state in pairs(spec.workAreaParameters.fruitTypesToUse) do
551 if state then
552 local realArea, area, sprayFactor, plowFactor, limeFactor, weedFactor, growthState, _, terrainDetailPixelsSum = FSDensityMapUtil.cutFruitArea(fruitType, xs,zs, xw,zw, xh,zh, true, true, spec.allowsForageGrowthState, g_currentMission.chopperGroundLayerType)
553
554 if realArea > 0 then
555 if self.isServer then
556 if growthState ~= spec.currentGrowthState then
557 spec.currentGrowthStateTimer = spec.currentGrowthStateTimer + dt
558 if spec.currentGrowthStateTimer > 500 or spec.currentGrowthStateTime + 1000 < g_time then
559 spec.currentGrowthState = growthState
560 spec.currentGrowthStateTimer = 0
561 end
562 else
563 spec.currentGrowthStateTimer = 0
564 spec.currentGrowthStateTime = g_time
565 end
566
567 spec.currentInputFruitType = fruitType
568
569 -- ai only works on terrain detail, so we do not allow the ai to require fruits that are out of a field
570 if terrainDetailPixelsSum > 0 then
571 spec.currentInputFruitTypeAI = fruitType
572 end
573 spec.currentInputFillType = g_fruitTypeManager:getFillTypeIndexByFruitTypeIndex(fruitType)
574 spec.useWindrow = false
575 end
576
577 local multiplier = g_currentMission:getHarvestScaleMultiplier(fruitType, sprayFactor, plowFactor, limeFactor, weedFactor)
578 lastRealArea = realArea * multiplier
579 lastThreshedArea = realArea
580 lastArea = area
581
582 spec.workAreaParameters.lastFruitType = fruitType
583 break
584 end
585 end
586 end
587
588 if lastArea > 0 then
589 if workArea.chopperAreaIndex ~= nil then
590 local workArea = self:getWorkAreaByIndex(workArea.chopperAreaIndex)
591 if workArea ~= nil then
592 local xs,_,zs = getWorldTranslation(workArea.start)
593 local xw,_,zw = getWorldTranslation(workArea.width)
594 local xh,_,zh = getWorldTranslation(workArea.height)
595
596 FSDensityMapUtil.setGroundTypeLayerArea(xs, zs, xw, zw, xh, zh, g_currentMission.chopperGroundLayerType)
597 else
598 workArea.chopperAreaIndex = nil
599 g_logManager:xmlWarning(self.configFileName, "Invalid chopperAreaIndex '%d' for workArea '%d'!", workArea.chopperAreaIndex, workArea.index)
600 end
601 end
602
603 spec.isWorking = true
604 end
605
606 spec.workAreaParameters.lastRealArea = spec.workAreaParameters.lastRealArea + lastRealArea
607 spec.workAreaParameters.lastThreshedArea = spec.workAreaParameters.lastThreshedArea + lastThreshedArea
608 spec.workAreaParameters.lastStatsArea = spec.workAreaParameters.lastStatsArea + lastThreshedArea
609 spec.workAreaParameters.lastArea = spec.workAreaParameters.lastArea + lastArea
610 end
611
612 return spec.workAreaParameters.lastRealArea, spec.workAreaParameters.lastArea
613end

processPickupCutterArea

Description
Definition
processPickupCutterArea()
Code
617function Cutter:processPickupCutterArea(workArea, dt)
618 local spec = self.spec_cutter
619
620 if spec.workAreaParameters.combineVehicle ~= nil then
621 local lsx, lsy, lsz, lex, ley, lez, lineRadius = DensityMapHeightUtil.getLineByArea(workArea.start, workArea.width, workArea.height)
622
623 for fruitType, state in pairs(spec.workAreaParameters.fruitTypesToUse) do
624 if state then
625 local fillType = g_fruitTypeManager:getWindrowFillTypeIndexByFruitTypeIndex(fruitType)
626 if fillType ~= nil then
627 local pickedUpLiters = -DensityMapHeightUtil.tipToGroundAroundLine(self, -math.huge, fillType, lsx, lsy, lsz, lex, ley, lez, lineRadius, nil, nil, false, nil)
628
629 if self.isServer then
630 if pickedUpLiters > 0 then
631 local fruitDesc = g_fruitTypeManager:getFruitTypeByIndex(fruitType)
632 local literPerSqm = fruitDesc.literPerSqm
633 local lastCutterArea = pickedUpLiters / (g_currentMission:getFruitPixelsToSqm() * literPerSqm);
634
635 spec.currentInputFruitType = fruitType
636 spec.useWindrow = true
637 spec.currentInputFillType = fillType
638 spec.workAreaParameters.lastFruitType = fruitType
639 spec.workAreaParameters.lastRealArea = spec.workAreaParameters.lastRealArea + lastCutterArea
640 spec.workAreaParameters.lastThreshedArea = spec.workAreaParameters.lastThreshedArea + lastCutterArea
641 spec.workAreaParameters.lastStatsArea = spec.workAreaParameters.lastStatsArea + lastCutterArea
642 spec.workAreaParameters.lastArea = spec.workAreaParameters.lastArea + lastCutterArea
643 spec.isWorking = true
644 break
645 end
646 end
647 end
648 end
649 end
650 end
651
652 return spec.workAreaParameters.lastRealArea, spec.workAreaParameters.lastArea
653end

readTestAreasStream

Description
Definition
readTestAreasStream()
Code
311function Cutter:readTestAreasStream(streamId, connection)
312 local spec = self.spec_cutter
313
314 local readGrowthState = false
315 for _, testArea in ipairs(spec.testAreas) do
316 testArea.hasFruitContact = streamReadBool(streamId)
317 if testArea.hasFruitContact then
318 readGrowthState = true
319 end
320 end
321
322 if readGrowthState then
323 spec.currentGrowthState = streamReadUIntN(streamId, 4)
324 end
325
326 spec.currentInputFruitType = streamReadUIntN(streamId, 7)
327 if streamReadBool(streamId) then
328 spec.lastValidInputFruitType = spec.currentInputFruitType
329 else
330 spec.currentInputFruitType = FillType.UNKNOWN
331 end
332
333 if streamReadBool(streamId) then
334 spec.currentInputFillType = g_fruitTypeManager:getWindrowFillTypeIndexByFruitTypeIndex(spec.currentInputFruitType)
335 else
336 spec.currentInputFillType = g_fruitTypeManager:getFillTypeIndexByFruitTypeIndex(spec.currentInputFruitType)
337 end
338end

registerEventListeners

Description
Definition
registerEventListeners()
Code
55function Cutter.registerEventListeners(vehicleType)
56 SpecializationUtil.registerEventListener(vehicleType, "onLoad", Cutter)
57 SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", Cutter)
58 SpecializationUtil.registerEventListener(vehicleType, "onDelete", Cutter)
59 SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Cutter)
60 SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Cutter)
61 SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Cutter)
62 SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Cutter)
63 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Cutter)
64 SpecializationUtil.registerEventListener(vehicleType, "onStartWorkAreaProcessing", Cutter)
65 SpecializationUtil.registerEventListener(vehicleType, "onEndWorkAreaProcessing", Cutter)
66 SpecializationUtil.registerEventListener(vehicleType, "onPreAttach", Cutter)
67 SpecializationUtil.registerEventListener(vehicleType, "onPostDetach", Cutter)
68 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOn", Cutter)
69 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOff", Cutter)
70 SpecializationUtil.registerEventListener(vehicleType, "onAIImplementStart", Cutter)
71end

registerFunctions

Description
Definition
registerFunctions()
Code
27function Cutter.registerFunctions(vehicleType)
28 SpecializationUtil.registerFunction(vehicleType, "readTestAreasStream", Cutter.readTestAreasStream)
29 SpecializationUtil.registerFunction(vehicleType, "writeTestAreasStream", Cutter.writeTestAreasStream)
30 SpecializationUtil.registerFunction(vehicleType, "getCombine", Cutter.getCombine)
31 SpecializationUtil.registerFunction(vehicleType, "getAllowCutterAIFruitRequirements", Cutter.getAllowCutterAIFruitRequirements)
32 SpecializationUtil.registerFunction(vehicleType, "loadTestAreaFromXML", Cutter.loadTestAreaFromXML)
33 SpecializationUtil.registerFunction(vehicleType, "getIsTestAreaActive", Cutter.getIsTestAreaActive)
34 SpecializationUtil.registerFunction(vehicleType, "processCutterArea", Cutter.processCutterArea)
35 SpecializationUtil.registerFunction(vehicleType, "processPickupCutterArea", Cutter.processPickupCutterArea)
36 SpecializationUtil.registerFunction(vehicleType, "getCutterLoad", Cutter.getCutterLoad)
37end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
41function Cutter.registerOverwrittenFunctions(vehicleType)
42 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadSpeedRotatingPartFromXML", Cutter.loadSpeedRotatingPartFromXML)
43 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsSpeedRotatingPartActive", Cutter.getIsSpeedRotatingPartActive)
44 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadRandomlyMovingPartFromXML", Cutter.loadRandomlyMovingPartFromXML)
45 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsRandomlyMovingPartActive", Cutter.getIsRandomlyMovingPartActive)
46 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsWorkAreaActive", Cutter.getIsWorkAreaActive)
47 SpecializationUtil.registerOverwrittenFunction(vehicleType, "doCheckSpeedLimit", Cutter.doCheckSpeedLimit)
48 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadWorkAreaFromXML", Cutter.loadWorkAreaFromXML)
49 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDirtMultiplier", Cutter.getDirtMultiplier)
50 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getWearMultiplier", Cutter.getWearMultiplier)
51end

updateDebugValues

Description
Definition
updateDebugValues()
Code
1029function Cutter:updateDebugValues(values)
1030 local spec = self.spec_cutter
1031 table.insert(values, {name="lastPrioritizedOutputType", value=string.format("%s", g_fillTypeManager:getFillTypeNameByIndex(spec.lastPrioritizedOutputType))})
1032
1033 local sum = 0
1034 for fillType, value in pairs(spec.lastOutputFillTypes) do
1035 sum = sum + value
1036 end
1037
1038 for fillType, value in pairs(spec.lastOutputFillTypes) do
1039 table.insert(values, {name=string.format("buffer (%s)", g_fillTypeManager:getFillTypeNameByIndex(fillType)), value=string.format("%.0f%%", value/sum*100)})
1040 end
1041end

updateExtraObjects

Description
Definition
updateExtraObjects()
Code
985function Cutter.updateExtraObjects(self)
986 local spec = self.spec_cutter
987
988 if spec.currentInputFruitType ~= nil then
989 local extraObject = spec.fruitExtraObjects[spec.currentInputFruitType]
990
991 if spec.hideExtraObjectsOnDetach then
992 if self.getAttacherVehicle == nil or self:getAttacherVehicle() == nil then
993 extraObject = nil
994 end
995 end
996
997 if extraObject ~= spec.currentExtraObject then
998 if spec.currentExtraObject ~= nil then
999 if spec.currentExtraObject.node ~= nil then
1000 setVisibility(spec.currentExtraObject.node, false)
1001 end
1002 if spec.currentExtraObject.anim ~= nil and self.playAnimation ~= nil then
1003 self:playAnimation(spec.currentExtraObject.anim, -1, self:getAnimationTime(spec.currentExtraObject.anim), true)
1004 end
1005 spec.currentExtraObject = nil
1006 end
1007
1008 if extraObject ~= nil then
1009 if extraObject.node ~= nil then
1010 setVisibility(extraObject.node, true)
1011 end
1012 if extraObject.anim ~= nil and self.playAnimation ~= nil then
1013 self:playAnimation(extraObject.anim, 1, self:getAnimationTime(extraObject.anim), true)
1014 end
1015 spec.currentExtraObject = extraObject
1016 end
1017 end
1018 end
1019end

writeTestAreasStream

Description
Definition
writeTestAreasStream()
Code
342function Cutter:writeTestAreasStream(streamId, connection)
343 local spec = self.spec_cutter
344
345 local sentGrowthState = false
346 for _, testArea in ipairs(spec.testAreas) do
347 streamWriteBool(streamId, testArea.hasFruitContact)
348 if testArea.hasFruitContact then
349 sentGrowthState = true
350 end
351 end
352
353 if sentGrowthState then
354 streamWriteUIntN(streamId, spec.currentGrowthState, 4)
355 end
356
357 streamWriteUIntN(streamId, spec.currentInputFruitType, 7)
358 streamWriteBool(streamId, spec.currentInputFruitType == spec.lastValidInputFruitType)
359 streamWriteBool(streamId, spec.useWindrow)
360end