LUADOC - Farming Simulator 22

Script v1_7_1_0

Engine v1_7_1_0

Foundation Reference

BunkerSilo

Description
Class for bunker silo
Parent
Object
Functions

clearSiloArea

Description
Clear the silo area
Definition
clearSiloArea()
Code
784function BunkerSilo:clearSiloArea()
785 local xs, _, zs = getWorldTranslation(self.bunkerSiloArea.start)
786 local xw, _, zw = getWorldTranslation(self.bunkerSiloArea.width)
787 local xh, _, zh = getWorldTranslation(self.bunkerSiloArea.height)
788 DensityMapHeightUtil.clearArea(xs,zs, xw,zw, xh,zh)
789end

delete

Description
Deleting bunker silo object
Definition
delete()
Code
180function BunkerSilo:delete()
181 if self.interactionTriggerNode ~= nil then
182 removeTrigger(self.interactionTriggerNode)
183 end
184
185 g_densityMapHeightManager:removeFixedFillTypesArea(self.bunkerSiloArea)
186 g_densityMapHeightManager:removeConvertingFillTypeAreas(self.bunkerSiloArea)
187
188 g_messageCenter:unsubscribeAll(self)
189
190 g_currentMission.activatableObjectsSystem:removeActivatable(self.activatable)
191 BunkerSilo:superClass().delete(self)
192end

getBunkerAreaOffset

Description
Get bunker area offset
Definition
getBunkerAreaOffset(boolean updateAtFront, float offset, integer fillType)
Arguments
booleanupdateAtFrontupdate at front
floatoffsetoffset
integerfillTypefill type
Return Values
floatoffsetoffset
Code
649function BunkerSilo:getBunkerAreaOffset(updateAtFront, offset, fillType)
650 local area = self.bunkerSiloArea
651
652 local hx, hz = area.dhx_norm, area.dhz_norm
653 local hl = MathUtil.vector3Length(area.dhx, area.dhy, area.dhz)
654
655 while offset <= (hl - 1) do
656 local pos = offset
657 if not updateAtFront then
658 pos = hl - offset - 1
659 end
660 local d1x,d1z = pos*hx, pos*hz
661 local d2x,d2z = (pos+1)*hx, (pos+1)*hz
662
663 local a0x, a0z = area.sx + d1x, area.sz + d1z
664 local a1x, a1z = area.wx + d1x, area.wz + d1z
665 local a2x, a2z = area.sx + d2x, area.sz + d2z
666
667 local fillLevel = DensityMapHeightUtil.getFillLevelAtArea(fillType, a0x,a0z, a1x,a1z, a2x,a2z)
668 if fillLevel > 0 then
669 return offset
670 end
671 offset = offset + 1
672 end
673
674 return math.max(hl - 1, 0)
675end

getCanCloseSilo

Description
Get can close silo
Definition
getCanCloseSilo()
Return Values
booleancanClosecan close silo
Code
758function BunkerSilo:getCanCloseSilo()
759 return self.state == BunkerSilo.STATE_FILL and self.fillLevel > 0 and self.compactedPercent >= 100
760end

getCanInteract

Description
Get can interact with silo
Definition
getCanInteract(boolean showInformationOnly)
Arguments
booleanshowInformationOnlyshow information only
Return Values
booleancanInteractcan interact
Code
733function BunkerSilo:getCanInteract(showInformationOnly)
734 if showInformationOnly then
735 if g_currentMission.controlPlayer and self.playerInRange then
736 return true
737 end
738 if not g_currentMission.controlPlayer and g_currentMission.controlledVehicle ~= nil then
739 for vehicle in pairs(self.vehiclesInRange) do
740 if vehicle:getIsActiveForInput(true) and vehicle == g_currentMission.controlledVehicle then
741 return true
742 end
743 end
744 end
745 else
746 if g_currentMission.controlPlayer and self.playerInRange then
747 --if next(self.vehiclesInRange) == nil then
748 return true
749 --end
750 end
751 end
752 return false
753end

getCanOpenSilo

Description
Get can open silo
Definition
getCanOpenSilo()
Return Values
booleancanOpencan open silo
Code
765function BunkerSilo:getCanOpenSilo()
766 if not (self.state == BunkerSilo.STATE_FERMENTED or self.state == BunkerSilo.STATE_DRAIN) then
767 return false
768 end
769 local ix,iy,iz = self:getInteractionPosition()
770 if ix ~= nil then
771 local closerToFront = self:getIsCloserToFront(ix,iy,iz)
772 if closerToFront and not self.isOpenedAtFront then
773 return true
774 end
775 if not closerToFront and not self.isOpenedAtBack then
776 return true
777 end
778 end
779 return false
780end

getInteractionPosition

Description
Get interact position
Definition
getInteractionPosition()
Return Values
floatxx world position
floatyy world position
floatzz world position
Code
835function BunkerSilo:getInteractionPosition()
836 if g_currentMission.controlPlayer and self.playerInRange then
837 return getWorldTranslation(g_currentMission.player.rootNode)
838 else
839 if self.vehiclesInRange[g_currentMission.currentVehicle] ~= nil then
840 return getWorldTranslation(self.vehiclesInRange[g_currentMission.currentVehicle].components[1].node)
841 end
842 end
843 return nil
844end

getIsCloserToFront

