LUADOC - Farming Simulator 22

Script v1_7_1_0

Engine v1_7_1_0

Foundation Reference

Leveler

Description
Specialization for levelers for distributing silage/chaff in bunker silos
Functions

getIsAttacherJointControlDampingAllowed

Description
Returns if damping is allowed
Definition
getIsAttacherJointControlDampingAllowed()
Return Values
booleanallowedallowed
Code
524function Leveler:getIsAttacherJointControlDampingAllowed(superFunc)
525 if not superFunc(self) then
526 return false
527 end
528
529 for _, levelerNode in pairs(self.spec_leveler.nodes) do
530 local x, y, z = getWorldTranslation(levelerNode.node)
531 local _, height = DensityMapHeightUtil.getHeightAtWorldPos(x, y, z)
532 if height == 0 then
533 return false
534 end
535 end
536
537 return true
538end

getIsLevelerPickupNodeActive

Description
Returns true if leveler pickup node is active
Definition
getIsLevelerPickupNodeActive(table levelerNode)
Arguments
tablelevelerNodepickup node data
Return Values
booleanisActivepickup node is active
Code
517function Leveler:getIsLevelerPickupNodeActive(levelerNode)
518 return self.getAttacherVehicle == nil or self:getAttacherVehicle() ~= nil
519end

initSpecialization

Description
Called on specialization initializing
Definition
initSpecialization()
Code
27function Leveler.initSpecialization()
28 local schema = Vehicle.xmlSchema
29 schema:setXMLSpecializationType("Leveler")
30
31 local basePath = Leveler.LEVELER_NODE_XML_KEY
32
33 schema:register(XMLValueType.NODE_INDEX, basePath .. "#node", "Leveler node")
34
35 schema:register(XMLValueType.FLOAT, basePath .. "#width", "Width")
36 schema:register(XMLValueType.FLOAT, basePath .. "#zOffset", "Z axis offset", 0)
37 schema:register(XMLValueType.FLOAT, basePath .. "#yOffset", "Y axis offset", 0)
38 schema:register(XMLValueType.FLOAT, basePath .. "#minDropWidth", "Min. drop width", "half of width")
39 schema:register(XMLValueType.FLOAT, basePath .. "#maxDropWidth", "Max. drop width", "width value")
40 schema:register(XMLValueType.FLOAT, basePath .. "#minDropDirOffset", "Min. drop direction offset", 0.7)
41 schema:register(XMLValueType.FLOAT, basePath .. "#maxDropDirOffset", "Max. drop direction offset", 0.7)
42 schema:register(XMLValueType.INT, basePath .. "#numHeightLimitChecks", "Number of height limit checks", 6)
43 schema:register(XMLValueType.BOOL, basePath .. "#alignToWorldY", "Defines if the leveler node is aligned to worlds Y axis", true)
44
45 schema:register(XMLValueType.BOOL, basePath .. ".smoothing#allowed", "Leveler smoothes while driving backward", true)
46 schema:register(XMLValueType.FLOAT, basePath .. ".smoothing#radius", "Smooth ground radius", 0.5)
47 schema:register(XMLValueType.FLOAT, basePath .. ".smoothing#overlap", "Radius overlap", 1.7)
48 schema:register(XMLValueType.INT, basePath .. ".smoothing#direction", "Smooth direction (if set to '0' it smooths in both directions)", -1)
49
50 schema:register(XMLValueType.INT, basePath .. "#fillUnitIndex", "Fill unit index", "Value of vehicle.leveler#fillUnitIndex")
51
52 schema:register(XMLValueType.NODE_INDEX, basePath .. ".occlusionAreas.occlusionArea(?)#startNode", "Start node")
53 schema:register(XMLValueType.NODE_INDEX, basePath .. ".occlusionAreas.occlusionArea(?)#widthNode", "Width node")
54 schema:register(XMLValueType.NODE_INDEX, basePath .. ".occlusionAreas.occlusionArea(?)#heightNode", "Height node")
55
56 schema:register(XMLValueType.INT, "vehicle.leveler.pickUpDirection", "Pick up direction", 1)
57 schema:register(XMLValueType.INT, "vehicle.leveler#fillUnitIndex", "Fill unit index")
58 schema:register(XMLValueType.FLOAT, "vehicle.leveler#maxFillLevelPerMS", "Max. fill level change rate as reference for effect and force", 20)
59
60 schema:register(XMLValueType.NODE_INDEX, "vehicle.leveler.force#node", "Force node")
61 schema:register(XMLValueType.NODE_INDEX, "vehicle.leveler.force#directionNode", "Force direction node")
62 schema:register(XMLValueType.FLOAT, "vehicle.leveler.force#maxForce", "Max. force in kN", 0)
63 schema:register(XMLValueType.INT, "vehicle.leveler.force#direction", "Driving direction for appling force", 1)
64
65 schema:register(XMLValueType.BOOL, "vehicle.leveler#ignoreFarmlandState", "If set to true the farmland underneath the leveler does not need to be bought to actually work", false)
66
67 EffectManager.registerEffectXMLPaths(schema, "vehicle.leveler.effects")
68
69 schema:setXMLSpecializationType()
70end

