LUADOC - Farming Simulator 22

Script v1_7_1_0

Engine v1_7_1_0

Foundation Reference

StumpCutter

Description
Specialization for stump cutter allowing to cut/remove tree trunks/split shapes
Functions

crushSplitShape

Description
Crush slit shape
Definition
crushSplitShape(integer shape)
Arguments
integershapeshape
Code
352function StumpCutter:crushSplitShape(shape)
353 if self.isServer then
354 local x, _, z = getWorldTranslation(shape)
355 delete(shape)
356
357 local range = 10
358 g_densityMapHeightManager:setCollisionMapAreaDirty(x-range, z-range, x+range, z+range, true)
359 g_currentMission.aiSystem:setAreaDirty(x-range, x+range, z-range, z+range)
360 end
361end

getConsumingLoad

Description
Definition
getConsumingLoad()
Code
473function StumpCutter:getConsumingLoad(superFunc)
474 local value, count = superFunc(self)
475
476 local spec = self.spec_stumpCutter
477 local loadPercentage = 0 -- idle load
478 for i=1, #spec.cutNodes do
479 if spec.cutNodes[i].lastWorkTime + 500 > g_time then
480 loadPercentage = 1
481 break
482 end
483 end
484
485 return value+loadPercentage, count+1
486end

getCultivatorLimitToField

Description
Returns if cultivator is limited to the field
Definition
getCultivatorLimitToField()
Return Values
booleanisLimitedis limited to field
Code
453function StumpCutter:getCultivatorLimitToField(superFunc)
454 return false
455end

getDirtMultiplier

Description
Returns current dirt multiplier
Definition
getDirtMultiplier()
Return Values
floatdirtMultipliercurrent dirt multiplier
Code
425function StumpCutter:getDirtMultiplier(superFunc)
426 local multiplier = superFunc(self)
427
428 local spec = self.spec_stumpCutter
429 if spec.curSplitShape ~= nil then
430 multiplier = multiplier + self:getWorkDirtMultiplier()
431 end
432
433 return multiplier
434end

getPlowForceLimitToField

Description
Returns if plow limit to field is forced and not changeable
Definition
getPlowForceLimitToField()
Return Values
booleanisForcedis forced
Code
467function StumpCutter:getPlowForceLimitToField()
468 return true
469end

getPlowLimitToField

Description
Returns if plow is limited to the field
Definition
getPlowLimitToField()
Return Values
booleanisLimitedis limited to field
Code
460function StumpCutter:getPlowLimitToField()
461 return false
462end

getWearMultiplier

Description
Returns current wear multiplier
Definition
getWearMultiplier()
Return Values
floatwearMultipliercurrent wear multiplier
Code
439function StumpCutter:getWearMultiplier(superFunc)
440 local multiplier = superFunc(self)
441
442 local spec = self.spec_stumpCutter
443 if spec.curSplitShape ~= nil then
444 multiplier = multiplier + self:getWorkWearMultiplier()
445 end
446
447 return multiplier
448end

initSpecialization

Description
Definition
initSpecialization()
Code
23function StumpCutter.initSpecialization()
24 g_workAreaTypeManager:addWorkAreaType("stumpCutter", true)
25
26 local schema = Vehicle.xmlSchema
27 schema:setXMLSpecializationType("StumpCutter")
28
29 schema:register(XMLValueType.NODE_INDEX, "vehicle.stumpCutter.cutNode(?)#node", "Cut node")
30
31 schema:register(XMLValueType.FLOAT, "vehicle.stumpCutter.cutNode(?)#cutSizeY", "Cut size Y", 1)
32 schema:register(XMLValueType.FLOAT, "vehicle.stumpCutter.cutNode(?)#cutSizeZ", "Cut size X", 1)
33
34 schema:register(XMLValueType.TIME, "vehicle.stumpCutter.cutNode(?)#maxCutTime", "Time until cut", 4)
35 schema:register(XMLValueType.TIME, "vehicle.stumpCutter.cutNode(?)#maxResetCutTime", "Time between cuts", 4)
36
37 schema:register(XMLValueType.FLOAT, "vehicle.stumpCutter.cutNode(?)#cutFullTreeThreshold", "Cut fill tree threshold", 0.4)
38 schema:register(XMLValueType.FLOAT, "vehicle.stumpCutter.cutNode(?)#cutPartThreshold", "Cut part threshold", 0.2)
39
40 schema:register(XMLValueType.INT, "vehicle.stumpCutter.cutNode(?)#workAreaIndex", "Work area index")
41 schema:register(XMLValueType.TIME, "vehicle.stumpCutter.cutNode(?)#cutDuration", "Cut duration", 1)
42
43 EffectManager.registerEffectXMLPaths(schema, "vehicle.stumpCutter.cutNode(?).effects")
44
45 SoundManager.registerSampleXMLPaths(schema, "vehicle.stumpCutter.sounds", "start")
46 SoundManager.registerSampleXMLPaths(schema, "vehicle.stumpCutter.sounds", "stop")
47 SoundManager.registerSampleXMLPaths(schema, "vehicle.stumpCutter.sounds", "idle")
48 SoundManager.registerSampleXMLPaths(schema, "vehicle.stumpCutter.sounds", "work")
49
50 EffectManager.registerEffectXMLPaths(schema, "vehicle.stumpCutter.effects")
51 AnimationManager.registerAnimationNodesXMLPaths(schema, "vehicle.stumpCutter.animationNodes")
52
53 schema:setXMLSpecializationType()
54end