Description
Get is closer to front
Definition
getIsCloserToFront(float ix, float iy, float iz)
Arguments
floatixx position
floatiyy position
floatizz position
Return Values
booleanisCloserToFrontis closer to front
Code
713function BunkerSilo:getIsCloserToFront(ix,iy,iz)
714 local area = self.bunkerSiloArea
715
716 local x = area.sx + (0.5*area.dwx) + (area.offsetFront * area.dhx_norm)
717 local y = area.sy + (0.5*area.dwy) + (area.offsetFront * area.dhy_norm)
718 local z = area.sz + (0.5*area.dwz) + (area.offsetFront * area.dhz_norm)
719 local distFront = MathUtil.vector3Length(x-ix, y-iy, z-iz)
720
721 x = area.sx + (0.5*area.dwx) + area.dhx - (area.offsetBack * area.dhx_norm)
722 y = area.sy + (0.5*area.dwy) + area.dhy - (area.offsetBack * area.dhy_norm)
723 z = area.sz + (0.5*area.dwz) + area.dhz - (area.offsetBack * area.dhz_norm)
724 local distBack = MathUtil.vector3Length(x-ix, y-iy, z-iz)
725
726 return distFront < distBack
727end

interactionTriggerCallback

Description
interactionTriggerCallback
Definition
interactionTriggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay, integer otherId)
Arguments
integertriggerIdid of trigger
integerotherIdid of actor
booleanonEnteron enter
booleanonLeaveon leave
booleanonStayon stay
integerotherIdid of other actor
Code
854function BunkerSilo:interactionTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId)
855 if onEnter or onLeave then
856 if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then
857 if onEnter then
858 self.playerInRange = true
859 g_currentMission.activatableObjectsSystem:addActivatable(self.activatable)
860 else
861 self.playerInRange = false
862 if self.numVehiclesInRange == 0 then
863 g_currentMission.activatableObjectsSystem:removeActivatable(self.activatable)
864 end
865 end
866 else
867 --local vehicle = g_currentMission.nodeToObject[otherId]
868 local vehicle = g_currentMission.nodeToObject[otherShapeId]
869 if vehicle ~= nil then
870 if onEnter then
871 if self.vehiclesInRange[vehicle] == nil then
872 self.vehiclesInRange[vehicle] = true
873 self.numVehiclesInRange = self.numVehiclesInRange + 1
874
875 g_currentMission.activatableObjectsSystem:addActivatable(self.activatable)
876
877 -- add callback if shovel
878 if vehicle.setBunkerSiloInteractorCallback ~= nil then
879 vehicle:setBunkerSiloInteractorCallback(BunkerSilo.onChangedFillLevelCallback, self)
880 end
881 end
882 else
883 if self.vehiclesInRange[vehicle] then
884 self.vehiclesInRange[vehicle] = nil
885 self.numVehiclesInRange = self.numVehiclesInRange - 1
886
887 if self.numVehiclesInRange == 0 and not self.playerInRange then
888 g_currentMission.activatableObjectsSystem:removeActivatable(self.activatable)
889 end
890
891 -- remove callback if shovel
892 if vehicle.setBunkerSiloInteractorCallback ~= nil then
893 vehicle:setBunkerSiloInteractorCallback(nil)
894 end
895 end
896 end
897 end
898 end
899 end
900end

load