loadLevelerNodeFromXML

Description
Loads leveler node from xml
Definition
loadLevelerNodeFromXML(table levelerNode, integer xmlFile, string key)
Arguments
tablelevelerNodeleveler node data
integerxmlFileid of xml object
stringkeykey
Return Values
booleansuccesssuccess
Code
442function Leveler:loadLevelerNodeFromXML(levelerNode, xmlFile, key)
443 levelerNode.node = xmlFile:getValue(key .. "#node", nil, self.components, self.i3dMappings)
444 if levelerNode.node ~= nil then
445 local referenceFrame = createTransformGroup("referenceFrame")
446 link(getParent(levelerNode.node), referenceFrame)
447 setTranslation(referenceFrame, getTranslation(levelerNode.node))
448 setRotation(referenceFrame, getRotation(levelerNode.node))
449 levelerNode.referenceFrame = referenceFrame
450
451 levelerNode.zOffset = xmlFile:getValue(key .. "#zOffset", 0)
452 levelerNode.yOffset = xmlFile:getValue(key .. "#yOffset", 0)
453 levelerNode.width = xmlFile:getValue(key .. "#width")
454 levelerNode.halfWidth = levelerNode.width * 0.5
455
456 levelerNode.minDropWidth = xmlFile:getValue(key .. "#minDropWidth", levelerNode.width*0.5)
457 levelerNode.halfMinDropWidth = levelerNode.minDropWidth * 0.5
458 levelerNode.maxDropWidth = xmlFile:getValue(key .. "#maxDropWidth", levelerNode.width)
459 levelerNode.halfMaxDropWidth = levelerNode.maxDropWidth * 0.5
460
461 levelerNode.minDropDirOffset = xmlFile:getValue(key .. "#minDropDirOffset", 0.7)
462 levelerNode.maxDropDirOffset = xmlFile:getValue(key .. "#maxDropDirOffset", 0.7)
463 levelerNode.numHeightLimitChecks = xmlFile:getValue(key .. "#numHeightLimitChecks", 6)
464 levelerNode.alignToWorldY = xmlFile:getValue(key .. "#alignToWorldY", true)
465
466 levelerNode.occlusionAreas = {}
467
468 local i = 0
469 while true do
470 local baseKey = string.format("%s.occlusionAreas.occlusionArea(%d)", key, i)
471 if not xmlFile:hasProperty(baseKey) then
472 break
473 end
474
475 local entry = {}
476 entry.startNode = xmlFile:getValue(baseKey .. "#startNode", nil, self.components, self.i3dMappings)
477 entry.widthNode = xmlFile:getValue(baseKey .. "#widthNode", nil, self.components, self.i3dMappings)
478 entry.heightNode = xmlFile:getValue(baseKey .. "#heightNode", nil, self.components, self.i3dMappings)
479
480 if entry.startNode ~= nil and entry.widthNode ~= nil and entry.heightNode ~= nil then
481 table.insert(levelerNode.occlusionAreas, entry)
482 else
483 Logging.xmlWarning(xmlFile, "Failed to load occlustion area '%s'. One or more nodes missing.", baseKey)
484 end
485
486 i = i + 1
487 end
488
489
490 levelerNode.allowsSmoothing = xmlFile:getValue(key .. ".smoothing#allowed", true)
491 levelerNode.smoothGroundRadius = xmlFile:getValue(key .. ".smoothing#radius", 0.5)
492 levelerNode.smoothOverlap = xmlFile:getValue(key .. ".smoothing#overlap", 1.7)
493 levelerNode.smoothDirection = xmlFile:getValue(key .. ".smoothing#direction", -1)
494
495 levelerNode.lineOffsetPickUp = nil
496 levelerNode.lineOffsetDrop = nil
497
498 levelerNode.lastPickUp = 0
499 levelerNode.lastDrop = 0
500
501 levelerNode.fillUnitIndex = xmlFile:getValue(key .. "#fillUnitIndex", self.spec_leveler.fillUnitIndex)
502 if not self:getFillUnitExists(levelerNode.fillUnitIndex) then
503 Logging.xmlWarning(self.xmlFile, "Unknown fillUnitIndex '%s' for leveler", tostring(levelerNode.fillUnitIndex))
504 return false
505 end
506
507 return true
508 end
509
510 return false
511end