onDeactivate

Description
Called on deactivate
Definition
onDeactivate()
Code
308function StumpCutter:onDeactivate()
309 if self.isClient then
310 local spec = self.spec_stumpCutter
311 g_effectManager:stopEffects(spec.effects)
312 for i=1, #spec.cutNodes do
313 g_effectManager:stopEffects(spec.cutNodes[i].effects)
314 end
315 end
316end

onDelete

Description
Called on deleting
Definition
onDelete()
Code
168function StumpCutter:onDelete()
169 local spec = self.spec_stumpCutter
170 g_soundManager:deleteSamples(spec.samples)
171 g_animationManager:deleteAnimations(spec.animationNodes)
172 g_effectManager:deleteEffects(spec.effects)
173
174 if spec.cutNodes ~= nil then
175 for i=1, #spec.cutNodes do
176 g_effectManager:deleteEffects(spec.cutNodes[i].effects)
177 end
178 end
179end

onLoad

Description
Called on loading
Definition
onLoad(table savegame)
Arguments
tablesavegamesavegame
Code
89function StumpCutter:onLoad(savegame)
90 local spec = self.spec_stumpCutter
91
92 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.turnedOnRotationNodes.turnedOnRotationNode", "vehicle.stumpCutter.animationNodes.animationNode", "stumbCutter") --FS17 to FS19
93 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutterStartSound", "vehicle.stumpCutter.sounds.start") --FS17 to FS19
94 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutterIdleSound", "vehicle.stumpCutter.sounds.idle") --FS17 to FS19
95 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutterWorkSound", "vehicle.stumpCutter.sounds.work") --FS17 to FS19
96 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutterStopSound", "vehicle.stumpCutter.sounds.stop") --FS17 to FS19
97 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutter.emitterShape(0)", "vehicle.stumpCutter.effects.effectNode") --FS17 to FS19
98 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutter.particleSystem(0)", "vehicle.stumpCutter.effects.effectNode") --FS17 to FS19
99
100 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutter#cutNode", "vehicle.stumpCutter.cutNode#node") --FS19 to FS22
101 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutter#cutSizeY", "vehicle.stumpCutter.cutNode#cutSizeY") --FS19 to FS22
102 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutter#cutSizeZ", "vehicle.stumpCutter.cutNode#cutSizeZ") --FS19 to FS22
103 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutter#cutFullTreeThreshold", "vehicle.stumpCutter.cutNode#cutFullTreeThreshold") --FS19 to FS22
104 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.stumpCutter#cutPartThreshold", "vehicle.stumpCutter.cutNode#cutPartThreshold") --FS19 to FS22
105
106 local baseKey = "vehicle.stumpCutter"
107
108 spec.cutNodes = {}
109 spec.currentCutNodeIndex = 1
110
111 local i = 0
112 while true do
113 local key = string.format("%s.cutNode(%d)", baseKey, i)
114 if not self.xmlFile:hasProperty(key) then
115 break
116 end
117
118 local node = self.xmlFile:getValue(key .. "#node", nil, self.components, self.i3dMappings)
119 if node == nil then
120 Logging.xmlWarning(self.xmlFile, "Missing 'node' for '%s'!", key)
121 break
122 end
123
124 local cutNode = {}
125 cutNode.node = node
126 cutNode.cutSizeY = self.xmlFile:getValue(key .. "#cutSizeY", 1)
127 cutNode.cutSizeZ = self.xmlFile:getValue(key .. "#cutSizeZ", 1)
128 cutNode.maxCutTime = self.xmlFile:getValue(key .. "#maxCutTime", 4)
129 cutNode.nextCutTime = cutNode.maxCutTime
130 cutNode.maxResetCutTime = self.xmlFile:getValue(key .. "#maxResetCutTime", 1)
131 cutNode.resetCutTime = cutNode.maxResetCutTime
132 cutNode.cutFullTreeThreshold = self.xmlFile:getValue(key .. "#cutFullTreeThreshold", 0.4)
133 cutNode.cutPartThreshold = self.xmlFile:getValue(key .. "#cutPartThreshold", 0.2)
134 cutNode.workAreaIndex = self.xmlFile:getValue(key.."#workAreaIndex")
135 cutNode.workTimer = 0
136 cutNode.workDuration = self.xmlFile:getValue(key .. "#cutDuration", 1)
137
138 cutNode.lastWorkTime = -1000
139
140 cutNode.workFadeTime = 0
141 cutNode.maxWorkFadeTime = 1000
142
143 if self.isClient then
144 cutNode.effects = g_effectManager:loadEffect(self.xmlFile, key..".effects", self.components, self, self.i3dMappings)
145 end
146
147 table.insert(spec.cutNodes, cutNode)
148 i = i + 1
149 end
150
151 if self.isClient then
152 spec.samples = {}
153 spec.samples.start = g_soundManager:loadSampleFromXML(self.xmlFile, baseKey .. ".sounds", "start", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
154 spec.samples.stop = g_soundManager:loadSampleFromXML(self.xmlFile, baseKey .. ".sounds", "stop", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
155 spec.samples.idle = g_soundManager:loadSampleFromXML(self.xmlFile, baseKey .. ".sounds", "idle", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
156 spec.samples.work = g_soundManager:loadSampleFromXML(self.xmlFile, baseKey .. ".sounds", "work", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
157
158 spec.maxWorkFadeTime = 1000
159 spec.workFadeTime = 0
160
161 spec.effects = g_effectManager:loadEffect(self.xmlFile, baseKey..".effects", self.components, self, self.i3dMappings)
162 spec.animationNodes = g_animationManager:loadAnimations(self.xmlFile, baseKey .. ".animationNodes", self.components, self, self.i3dMappings)
163 end
164end

onTurnedOff

Description
Called on turn off
Definition
onTurnedOff(boolean noEventSend)
Arguments
booleannoEventSendno event send
Code
334function StumpCutter:onTurnedOff()
335 if self.isClient then
336 local spec = self.spec_stumpCutter
337 spec.workFadeTime = 0
338 g_effectManager:stopEffects(spec.effects)
339 for i=1, #spec.cutNodes do
340 g_effectManager:stopEffects(spec.cutNodes[i].effects)
341 end
342
343 g_soundManager:stopSamples(spec.samples)
344 g_soundManager:playSample(spec.samples.stop)
345 g_animationManager:stopAnimations(spec.animationNodes)
346 end
347end

onTurnedOn

Description
Called on turn on
Definition
onTurnedOn(boolean noEventSend)
Arguments
booleannoEventSendno event send
Code
321function StumpCutter:onTurnedOn()
322 if self.isClient then
323 local spec = self.spec_stumpCutter
324 g_soundManager:stopSamples(spec.samples)
325 g_soundManager:playSample(spec.samples.start)
326 g_soundManager:playSample(spec.samples.idle, 0, spec.samples.start)
327 g_animationManager:startAnimations(spec.animationNodes)
328 end
329end

onUpdateTick

Description
Called on update tick
Definition
onUpdateTick(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
186function StumpCutter:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
187 if self:getIsTurnedOn() then
188 local spec = self.spec_stumpCutter
189
190 local numCutNodes = #spec.cutNodes
191 if numCutNodes > 0 then
192 local nextCutNodeIndex = spec.currentCutNodeIndex + 1
193 if nextCutNodeIndex > numCutNodes then
194 nextCutNodeIndex = 1
195 end
196
197 spec.currentCutNodeIndex = nextCutNodeIndex
198 local cutNode = spec.cutNodes[nextCutNodeIndex]
199
200 cutNode.curLenAbove = 0
201 cutNode.curLenBelow = 0
202
203 local x,y,z = getWorldTranslation(cutNode.node)
204 local nx,ny,nz = localDirectionToWorld(cutNode.node, 1,0,0)
205 local yx,yy,yz = localDirectionToWorld(cutNode.node, 0,1,0)
206 if cutNode.curSplitShape ~= nil then
207 if testSplitShape(cutNode.curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, cutNode.cutSizeY, cutNode.cutSizeZ) == nil then
208 cutNode.curSplitShape = nil
209 end
210 end
211 if cutNode.curSplitShape == nil then
212 local shape, _, _, _, _ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, cutNode.cutSizeY, cutNode.cutSizeZ)
213 if shape ~= 0 then
214 cutNode.curSplitShape = shape
215 end
216 end
217
218 if VehicleDebug.state == VehicleDebug.DEBUG_ATTRIBUTES then
219 local x1, y1, z1 = localToWorld(cutNode.node, 0, 0, cutNode.cutSizeZ)
220 local x2, y2, z2 = localToWorld(cutNode.node, 0, cutNode.cutSizeY, 0)
221 DebugUtil.drawDebugAreaRectangle(x, y, z, x1, y1, z1, x2, y2, z2, false, 0.5924, 0.1871, 0.3723)
222 end
223
224 if cutNode.curSplitShape ~= nil then
225 local lenBelow, lenAbove = getSplitShapePlaneExtents(cutNode.curSplitShape, x,y,z, nx,ny,nz)
226
227 if lenAbove >= cutNode.cutPartThreshold then
228 cutNode.lastWorkTime = g_time
229 end
230
231 cutNode.workFadeTime = math.min(cutNode.maxWorkFadeTime, cutNode.workFadeTime + dt * numCutNodes)
232 if self.isServer then
233 cutNode.resetCutTime = cutNode.maxResetCutTime
234 if cutNode.nextCutTime > 0 then
235 cutNode.nextCutTime = cutNode.nextCutTime - dt
236 if cutNode.nextCutTime <= 0 then
237 -- cut
238 local _,ly,_ = worldToLocal(cutNode.curSplitShape, x,y,z)
239 if (lenBelow <= cutNode.cutFullTreeThreshold or ly < cutNode.cutPartThreshold+0.01) and lenAbove < 1 then -- only delete tree if lenAbove < 1 to avoid full tree deletion
240 -- Delete full tree: Not much left below the cut or cutting near the ground
241 self:crushSplitShape(cutNode.curSplitShape)
242 cutNode.curSplitShape = nil
243 elseif lenAbove >= cutNode.cutPartThreshold then
244 -- Normal cut: Splitting 20cm below the top
245 cutNode.nextCutTime = cutNode.maxCutTime
246 local curSplitShape = cutNode.curSplitShape
247 cutNode.curSplitShape = nil
248 cutNode.curLenAbove = lenAbove
249 cutNode.curLenBelow = lenBelow
250
251 self.shapeBeingCut = curSplitShape
252 splitShape(curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, cutNode.cutSizeY, cutNode.cutSizeZ, "stumpCutterSplitShapeCallback", self)
253 g_treePlantManager:removingSplitShape(curSplitShape)
254 else
255 cutNode.curSplitShape = nil
256 cutNode.nextCutTime = cutNode.maxCutTime
257 end
258 end
259 end
260 end
261 else
262 cutNode.workFadeTime = math.max(0, cutNode.workFadeTime - dt)
263 if self.isServer then
264 if cutNode.resetCutTime > 0 then
265 cutNode.resetCutTime = cutNode.resetCutTime - dt
266 if cutNode.resetCutTime <= 0 then
267 cutNode.nextCutTime = cutNode.maxCutTime
268 end
269 end
270 end
271 end
272
273 if self.isClient then
274 if cutNode.lastWorkTime + 500 > g_time then
275 g_effectManager:setFillType(cutNode.effects, FillType.WOODCHIPS)
276 g_effectManager:startEffects(cutNode.effects)
277 else
278 g_effectManager:stopEffects(cutNode.effects)
279 end
280
281 local anyCutNodeWorking = false
282 for i=1, #spec.cutNodes do
283 if spec.cutNodes[i].lastWorkTime + 500 > g_time then
284 anyCutNodeWorking = true
285 break
286 end
287 end
288
289 if anyCutNodeWorking then
290 g_effectManager:setFillType(spec.effects, FillType.WOODCHIPS)
291 g_effectManager:startEffects(spec.effects)
292 if not g_soundManager:getIsSamplePlaying(spec.samples.work) then
293 g_soundManager:playSample(spec.samples.work)
294 end
295 else
296 g_effectManager:stopEffects(spec.effects)
297 if g_soundManager:getIsSamplePlaying(spec.samples.work) then
298 g_soundManager:stopSample(spec.samples.work)
299 end
300 end
301 end
302 end
303 end
304end

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
17function StumpCutter.prerequisitesPresent(specializations)
18 return SpecializationUtil.hasSpecialization(TurnOnVehicle, specializations)
19end

processStumpCutterArea

Description
Definition
processStumpCutterArea()
Code
399function StumpCutter:processStumpCutterArea(workArea, dt)
400 local spec = self.spec_stumpCutter
401
402 local area, totalArea = 0, 0
403 for _, cutNode in ipairs(spec.cutNodes) do
404 if cutNode.workAreaIndex == workArea.index then
405 local xs,_,zs = getWorldTranslation(workArea.start)
406 local xw,_,zw = getWorldTranslation(workArea.width)
407 local xh,_,zh = getWorldTranslation(workArea.height)
408
409 local _area, _totalArea, nonMowableCut = FSDensityMapUtil.clearDecoArea(xs, zs, xw, zw, xh, zh)
410 if _area > 0 and nonMowableCut then
411 cutNode.lastWorkTime = g_time
412 end
413
414 area = area + _area
415 totalArea = totalArea + _totalArea
416 end
417 end
418
419 return area, totalArea
420end

registerEventListeners

Description
Definition
registerEventListeners()
Code
77function StumpCutter.registerEventListeners(vehicleType)
78 SpecializationUtil.registerEventListener(vehicleType, "onLoad", StumpCutter)
79 SpecializationUtil.registerEventListener(vehicleType, "onDelete", StumpCutter)
80 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", StumpCutter)
81 SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", StumpCutter)
82 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOn", StumpCutter)
83 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOff", StumpCutter)
84end

registerFunctions

Description
Definition
registerFunctions()
Code
58function StumpCutter.registerFunctions(vehicleType)
59 SpecializationUtil.registerFunction(vehicleType, "crushSplitShape", StumpCutter.crushSplitShape)
60 SpecializationUtil.registerFunction(vehicleType, "stumpCutterSplitShapeCallback", StumpCutter.stumpCutterSplitShapeCallback)
61 SpecializationUtil.registerFunction(vehicleType, "processStumpCutterArea", StumpCutter.processStumpCutterArea)
62end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
66function StumpCutter.registerOverwrittenFunctions(vehicleType)
67 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDirtMultiplier", StumpCutter.getDirtMultiplier)
68 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getWearMultiplier", StumpCutter.getWearMultiplier)
69 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCultivatorLimitToField", StumpCutter.getCultivatorLimitToField)
70 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getPlowLimitToField", StumpCutter.getPlowLimitToField)
71 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getPlowForceLimitToField", StumpCutter.getPlowForceLimitToField)
72 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getConsumingLoad", StumpCutter.getConsumingLoad)
73end

stumpCutterSplitShapeCallback

Description
Split shape callback
Definition
stumpCutterSplitShapeCallback(integer shape, boolean isBelow, boolean isAbove, float minY, float maxY, float minZ, float maxZ)
Arguments
integershapeshape
booleanisBelowis below
booleanisAboveis above
floatminYmin y split position
floatmaxYmax y split position
floatminZmin z split position
floatmaxZmax z split position
Code
372function StumpCutter:stumpCutterSplitShapeCallback(shape, isBelow, isAbove, minY, maxY, minZ, maxZ)
373 local spec = self.spec_stumpCutter
374 local cutNode = spec.cutNodes[spec.currentCutNodeIndex]
375
376 if not isBelow then
377 if cutNode.curLenAbove < 1 then -- split tree if lenAbove > 1 to avoid fulltree deletion
378 self:crushSplitShape(shape)
379 else
380 g_treePlantManager:addingSplitShape(shape, self.shapeBeingCut)
381 end
382 else
383 local yPos = minY + (maxY-minY)/2
384 local zPos = minZ + (maxZ-minZ)/2
385
386 local _,y,_ = localToWorld(cutNode.node, -0.05, yPos, zPos)
387 local height = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, getWorldTranslation(cutNode.node))
388 if y < height then
389 self:crushSplitShape(shape)
390 else
391 spec.curSplitShape = shape
392 g_treePlantManager:addingSplitShape(shape, self.shapeBeingCut)
393 end
394 end
395end