Description
Load bunker silo
Definition
load(table components, table xmlFile, string key, table i3dMappings)
Arguments
tablecomponentscomponents
tablexmlFilexml file object
stringkeyxml key
tablei3dMappingsi3dMappings
Return Values
booleansuccesssuccess
Code
86function BunkerSilo:load(components, xmlFile, key, i3dMappings)
87 self.bunkerSiloArea.start = xmlFile:getValue(key..".area#startNode", nil, components, i3dMappings)
88 self.bunkerSiloArea.width = xmlFile:getValue(key..".area#widthNode", nil, components, i3dMappings)
89 self.bunkerSiloArea.height = xmlFile:getValue(key..".area#heightNode", nil, components, i3dMappings)
90
91 self.bunkerSiloArea.sx, self.bunkerSiloArea.sy, self.bunkerSiloArea.sz = getWorldTranslation(self.bunkerSiloArea.start)
92 self.bunkerSiloArea.wx, self.bunkerSiloArea.wy, self.bunkerSiloArea.wz = getWorldTranslation(self.bunkerSiloArea.width)
93 self.bunkerSiloArea.hx, self.bunkerSiloArea.hy, self.bunkerSiloArea.hz = getWorldTranslation(self.bunkerSiloArea.height)
94
95 self.bunkerSiloArea.dhx = self.bunkerSiloArea.hx - self.bunkerSiloArea.sx
96 self.bunkerSiloArea.dhy = self.bunkerSiloArea.hy - self.bunkerSiloArea.sy
97 self.bunkerSiloArea.dhz = self.bunkerSiloArea.hz - self.bunkerSiloArea.sz
98 self.bunkerSiloArea.dhx_norm, self.bunkerSiloArea.dhy_norm, self.bunkerSiloArea.dhz_norm = MathUtil.vector3Normalize(self.bunkerSiloArea.dhx, self.bunkerSiloArea.dhy, self.bunkerSiloArea.dhz)
99
100 self.bunkerSiloArea.dwx = self.bunkerSiloArea.wx - self.bunkerSiloArea.sx
101 self.bunkerSiloArea.dwy = self.bunkerSiloArea.wy - self.bunkerSiloArea.sy
102 self.bunkerSiloArea.dwz = self.bunkerSiloArea.wz - self.bunkerSiloArea.sz
103 self.bunkerSiloArea.dwx_norm, self.bunkerSiloArea.dwy_norm, self.bunkerSiloArea.dwz_norm = MathUtil.vector3Normalize(self.bunkerSiloArea.dwx, self.bunkerSiloArea.dwy, self.bunkerSiloArea.dwz)
104
105 self.bunkerSiloArea.inner = {}
106 self.bunkerSiloArea.inner.start = xmlFile:getValue(key..".innerArea#startNode", self.bunkerSiloArea.start, components, i3dMappings)
107 self.bunkerSiloArea.inner.width = xmlFile:getValue(key..".innerArea#widthNode", self.bunkerSiloArea.width, components, i3dMappings)
108 self.bunkerSiloArea.inner.height = xmlFile:getValue(key..".innerArea#heightNode", self.bunkerSiloArea.height, components, i3dMappings)
109
110 self.bunkerSiloArea.inner.sx, self.bunkerSiloArea.inner.sy, self.bunkerSiloArea.inner.sz = getWorldTranslation(self.bunkerSiloArea.inner.start)
111 self.bunkerSiloArea.inner.wx, self.bunkerSiloArea.inner.wy, self.bunkerSiloArea.inner.wz = getWorldTranslation(self.bunkerSiloArea.inner.width)
112 self.bunkerSiloArea.inner.hx, self.bunkerSiloArea.inner.hy, self.bunkerSiloArea.inner.hz = getWorldTranslation(self.bunkerSiloArea.inner.height)
113
114 self.interactionTriggerNode = xmlFile:getValue(key..".interactionTrigger#node", nil, components, i3dMappings)
115 if self.interactionTriggerNode ~= nil then
116 addTrigger(self.interactionTriggerNode, "interactionTriggerCallback", self)
117 end
118
119 self.acceptedFillTypes = {}
120 local data = xmlFile:getValue(key.."#acceptedFillTypes", "chaff grass_windrow dryGrass_windrow"):split(" ")
121 for i=1, table.getn(data) do
122 local fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(data[i])
123 if fillTypeIndex ~= nil then
124 self.acceptedFillTypes[fillTypeIndex] = true
125 else
126 Logging.warning("'%s' is an invalid fillType for bunkerSilo '%s'!", tostring(data[i]), key.."#acceptedFillTypes")
127 end
128 end
129
130 local inputFillTypeName = xmlFile:getValue(key.."#inputFillType", "chaff")
131 local inputFillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(inputFillTypeName)
132 if inputFillTypeIndex ~= nil then
133 self.inputFillType = inputFillTypeIndex
134 else
135 Logging.warning("'%s' is an invalid input fillType for bunkerSilo '%s'!", tostring(inputFillTypeName), key.."#inputFillType")
136 end
137
138 local outputFillTypeName = xmlFile:getValue(key.."#outputFillType", "silage")
139 local outputFillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(outputFillTypeName)
140 if outputFillTypeIndex ~= nil then
141 self.outputFillType = outputFillTypeIndex
142 else
143 Logging.warning("'%s' is an invalid output fillType for bunkerSilo '%s'!", tostring(outputFillTypeName), key.."#outputFillType")
144 end
145
146 g_densityMapHeightManager:setConvertingFillTypeAreas(self.bunkerSiloArea, self.acceptedFillTypes, self.inputFillType)
147
148 self.distanceToCompactedFillLevel = xmlFile:getValue(key.."#distanceToCompactedFillLevel", self.distanceToCompactedFillLevel)
149 self.openingLength = xmlFile:getValue(key.."#openingLength", 5)
150
151 local leftWallNode = xmlFile:getValue(key .. ".wallLeft#node", nil, components, i3dMappings)
152 if leftWallNode ~= nil then
153 self.wallLeft = {}
154 self.wallLeft.node = leftWallNode
155 self.wallLeft.visible = true
156 self.wallLeft.collision = xmlFile:getValue(key .. ".wallLeft#collision", nil, components, i3dMappings)
157 end
158
159 local rightWallNode = xmlFile:getValue(key .. ".wallRight#node", nil, components, i3dMappings)
160 if rightWallNode ~= nil then
161 self.wallRight = {}
162 self.wallRight.node = rightWallNode
163 self.wallRight.visible = true
164 self.wallRight.collision = xmlFile:getValue(key .. ".wallRight#collision", nil, components, i3dMappings)
165 end
166
167 self.fillLevel = 0
168
169 -- adjust timings to difficulty
170 local difficultyMultiplier = g_currentMission.missionInfo.economicDifficulty
171 self.distanceToCompactedFillLevel = self.distanceToCompactedFillLevel/difficultyMultiplier
172
173 self:setState(BunkerSilo.STATE_FILL)
174
175 return true
176end

loadFromXMLFile