onDelete

Description
Called on deleting
Definition
onDelete()
Code
161function Leveler:onDelete()
162 local spec = self.spec_leveler
163
164 g_effectManager:deleteEffects(spec.effects)
165end

onLevelerRaycastCallback

Description
Callback used when raycast hits an object.
Definition
onLevelerRaycastCallback(integer hitObjectId, float x, float y, float z, float distance, float nx, float ny, float nz, integer subShapeIndex, integer shapeId, boolean isLast)
Arguments
integerhitObjectIdscenegraph object id
floatxworld x hit position
floatyworld y hit position
floatzworld z hit position
floatdistancedistance at which the cast hit the object
floatnxnormal x direction
floatnynormal y direction
floatnznormal z direction
integersubShapeIndexsub shape index
integershapeIdid of shape
booleanisLastis last hit
Return Values
boolreturnfalse to stop raycast
Code
555function Leveler.onLevelerRaycastCallback(levelerNode, hitObjectId, x, y, z, distance, nx, ny, nz, subShapeIndex, shapeId, isLast)
556 local self = levelerNode.vehicle
557 local spec = self.spec_leveler
558
559 if hitObjectId ~= 0 then
560 if hitObjectId ~= g_currentMission.terrainRootNode then
561 levelerNode.raycastHitObject = true
562 --#debug drawDebugLine(x, y, z, 1, 0, 0, x, y + 1, z, 1, 0, 0, true)
563 end
564 end
565
566 if isLast then
567 if not levelerNode.raycastHitObject then
568 local fillLevel = self:getFillUnitFillLevel(levelerNode.fillUnitIndex)
569 if fillLevel > 0 then
570 local fillType = levelerNode.raycastLastFillType
571 local outerRadius = levelerNode.raycastLastRadius
572
573 local f = spec.lastFillLevelMovedPct
574 local width = MathUtil.lerp(levelerNode.halfMinDropWidth, levelerNode.halfMaxDropWidth, f)
575 local dropOffset = MathUtil.lerp(levelerNode.minDropDirOffset, levelerNode.maxDropDirOffset, f)
576
577 local terrainHeightUpdater = g_densityMapHeightManager:getTerrainDetailHeightUpdater()
578 if terrainHeightUpdater ~= nil then
579 for i=1, #levelerNode.occlusionAreas do
580 local occlusionArea = levelerNode.occlusionAreas[i]
581
582 local ox1, oy1, oz1 = getWorldTranslation(occlusionArea.startNode)
583 local ox2, _, oz2 = getWorldTranslation(occlusionArea.widthNode)
584 local ox3, _, oz3 = getWorldTranslation(occlusionArea.heightNode)
585
586 local x, z, widthX, widthZ, heightX, heightZ = MathUtil.getXZWidthAndHeight(ox1, oz1, ox2, oz2, ox3, oz3)
587
588 addDensityMapHeightOcclusionArea(terrainHeightUpdater, x, oy1, z, widthX, oy1, widthZ, heightX, oy1, heightZ, true)
589
590 --#debug DebugUtil.drawDebugParallelogram(x, z, widthX, widthZ, heightX, heightZ, 0, 0.5, 0, 0, 0.1)
591 end
592 end
593
594 local sx,sy,sz = localToWorld(levelerNode.node, -width, levelerNode.yOffset, levelerNode.zOffset + dropOffset)
595 local ex,ey,ez = localToWorld(levelerNode.node, width, levelerNode.yOffset, levelerNode.zOffset + dropOffset)
596 levelerNode.lastDrop2, levelerNode.lineOffsetDrop2 = DensityMapHeightUtil.tipToGroundAroundLine(self, fillLevel, fillType, sx,sy,sz, ex,ey,ez, 0, outerRadius, levelerNode.lineOffsetDrop2, false, nil)
597
598 --#debug DebugUtil.drawDebugLine(sx,sy,sz, ex,ey,ez, 0, 1, 1, outerRadius)
599
600 if levelerNode.lastDrop2 > 0 then
601 local leftOver = fillLevel - levelerNode.lastDrop2
602 if leftOver <= g_densityMapHeightManager:getMinValidLiterValue(fillType) then
603 levelerNode.lastDrop2 = fillLevel
604 spec.litersToPickup = spec.litersToPickup + leftOver
605 end
606 self:addFillUnitFillLevel(self:getOwnerFarmId(), levelerNode.fillUnitIndex, -levelerNode.lastDrop2, fillType, ToolType.UNDEFINED, nil)
607 end
608 end
609 end
610 end
611end