Description
Loading from attributes and nodes
Definition
loadFromXMLFile(integer xmlFile, string key)
Arguments
integerxmlFileid of xml object
stringkeykey
Return Values
booleansuccesssuccess
Code
284function BunkerSilo:loadFromXMLFile(xmlFile, key)
285
286 local state = xmlFile:getValue(key.."#state")
287 if state ~= nil then
288 if state >= 0 and state < BunkerSilo.NUM_STATES then
289 self:setState(state)
290 end
291 end
292
293 local fillLevel = xmlFile:getValue(key.."#fillLevel")
294 if fillLevel ~= nil then
295 self.fillLevel = fillLevel
296 end
297 local compactedFillLevel = xmlFile:getValue(key.."#compactedFillLevel")
298 if compactedFillLevel ~= nil then
299 self.compactedFillLevel = MathUtil.clamp(compactedFillLevel, 0, self.fillLevel)
300 end
301 self.compactedPercent = MathUtil.getFlooredPercent(math.min(self.compactedFillLevel, self.fillLevel), self.fillLevel)
302
303 local fermentingTime = xmlFile:getValue(key.."#fermentingTime")
304 if fermentingTime ~= nil then
305 -- Convert to percent
306 self.fermentingPercent = fermentingTime / (BunkerSilo.MILLISECONDS_PER_DAY * g_currentMission.environment.daysPerPeriod)
307 end
308
309 self.isOpenedAtFront = xmlFile:getValue(key.."#openedAtFront", false)
310 self.isOpenedAtBack = xmlFile:getValue(key.."#openedAtBack", false)
311
312 if self.isOpenedAtFront then
313 self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.outputFillType)
314 else
315 self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.fermentingFillType)
316 end
317 if self.isOpenedAtBack then
318 self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.outputFillType)
319 else
320 self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.fermentingFillType)
321 end
322
323 if self.fillLevel > 0 and self.state == BunkerSilo.STATE_DRAIN then
324 local area = self.bunkerSiloArea
325 local offWx = area.wx - area.sx
326 local offWz = area.wz - area.sz
327 local offW = math.sqrt(offWx*offWx + offWz*offWz)
328
329 local offHx = area.hx - area.sx
330 local offHz = area.hz - area.sz
331 local offH = math.sqrt(offHx*offHx + offHz*offHz)
332
333 if offW > 0.001 and offH > 0.001 then
334 -- offset by 0.9m in each direction (and max 45%)
335 local offWScale = math.min(0.45, 0.9 / offW)
336 offWx = offWx * offWScale
337 offWz = offWz * offWScale
338
339 local offHScale = math.min(0.45, 0.9 / offH)
340 offHx = offHx * offHScale
341 offHz = offHz * offHScale
342
343 local innerFillLevel1 = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx+offWx+offHx,area.sz+offWz+offHz, area.wx-offWx+offHx,area.wz-offWz+offHz, area.hx+offWx-offHx,area.hz+offWz-offHz)
344 local innerFillLevel2 = DensityMapHeightUtil.getFillLevelAtArea(self.outputFillType, area.sx+offWx+offHx,area.sz+offWz+offHz, area.wx-offWx+offHx,area.wz-offWz+offHz, area.hx+offWx-offHx,area.hz+offWz-offHz)
345 local innerFillLevel = innerFillLevel1 + innerFillLevel2
346 if innerFillLevel < self.emptyThreshold*0.5 then
347 DensityMapHeightUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.fermentingFillType)
348 DensityMapHeightUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.outputFillType)
349 self:setState(BunkerSilo.STATE_FILL, false)
350 end
351 end
352
353 DensityMapHeightUtil.changeFillTypeAtArea(area.sx, area.sz, area.wx, area.wz, area.hx, area.hz, self.inputFillType, self.outputFillType)
354 elseif self.fillLevel > 0 and (self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED) then
355 local area = self.bunkerSiloArea
356 DensityMapHeightUtil.changeFillTypeAtArea(area.sx, area.sz, area.wx, area.wz, area.hx, area.hz, self.inputFillType, self.fermentingFillType)
357 elseif self.state == BunkerSilo.STATE_FILL then
358 local area = self.bunkerSiloArea
359 local fermentingFillLevel, fermentingPixels, totalFermentingPixels = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
360 -- Set to fermented state if more than 50% of the area is filled with fermenting fill type
361 if fermentingFillLevel > self.emptyThreshold and fermentingPixels > 0.5 * totalFermentingPixels then
362 local inputFillLevel, inputPixels, totalInputPixels = DensityMapHeightUtil.getFillLevelAtArea(self.inputFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
363 -- Only change if less than 10% is filled with input type (chaff) (ie. the silo is not being filled)
364 if inputPixels < 0.1*totalInputPixels then
365 self:setState(BunkerSilo.STATE_FERMENTED, false)
366 end
367 end
368 end
369
370 return true
371end

new

Description
Creating bunker silo object
Definition
new(boolean isServer, boolean isClient, table customMt)
Arguments
booleanisServeris server
booleanisClientis client
tablecustomMtcustomMt
Return Values
tableinstanceInstance of object
Code
33function BunkerSilo.new(isServer, isClient, customMt)
34 local self = Object.new(isServer, isClient, customMt or BunkerSilo_mt)
35
36 self.interactionTriggerNode = nil
37
38 self.bunkerSiloArea = {}
39 self.bunkerSiloArea.offsetFront = 0
40 self.bunkerSiloArea.offsetBack = 0
41
42 self.acceptedFillTypes = {}
43
44 self.inputFillType = FillType.CHAFF
45 self.outputFillType = FillType.SILAGE
46 self.fermentingFillType = FillType.TARP
47
48 self.isOpenedAtFront = false
49 self.isOpenedAtBack = false
50 self.distanceToCompactedFillLevel = 100
51
52 self.fermentingPercent = 0
53
54 self.fillLevel = 0
55 self.compactedFillLevel = 0
56 self.compactedPercent = 0
57 self.emptyThreshold = 100
58
59 self.playerInRange = false
60 self.vehiclesInRange = {}
61 self.numVehiclesInRange = 0
62
63 self.siloIsFullWarningTimer = 0
64 self.siloIsFullWarningDuration = 2000
65
66 self.updateTimer = 0
67
68 self.activatable = BunkerSiloActivatable.new(self)
69
70 self.state = BunkerSilo.STATE_FILL
71
72 self.bunkerSiloDirtyFlag = self:getNextDirtyFlag()
73
74 g_messageCenter:subscribe(MessageType.HOUR_CHANGED, self.onHourChanged, self)
75
76 return self
77end

onChangedFillLevelCallback

Description
Called if fill level changed
Definition
onChangedFillLevelCallback(table vehicle, integer fillDelta, integer fillType)
Arguments
tablevehiclevehicle
integerfillDeltafill delta
integerfillTypefill type
Code
907function BunkerSilo.onChangedFillLevelCallback(self, vehicle, fillDelta, fillType, x, y, z)
908 if fillDelta >= 0 then
909 return
910 end
911
912 local area = self.bunkerSiloArea
913 if x == nil or y == nil or z == nil then
914 x, y, z = getWorldTranslation(vehicle.components[1].node)
915 end
916
917 local closerToFront = self:getIsCloserToFront(x, y, z)
918 local length = self.openingLength
919
920 if closerToFront then
921 if self.isOpenedAtFront then
922 local p1 = MathUtil.getProjectOnLineParameter(x,z, area.sx,area.sz, area.dhx_norm,area.dhz_norm)
923 if p1 > area.offsetFront - length then
924 local offset = self:getBunkerAreaOffset(true, area.offsetFront, self.fermentingFillType)
925 local targetOffset = math.max(p1, offset) + length
926
927 self:switchFillTypeAtOffset(true, area.offsetFront, targetOffset - area.offsetFront)
928 area.offsetFront = targetOffset
929 end
930 end
931 else
932 if self.isOpenedAtBack then
933 local p1 = MathUtil.getProjectOnLineParameter(x,z, area.hx,area.hz, -area.dhx_norm,-area.dhz_norm)
934 if p1 > area.offsetBack - length then
935 local offset = self:getBunkerAreaOffset(false, area.offsetBack, self.fermentingFillType)
936 local targetOffset = math.max(p1, offset) + length
937
938 self:switchFillTypeAtOffset(false, area.offsetBack, targetOffset - area.offsetBack)
939 area.offsetBack = targetOffset
940 end
941 end
942 end
943end

onCreate

Description
Creating bunker silo object
Definition
onCreate(integer id)
Arguments
integeridnode id
Code
23function BunkerSilo:onCreate(id)
24 Logging.error("BunkerSilo.onCreate is deprecated!")
25end

openSilo

Description
Open silo
Definition
openSilo(float px, float py, float pz)
Arguments
floatpxx player position
floatpyy player position
floatpzz player position
Code
624function BunkerSilo:openSilo(px,py,pz)
625 self:setState(BunkerSilo.STATE_DRAIN, true)
626
627 self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.fermentingFillType)
628 self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.fermentingFillType)
629
630 -- check which side is closer to player
631 local openAtFront = self:getIsCloserToFront(px,py,pz)
632 if openAtFront and not self.isOpenedAtFront then
633 self:switchFillTypeAtOffset(true, self.bunkerSiloArea.offsetFront, self.openingLength)
634 self.isOpenedAtFront = true
635 self:raiseDirtyFlags(self.bunkerSiloDirtyFlag)
636 elseif not self.isOpenedAtBack then
637 self:switchFillTypeAtOffset(false, self.bunkerSiloArea.offsetBack, self.openingLength)
638 self.isOpenedAtBack = true
639 self:raiseDirtyFlags(self.bunkerSiloDirtyFlag)
640 end
641end

readStream

Description
Called on client side on join
Definition
readStream(integer streamId, table connection)
Arguments
integerstreamIdstream ID
tableconnectionconnection
Code
198function BunkerSilo:readStream(streamId, connection)
199 BunkerSilo:superClass().readStream(self, streamId, connection)
200 if connection:getIsServer() then
201 local state = streamReadUIntN(streamId, 3)
202 self:setState(state)
203 self.isOpenedAtFront = streamReadBool(streamId)
204 self.isOpenedAtBack = streamReadBool(streamId)
205 self.fillLevel = streamReadFloat32(streamId)
206 self.compactedPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5)
207 self.fermentingPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5)
208 end
209end

readUpdateStream

Description
Called on client side on update
Definition
readUpdateStream(integer streamId, integer timestamp, table connection)
Arguments
integerstreamIdstream ID
integertimestamptimestamp
tableconnectionconnection
Code
232function BunkerSilo:readUpdateStream(streamId, timestamp, connection)
233 BunkerSilo:superClass().readUpdateStream(self, streamId, timestamp, connection)
234 if connection:getIsServer() then
235 if streamReadBool(streamId) then
236 local state = streamReadUIntN(streamId, 3)
237 if state ~= self.state then
238 self:setState(state, true)
239 end
240
241 self.fillLevel = streamReadFloat32(streamId)
242 self.isOpenedAtFront = streamReadBool(streamId)
243 self.isOpenedAtBack = streamReadBool(streamId)
244
245 if self.state == BunkerSilo.STATE_FILL then
246 self.compactedPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5)
247 elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then
248 self.fermentingPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5)
249 end
250 end
251 end
252end

registerSavegameXMLPaths