onLoad

Description
Called on loading
Definition
onLoad(table savegame)
Arguments
tablesavegamesavegame
Code
99function Leveler:onLoad(savegame)
100 local spec = self.spec_leveler
101
102 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.leveler.levelerNode#index", "vehicle.leveler.levelerNode#node") -- FS17
103 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.levelerEffects", "vehicle.leveler.effects") -- FS17
104
105 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.leveler.levelerNode(0)#minDropHeight") -- FS22
106 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.leveler.levelerNode(0)#maxDropHeight") -- FS22
107
108 spec.pickUpDirection = self.xmlFile:getValue("vehicle.leveler.pickUpDirection", 1.0)
109 spec.maxFillLevelPerMS = self.xmlFile:getValue("vehicle.leveler#maxFillLevelPerMS", 35)
110 spec.fillUnitIndex = self.xmlFile:getValue("vehicle.leveler#fillUnitIndex")
111
112 spec.nodes = {}
113 local i = 0
114 while true do
115 local key = string.format("vehicle.leveler.levelerNode(%d)", i)
116 if not self.xmlFile:hasProperty(key) then
117 break
118 end
119
120 local levelerNode = {}
121 if self:loadLevelerNodeFromXML(levelerNode, self.xmlFile, key) then
122 levelerNode.vehicle = self
123 levelerNode.onLevelerRaycastCallback = self.onLevelerRaycastCallback
124 table.insert(spec.nodes, levelerNode)
125 end
126 i = i + 1
127 end
128
129 spec.litersToPickup = 0
130 spec.smoothAccumulation = 0
131
132 spec.lastFillLevelMoved = 0
133 spec.lastFillLevelMovedPct = 0
134 spec.lastFillLevelMovedTarget = 0
135 spec.lastFillLevelMovedBuffer = 0
136 spec.lastFillLevelMovedBufferTime = 300
137 spec.lastFillLevelMovedBufferTimer = 0
138
139 spec.forceNode = self.xmlFile:getValue("vehicle.leveler.force#node", nil, self.components, self.i3dMappings)
140 spec.forceDirNode = self.xmlFile:getValue("vehicle.leveler.force#directionNode", spec.forceNode, self.components, self.i3dMappings)
141
142 spec.maxForce = self.xmlFile:getValue("vehicle.leveler.force#maxForce", 0) -- kN
143 spec.lastForce = 0
144 spec.forceDir = self.xmlFile:getValue("vehicle.leveler.force#direction", 1)
145
146 spec.ignoreFarmlandState = self.xmlFile:getValue("vehicle.leveler#ignoreFarmlandState", false)
147
148 if self.isClient then
149 spec.effects = g_effectManager:loadEffect(self.xmlFile, "vehicle.leveler.effects", self.components, self, self.i3dMappings)
150 end
151
152 if #spec.nodes == 0 then
153 SpecializationUtil.removeEventListener(self, "onUpdate", Leveler)
154 end
155
156 spec.dirtyFlag = self:getNextDirtyFlag()
157end