Description
Definition
registerSavegameXMLPaths()
Code
972function BunkerSilo.registerSavegameXMLPaths(schema, basePath)
973 schema:register(XMLValueType.INT, basePath .. "#state", "Current silo state (FILL = 0, CLOSED = 1, FERMENTED = 2, DRAIN = 3)", 0)
974 schema:register(XMLValueType.FLOAT, basePath .. "#fillLevel", "Current fill level")
975 schema:register(XMLValueType.FLOAT, basePath .. "#compactedFillLevel", "Compacted fill level")
976 schema:register(XMLValueType.FLOAT, basePath .. "#fermentingTime", "Fermenting time")
977 schema:register(XMLValueType.BOOL, basePath .. "#openedAtFront", "Is opened at front", false)
978 schema:register(XMLValueType.BOOL, basePath .. "#openedAtBack", "Is opened at back", false)
979end

registerXMLPaths

Description
Definition
registerXMLPaths()
Code
947function BunkerSilo.registerXMLPaths(schema, basePath)
948 schema:register(XMLValueType.NODE_INDEX, basePath .. ".area#startNode", "Area start node (placed in the middle of the walls)")
949 schema:register(XMLValueType.NODE_INDEX, basePath .. ".area#widthNode", "Area width node (placed in the middle of the walls)")
950 schema:register(XMLValueType.NODE_INDEX, basePath .. ".area#heightNode", "Area height node (placed in the middle of the walls)")
951
952 schema:register(XMLValueType.NODE_INDEX, basePath .. ".innerArea#startNode", "Inner area start node (Used to detect fill level - placed 25cm from inner walls)")
953 schema:register(XMLValueType.NODE_INDEX, basePath .. ".innerArea#widthNode", "Inner area width node (Used to detect fill level - placed 25cm from inner walls)")
954 schema:register(XMLValueType.NODE_INDEX, basePath .. ".innerArea#heightNode", "Inner area height node (Used to detect fill level - placed 25cm from inner walls)")
955
956 schema:register(XMLValueType.NODE_INDEX, basePath .. ".wallLeft#node", "Left wall node")
957 schema:register(XMLValueType.NODE_INDEX, basePath .. ".wallLeft#collision", "Left wall collision")
958 schema:register(XMLValueType.NODE_INDEX, basePath .. ".wallRight#node", "Right wall node")
959 schema:register(XMLValueType.NODE_INDEX, basePath .. ".wallRight#collision", "Right wall collision")
960
961 schema:register(XMLValueType.NODE_INDEX, basePath .. ".interactionTrigger#node", "Interaction trigger node")
962 schema:register(XMLValueType.STRING, basePath .. "#acceptedFillTypes", "Accepted fill types", "chaff grass_windrow dryGrass_windrow")
963 schema:register(XMLValueType.STRING, basePath .. "#inputFillType", "Input fill type", "chaff")
964 schema:register(XMLValueType.STRING, basePath .. "#outputFillType", "Output fill type", "silage")
965
966 schema:register(XMLValueType.FLOAT, basePath .. "#distanceToCompactedFillLevel", "Distance to drive on bunker silo for full compaction", 100)
967 schema:register(XMLValueType.FLOAT, basePath .. "#openingLength", "Opening length", 5)
968end

saveToXMLFile

Description
Save to XML file
Definition
saveToXMLFile(integer xmlFile, string key, table usedModNames)
Arguments
integerxmlFileid of xml object
stringkeykey
tableusedModNameslist of use dmod names
Code
378function BunkerSilo:saveToXMLFile(xmlFile, key, usedModNames)
379 xmlFile:setValue(key.."#state", self.state)
380 xmlFile:setValue(key.."#fillLevel", self.fillLevel)
381 xmlFile:setValue(key.."#compactedFillLevel", self.compactedFillLevel)
382 xmlFile:setValue(key.."#fermentingTime", self.fermentingPercent * BunkerSilo.MILLISECONDS_PER_DAY * g_currentMission.environment.daysPerPeriod)
383 xmlFile:setValue(key.."#openedAtFront", self.isOpenedAtFront)
384 xmlFile:setValue(key.."#openedAtBack", self.isOpenedAtBack)
385end

setState

Description
Set state
Definition
setState(boolean state, boolean showNotification)
Arguments
booleanstatenew state
booleanshowNotificationshow notification
Code
537function BunkerSilo:setState(state, showNotification)
538 if state ~= self.state then
539 if state == BunkerSilo.STATE_FILL then
540 self.fermentingPercent = 0
541 self.compactedFillLevel = 0
542 self.compactedPercent = 0
543 self.isOpenedAtFront = false
544 self.isOpenedAtBack = false
545 self.bunkerSiloArea.offsetFront = 0
546 self.bunkerSiloArea.offsetBack = 0
547
548 if showNotification then
549 self:showBunkerMessage(g_i18n:getText("ingameNotification_bunkerSiloIsEmpty"))
550 end
551
552 if self.isServer then
553 g_densityMapHeightManager:removeFixedFillTypesArea(self.bunkerSiloArea)
554 g_densityMapHeightManager:setConvertingFillTypeAreas(self.bunkerSiloArea, self.acceptedFillTypes, self.inputFillType)
555 end
556
557 elseif state == BunkerSilo.STATE_CLOSED then
558
559 if self.isServer then
560 -- change fillType
561 local area = self.bunkerSiloArea
562 local offsetFront = self:getBunkerAreaOffset(true, 0, self.inputFillType)
563 local offsetBack = self:getBunkerAreaOffset(false, 0, self.inputFillType)
564
565 local x0 = area.sx + (offsetFront * area.dhx_norm)
566 local z0 = area.sz + (offsetFront * area.dhz_norm)
567 local x1 = x0 + area.dwx
568 local z1 = z0 + area.dwz
569 local x2 = area.sx + area.dhx - (offsetBack * area.dhx_norm)
570 local z2 = area.sz + area.dhz - (offsetBack * area.dhz_norm)
571
572 local changed = DensityMapHeightUtil.changeFillTypeAtArea(x0,z0, x1,z1, x2,z2, self.inputFillType, self.fermentingFillType)
573
574 g_densityMapHeightManager:removeFixedFillTypesArea(self.bunkerSiloArea)
575 g_densityMapHeightManager:removeConvertingFillTypeAreas(self.bunkerSiloArea)
576 end
577
578 if showNotification then
579 self:showBunkerMessage(g_i18n:getText("ingameNotification_bunkerSiloCovered"))
580 end
581
582 elseif state == BunkerSilo.STATE_FERMENTED then
583
584 if showNotification then
585 self:showBunkerMessage(g_i18n:getText("ingameNotification_bunkerSiloDoneFermenting"))
586 end
587
588 elseif state == BunkerSilo.STATE_DRAIN then
589
590 self.bunkerSiloArea.offsetFront = 0
591 self.bunkerSiloArea.offsetBack = 0
592
593 if showNotification then
594 self:showBunkerMessage(g_i18n:getText("ingameNotification_bunkerSiloOpened"))
595 end
596
597 if self.isServer then
598 g_densityMapHeightManager:removeConvertingFillTypeAreas(self.bunkerSiloArea)
599 local fillTypes = {}
600 fillTypes[self.outputFillType] = true
601 g_densityMapHeightManager:setFixedFillTypesArea(self.bunkerSiloArea, fillTypes)
602 end
603
604 end
605
606 self.state = state
607 if self.isServer then
608 self:raiseDirtyFlags(self.bunkerSiloDirtyFlag)
609 end
610 end
611end

switchFillTypeAtOffset

Description
Switch fill type at offset
Definition
switchFillTypeAtOffset(boolean switchAtFront, float offset, float length)
Arguments
booleanswitchAtFrontswitch at front
floatoffsetoffset
floatlengthlength
Code
682function BunkerSilo:switchFillTypeAtOffset(switchAtFront, offset, length)
683
684 local fillType = self.fermentingFillType
685 local newFillType = self.outputFillType
686
687 local a0x, a0z
688 local a1x, a1z
689 local a2x, a2z
690
691 local area = self.bunkerSiloArea
692
693 if switchAtFront then
694 a0x, a0z = area.sx + (offset * area.dhx_norm), area.sz + (offset * area.dhz_norm)
695 a1x, a1z = a0x + area.dwx, a0z + area.dwz
696 a2x, a2z = area.sx + ((offset + length) * area.dhx_norm), area.sz + ((offset + length) * area.dhz_norm)
697 else
698 a0x, a0z = area.hx - (offset * area.dhx_norm), area.hz - (offset * area.dhz_norm)
699 a1x, a1z = a0x + area.dwx, a0z + area.dwz
700 a2x, a2z = area.hx - ((offset + length) * area.dhx_norm), area.hz - ((offset + length) * area.dhz_norm)
701 end
702
703 DensityMapHeightUtil.changeFillTypeAtArea(a0x,a0z, a1x,a1z, a2x,a2z, fillType, newFillType)
704
705end

update

Description
Update
Definition
update(float dt)
Arguments
floatdttime since last call in ms
Code
390function BunkerSilo:update(dt)
391 if self:getCanInteract(true) then
392 local fillTypeIndex = self.inputFillType
393 if self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED or self.state == BunkerSilo.STATE_DRAIN then
394 fillTypeIndex = self.outputFillType
395 end
396 local fillTypeName = ""
397 local fillType = g_fillTypeManager:getFillTypeByIndex(fillTypeIndex)
398 if fillType ~= nil then
399 fillTypeName = fillType.title
400 end
401 if self.state == BunkerSilo.STATE_FILL then
402 g_currentMission:addExtraPrintText(g_i18n:getText("info_fillLevel")..string.format(" %s: %d", fillTypeName, self.fillLevel))
403 g_currentMission:addExtraPrintText(g_i18n:getText("info_compacting")..string.format(" %d%%", self.compactedPercent))
404 elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then
405 g_currentMission:addExtraPrintText(g_i18n:getText("info_fermenting")..string.format(" %s: %d%%", fillTypeName, math.ceil(self.fermentingPercent * 100)))
406 elseif self.state == BunkerSilo.STATE_DRAIN then
407 g_currentMission:addExtraPrintText(g_i18n:getText("info_fillLevel")..string.format(" %s: %d", fillTypeName, self.fillLevel))
408 end
409 end
410
411 if self.isServer then
412 if self.state == BunkerSilo.STATE_FILL then
413 for vehicle,state in pairs(self.vehiclesInRange) do
414 if state then
415 if vehicle:getIsActive() then
416 local distance = vehicle.lastMovedDistance
417 if distance > 0 then
418 local mass = vehicle:getTotalMass(false)
419
420 local compactingFactor = (mass / BunkerSilo.COMPACTING_BASE_MASS)
421
422 local compactingScale = 1
423 if vehicle.getBunkerSiloCompacterScale ~= nil then
424 compactingScale = vehicle:getBunkerSiloCompacterScale() or compactingScale
425 end
426 compactingFactor = compactingFactor * compactingScale
427
428 local deltaCompact = distance*compactingFactor*self.distanceToCompactedFillLevel
429
430 if vehicle.getWheels ~= nil then
431 local wheels = vehicle:getWheels()
432 local numWheels = #wheels
433 if numWheels > 0 then
434 local wheelsOnSilo = 0
435 local wheelsInAir = 0
436 for _, wheel in ipairs(wheels) do
437 if wheel.contact == Wheels.WHEEL_GROUND_HEIGHT_CONTACT then
438 wheelsOnSilo = wheelsOnSilo + 1
439 elseif wheel.contact == Wheels.WHEEL_NO_CONTACT then
440 wheelsInAir = wheelsInAir + 1
441 end
442 end
443 if wheelsOnSilo > 0 then
444 deltaCompact = deltaCompact * ((wheelsOnSilo + wheelsInAir) / numWheels)
445 else
446 deltaCompact = 0
447 end
448 end
449 end
450
451 if deltaCompact > 0 then
452 local compactedFillLevel = math.min(self.compactedFillLevel + deltaCompact, self.fillLevel)
453 if compactedFillLevel ~= self.compactedFillLevel then
454 self:updateCompacting(compactedFillLevel)
455 end
456 end
457 end
458 end
459 end
460 end
461 end
462 end
463
464 -- for chaff tutorial: always take the highest fill level of all bunker silos
465 if g_currentMission ~= nil and g_currentMission.bunkerScore ~= nil then
466 if g_currentMission.bunkerScore < self.fillLevel then
467 g_currentMission.bunkerScore = self.fillLevel
468 end
469 end
470
471 self:raiseActive()
472end