onReadUpdateStream

Description
Called on on update
Definition
onReadUpdateStream(integer streamId, integer timestamp, table connection)
Arguments
integerstreamIdstream ID
integertimestamptimestamp
tableconnectionconnection
Code
172function Leveler:onReadUpdateStream(streamId, timestamp, connection)
173 if connection:getIsServer() then
174 if streamReadBool(streamId) then
175 local spec = self.spec_leveler
176 spec.lastFillLevelMovedPct = streamReadUIntN(streamId, Leveler.LEVEL_NUM_BITS) / Leveler.LEVEL_MAX_VALUE
177 end
178 end
179end

onUpdate

Description
Called on update
Definition
onUpdate(float dt, boolean isActiveForInput, boolean isSelected)
Arguments
floatdttime since last call in ms
booleanisActiveForInputtrue if vehicle is active for input
booleanisSelectedtrue if vehicle is selected
Code
200function Leveler:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
201 local spec = self.spec_leveler
202
203 if self.isClient then
204 local fillType = FillType.UNKNOWN
205 for _, levelerNode in pairs(spec.nodes) do
206 fillType = self:getFillUnitLastValidFillType(levelerNode.fillUnitIndex)
207 if fillType ~= FillType.UNKNOWN then
208 break
209 end
210 end
211
212 if fillType ~= FillType.UNKNOWN and spec.lastFillLevelMovedPct > 0 then
213 g_effectManager:setFillType(spec.effects, fillType)
214 g_effectManager:startEffects(spec.effects)
215
216
217 for _, effect in pairs(spec.effects) do
218 if effect:isa(LevelerEffect) or effect:isa(SnowPlowMotionPathEffect) then
219 effect:setFillLevel(spec.lastFillLevelMovedPct)
220 effect:setLastVehicleSpeed(self.movingDirection * self:getLastSpeed())
221 end
222 end
223 else
224 g_effectManager:stopEffects(spec.effects)
225 end
226 end
227
228 if self.isServer then
229 for _, levelerNode in pairs(spec.nodes) do
230 local x0,y0,z0 = localToWorld(levelerNode.node, -levelerNode.halfWidth, levelerNode.yOffset, levelerNode.maxDropDirOffset)
231 local x1,y1,z1 = localToWorld(levelerNode.node, levelerNode.halfWidth, levelerNode.yOffset, levelerNode.maxDropDirOffset)
232 if not spec.ignoreFarmlandState then
233 local ownerFarmId = self:getOwnerFarmId()
234 if not g_farmlandManager:getCanAccessLandAtWorldPosition(ownerFarmId, x0, z0) or
235 not g_farmlandManager:getCanAccessLandAtWorldPosition(ownerFarmId, x1, z1) then
236 break
237 end
238 end
239
240 local pickedUpFillLevel = 0
241 local fillType = self:getFillUnitFillType(levelerNode.fillUnitIndex)
242 local fillLevel = self:getFillUnitFillLevel(levelerNode.fillUnitIndex)
243
244 if fillType == FillType.UNKNOWN or fillLevel < g_densityMapHeightManager:getMinValidLiterValue(fillType) + 0.001 then
245 local newFillType = DensityMapHeightUtil.getFillTypeAtLine(x0,y0,z0, x1,y1,z1, 0.5*levelerNode.maxDropDirOffset)
246 if newFillType ~= FillType.UNKNOWN and newFillType ~= fillType then
247 if self:getFillUnitSupportsFillType(levelerNode.fillUnitIndex, newFillType) then
248 self:addFillUnitFillLevel(self:getOwnerFarmId(), levelerNode.fillUnitIndex, -math.huge)
249 fillType = newFillType
250 end
251 end
252 end
253 local heightType = g_densityMapHeightManager:getDensityMapHeightTypeByFillTypeIndex(fillType)
254
255 if fillType ~= FillType.UNKNOWN and heightType ~= nil then
256 local innerRadius = 0.5
257 local outerRadius = 2
258 local capacity = self:getFillUnitCapacity(levelerNode.fillUnitIndex)
259
260 -- limit the leveler node Y direction to the world Y (So the node + offset can be higher as the node but cannot dig into the ground when facing downwards)
261 local dirY = 0
262 if levelerNode.alignToWorldY then
263 local dirX, dirZ
264 dirX, dirY, dirZ = localDirectionToWorld(levelerNode.referenceFrame, 0, 0, 1)
265 I3DUtil.setWorldDirection(levelerNode.node, dirX, math.max(dirY, 0), dirZ, 0, 1, 0)
266 end
267 --#debug DebugUtil.drawDebugNode(levelerNode.node, "levelerNode", false)
268
269 -- pick up at node
270 if self:getIsLevelerPickupNodeActive(levelerNode) then
271 if spec.pickUpDirection == self.movingDirection and self.lastSpeed > 0.0001 then
272 local sx,sy,sz = localToWorld(levelerNode.node, -levelerNode.halfWidth, levelerNode.yOffset, levelerNode.zOffset)
273 local ex,ey,ez = localToWorld(levelerNode.node, levelerNode.halfWidth, levelerNode.yOffset, levelerNode.zOffset)
274
275 --#debug DebugUtil.drawDebugLine(sx, sy, sz, ex, ey, ez, 0, 1, 0)
276
277 if dirY >= 0 then
278 local _, sy2, _ = localToWorld(levelerNode.node, -levelerNode.halfWidth, levelerNode.yOffset, levelerNode.zOffset + innerRadius)
279 local _, ey2, _ = localToWorld(levelerNode.node, levelerNode.halfWidth, levelerNode.yOffset, levelerNode.zOffset + innerRadius)
280
281 sy = math.max(sy, sy2)
282 ey = math.max(ey, ey2)
283 end
284
285 fillLevel = self:getFillUnitFillLevel(levelerNode.fillUnitIndex)
286 local delta = -(capacity-fillLevel)
287 local numHeightLimitChecks = levelerNode.numHeightLimitChecks
288 if numHeightLimitChecks > 0 then
289 local movementY = 0
290 for i=0,numHeightLimitChecks do
291 local t = i/numHeightLimitChecks
292 local xi = sx + (ex-sx)*t
293 local yi = sy + (ey-sy)*t
294 local zi = sz + (ez-sz)*t
295 local hi = DensityMapHeightUtil.getHeightAtWorldPos(xi,yi,zi)
296 movementY = math.max(movementY, hi-0.05 - yi) -- limit to 5cm below surface
297 end
298 if movementY > 0 then
299 sy = sy+movementY
300 ey = ey+movementY
301 end
302 end
303
304 levelerNode.lastPickUp, levelerNode.lineOffsetPickUp = DensityMapHeightUtil.tipToGroundAroundLine(self, delta, fillType, sx,sy,sz, ex,ey,ez, innerRadius, outerRadius, levelerNode.lineOffsetPickUp, true, nil)
305
306 --#debug DebugUtil.drawDebugLine(sx, sy, sz, ex, ey, ez, 1, 0, 0, innerRadius)
307
308 if levelerNode.lastPickUp < 0 then
309 if self.notifiyBunkerSilo ~= nil then
310 self:notifiyBunkerSilo(levelerNode.lastPickUp, fillType, (sx+ex) * 0.5, (sy+ey) * 0.5, (sz+ez) * 0.5)
311 end
312
313 levelerNode.lastPickUp = levelerNode.lastPickUp + spec.litersToPickup
314 spec.litersToPickup = 0
315
316 self:addFillUnitFillLevel(self:getOwnerFarmId(), levelerNode.fillUnitIndex, -levelerNode.lastPickUp, fillType, ToolType.UNDEFINED, nil)
317 pickedUpFillLevel = levelerNode.lastPickUp
318 end
319 end
320 end
321
322 local lastPickUpPerMS = -pickedUpFillLevel
323
324 spec.lastFillLevelMovedBuffer = spec.lastFillLevelMovedBuffer + lastPickUpPerMS
325 spec.lastFillLevelMovedBufferTimer = spec.lastFillLevelMovedBufferTimer + dt
326 if spec.lastFillLevelMovedBufferTimer > spec.lastFillLevelMovedBufferTime then
327 spec.lastFillLevelMovedTarget = spec.lastFillLevelMovedBuffer / spec.lastFillLevelMovedBufferTimer
328
329 spec.lastFillLevelMovedBufferTimer = 0
330 spec.lastFillLevelMovedBuffer = 0
331 end
332
333 if self.movingDirection < 0 and self.lastSpeed * 3600 > 0.5 then
334 spec.lastFillLevelMovedBuffer = 0
335 end
336
337 -- drop at node
338 fillLevel = self:getFillUnitFillLevel(levelerNode.fillUnitIndex)
339 if fillLevel > 0 then
340 local f = (fillLevel/capacity)
341 local width = MathUtil.lerp(levelerNode.halfMinDropWidth, levelerNode.halfMaxDropWidth, f)
342
343 local sx,sy,sz = localToWorld(levelerNode.node, -width, levelerNode.yOffset, levelerNode.zOffset)
344 local ex,ey,ez = localToWorld(levelerNode.node, width, levelerNode.yOffset, levelerNode.zOffset)
345
346 local yOffset = -0.15
347
348 levelerNode.lastDrop1, levelerNode.lineOffsetDrop1 = DensityMapHeightUtil.tipToGroundAroundLine(self, fillLevel, fillType, sx,sy+yOffset,sz, ex,ey+yOffset,ez, innerRadius, outerRadius, levelerNode.lineOffsetDrop1, true, nil)
349
350 --#debug DebugUtil.drawDebugLine(sx,sy+yOffset,sz, ex,ey+yOffset,ez, 1, 1, 1, innerRadius)
351
352 if levelerNode.lastDrop1 > 0 then
353 local leftOver = fillLevel - levelerNode.lastDrop1
354 if leftOver <= g_densityMapHeightManager:getMinValidLiterValue(fillType) then
355 levelerNode.lastDrop1 = fillLevel
356 spec.litersToPickup = spec.litersToPickup + leftOver
357 end
358 self:addFillUnitFillLevel(self:getOwnerFarmId(), levelerNode.fillUnitIndex, -levelerNode.lastDrop1, fillType, ToolType.UNDEFINED, nil)
359 end
360 end
361
362 -- drop further at front
363 fillLevel = self:getFillUnitFillLevel(levelerNode.fillUnitIndex)
364
365 if fillLevel > 0 then
366 local dropOffset = MathUtil.lerp(levelerNode.minDropDirOffset, levelerNode.maxDropDirOffset, spec.lastFillLevelMovedPct)
367
368 local wx, wy, wz = localToWorld(levelerNode.node, 0, levelerNode.yOffset, 0)
369 local tx, ty, tz = localToWorld(levelerNode.node, 0, levelerNode.yOffset, levelerNode.zOffset + dropOffset)
370
371 levelerNode.raycastLastFillType = fillType
372 levelerNode.raycastLastRadius = outerRadius
373 levelerNode.raycastHitObject = false
374 local rDirX, rDirY, rDirZ = tx-wx, ty-wy, tz-wz
375 local distance = MathUtil.vector3Length(rDirX, rDirY, rDirZ)
376 rDirX, rDirY, rDirZ = MathUtil.vector3Normalize(rDirX, rDirY, rDirZ)
377 raycastAll(wx, wy, wz, rDirX, rDirY, rDirZ, "onLevelerRaycastCallback", distance, levelerNode, CollisionFlag.STATIC_OBJECTS, false, true)
378
379 --#debug drawDebugLine(wx, wy, wz, 1, 0, 0, wx+rDirX*distance, wy+rDirY*distance, wz+rDirZ*distance, 0, 1, 0, true)
380 end
381 else
382 spec.lastFillLevelMovedBuffer = 0
383 spec.lastFillLevelMovedTarget = 0
384 end
385
386 -- call fill level changed callack to inform bunker silo about change
387 if pickedUpFillLevel < 0 and fillType ~= FillType.UNKNOWN then
388 self:notifiyBunkerSilo(pickedUpFillLevel, fillType)
389 end
390
391 if levelerNode.allowsSmoothing and (levelerNode.smoothDirection == 0 or self.movingDirection == levelerNode.smoothDirection) then
392 local smoothAmount = 0
393 if self.lastSpeedReal > 0.0002 then -- start smoothing if driving faster than 0.7km/h
394 smoothAmount = spec.smoothAccumulation + math.max(self.lastMovedDistance * 0.5, 0.0003*dt) -- smooth 1.2m per meter driving or at least 0.3m/s
395 local rounded = DensityMapHeightUtil.getRoundedHeightValue(smoothAmount)
396 spec.smoothAccumulation = smoothAmount - rounded
397 else
398 spec.smoothAccumulation = 0
399 end
400
401 if smoothAmount > 0 then
402 DensityMapHeightUtil.smoothAroundLine(levelerNode.node, levelerNode.width, levelerNode.smoothGroundRadius, levelerNode.smoothOverlap, smoothAmount)
403 end
404 end
405 end
406
407 local smoothFactor = 0.05
408 if spec.lastFillLevelMovedTarget == 0 then
409 smoothFactor = 0.2
410 end
411
412 spec.lastFillLevelMoved = spec.lastFillLevelMoved * (1-smoothFactor) + spec.lastFillLevelMovedTarget * smoothFactor
413 if spec.lastFillLevelMoved < 0.005 then
414 spec.lastFillLevelMoved = 0
415 end
416
417 local oldPercentage = spec.lastFillLevelMovedPct
418 spec.lastFillLevelMovedPct = math.max(math.min(spec.lastFillLevelMoved / spec.maxFillLevelPerMS, 1), 0)
419
420 if spec.lastFillLevelMovedPct ~= oldPercentage then
421 self:raiseDirtyFlags(spec.dirtyFlag)
422 end
423
424 if spec.forceNode ~= nil then
425 if self.movingDirection == spec.forceDir and spec.lastFillLevelMoved > 0 then
426 spec.lastForce = -spec.maxForce * spec.lastFillLevelMovedPct
427 local dx, dy, dz = localDirectionToWorld(spec.forceDirNode, 0, 0, spec.lastForce)
428 local px, py, pz = getCenterOfMass(spec.forceNode)
429
430 addForce(spec.forceNode, dx, dy, dz, px, py, pz, true)
431 end
432 end
433 end
434end

onWriteUpdateStream

Description
Called on on update
Definition
onWriteUpdateStream(integer streamId, table connection, integer dirtyMask)
Arguments
integerstreamIdstream ID
tableconnectionconnection
integerdirtyMaskdirty mask
Code
186function Leveler:onWriteUpdateStream(streamId, connection, dirtyMask)
187 if not connection:getIsServer() then
188 local spec = self.spec_leveler
189 if streamWriteBool(streamId, bitAND(dirtyMask, spec.dirtyFlag) ~= 0) then
190 streamWriteUIntN(streamId, spec.lastFillLevelMovedPct * Leveler.LEVEL_MAX_VALUE, Leveler.LEVEL_NUM_BITS)
191 end
192 end
193end

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
21function Leveler.prerequisitesPresent(specializations)
22 return SpecializationUtil.hasSpecialization(FillUnit, specializations) and SpecializationUtil.hasSpecialization(BunkerSiloInteractor, specializations)
23end

registerEventListeners

Description
Definition
registerEventListeners()
Code
88function Leveler.registerEventListeners(vehicleType)
89 SpecializationUtil.registerEventListener(vehicleType, "onLoad", Leveler)
90 SpecializationUtil.registerEventListener(vehicleType, "onDelete", Leveler)
91 SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Leveler)
92 SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Leveler)
93 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Leveler)
94end

registerFunctions

Description
Definition
registerFunctions()
Code
74function Leveler.registerFunctions(vehicleType)
75 SpecializationUtil.registerFunction(vehicleType, "getIsLevelerPickupNodeActive", Leveler.getIsLevelerPickupNodeActive)
76 SpecializationUtil.registerFunction(vehicleType, "loadLevelerNodeFromXML", Leveler.loadLevelerNodeFromXML)
77 SpecializationUtil.registerFunction(vehicleType, "onLevelerRaycastCallback", Leveler.onLevelerRaycastCallback)
78end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
82function Leveler.registerOverwrittenFunctions(vehicleType)
83 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsAttacherJointControlDampingAllowed", Leveler.getIsAttacherJointControlDampingAllowed)
84end