updateFillLevel

Description
Update fill level
Definition
updateFillLevel()
Code
504function BunkerSilo:updateFillLevel()
505 local area = self.bunkerSiloArea.inner
506 local fillLevel = self.fillLevel
507 local fillType = self.inputFillType
508
509 if fillType ~= FillType.UNKNOWN then
510 if self.state == BunkerSilo.STATE_FILL then
511 fillLevel = DensityMapHeightUtil.getFillLevelAtArea(fillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
512 elseif self.state == BunkerSilo.STATE_CLOSED then
513 fillLevel = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
514 elseif self.state == BunkerSilo.STATE_FERMENTED then
515 local fillLevel1 = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
516 local fillLevel2 = DensityMapHeightUtil.getFillLevelAtArea(self.outputFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
517 fillLevel = fillLevel1 + fillLevel2
518 elseif self.state == BunkerSilo.STATE_DRAIN then
519 local fillLevel1 = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
520 local fillLevel2 = DensityMapHeightUtil.getFillLevelAtArea(self.outputFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
521 fillLevel = fillLevel1 + fillLevel2
522 if fillLevel < self.emptyThreshold then
523 DensityMapHeightUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.fermentingFillType)
524 DensityMapHeightUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.outputFillType)
525 self:setState(BunkerSilo.STATE_FILL, true)
526 end
527 end
528 end
529
530 self.fillLevel = fillLevel
531end

updateTick

Description
UpdateTick
Definition
updateTick(float dt)
Arguments
floatdttime since last call in ms
Code
477function BunkerSilo:updateTick(dt)
478 if self.isServer then
479 self.updateTimer = self.updateTimer - dt
480 if self.updateTimer <= 0 then
481 self.updateTimer = 200 + math.random()*100 -- update every 200 to 300ms
482
483 local oldFillLevel = self.fillLevel
484 self:updateFillLevel()
485 if oldFillLevel ~= self.fillLevel then
486 self:updateCompacting(self.compactedFillLevel)
487 end
488 end
489 end
490 if not self.adjustedOpeningLength then
491 self.adjustedOpeningLength = true
492 self.openingLength = math.max(self.openingLength, DensityMapHeightUtil.getDefaultMaxRadius(self.outputFillType)+1)
493 end
494end

writeStream

Description
Called on server side on join
Definition
writeStream(integer streamId, table connection)
Arguments
integerstreamIdstream ID
tableconnectionconnection
Code
215function BunkerSilo:writeStream(streamId, connection)
216 BunkerSilo:superClass().writeStream(self, streamId, connection)
217 if not connection:getIsServer() then
218 streamWriteUIntN(streamId, self.state, 3)
219 streamWriteBool(streamId, self.isOpenedAtFront)
220 streamWriteBool(streamId, self.isOpenedAtBack)
221 streamWriteFloat32(streamId, self.fillLevel)
222 streamWriteUIntN(streamId, 2.55*self.compactedPercent, 8)
223 streamWriteUIntN(streamId, 2.55*self.fermentingPercent, 8)
224 end
225end

writeUpdateStream

Description
Called on server side on update
Definition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)
Arguments
integerstreamIdstream ID
tableconnectionconnection
integerdirtyMaskdirty mask
Code
259function BunkerSilo:writeUpdateStream(streamId, connection, dirtyMask)
260 BunkerSilo:superClass().writeUpdateStream(self, streamId, connection, dirtyMask)
261 if not connection:getIsServer() then
262 if streamWriteBool(streamId, bitAND(dirtyMask, self.bunkerSiloDirtyFlag) ~= 0) then
263 streamWriteUIntN(streamId, self.state, 3)
264
265 streamWriteFloat32(streamId, self.fillLevel)
266 streamWriteBool(streamId, self.isOpenedAtFront)
267 streamWriteBool(streamId, self.isOpenedAtBack)
268
269 if self.state == BunkerSilo.STATE_FILL then
270 streamWriteUIntN(streamId, 2.55*self.compactedPercent, 8)
271 elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then
272 streamWriteUIntN(streamId, 2.55*self.fermentingPercent, 8)
273 end
274 end
275 end
276end