645 | function BunkerSilo:getBunkerAreaOffset(updateAtFront, offset, fillType) |
646 | local area = self.bunkerSiloArea |
647 | |
648 | local hx, hz = area.dhx_norm, area.dhz_norm |
649 | local hl = MathUtil.vector3Length(area.dhx, area.dhy, area.dhz) |
650 | |
651 | while offset <= (hl - 1) do |
652 | local pos = offset |
653 | if not updateAtFront then |
654 | pos = hl - offset - 1 |
655 | end |
656 | local d1x,d1z = pos*hx, pos*hz |
657 | local d2x,d2z = (pos+1)*hx, (pos+1)*hz |
658 | |
659 | local a0x, a0z = area.sx + d1x, area.sz + d1z |
660 | local a1x, a1z = area.wx + d1x, area.wz + d1z |
661 | local a2x, a2z = area.sx + d2x, area.sz + d2z |
662 | |
663 | local fillLevel = DensityMapHeightUtil.getFillLevelAtArea(fillType, a0x,a0z, a1x,a1z, a2x,a2z) |
664 | if fillLevel > 0 then |
665 | return offset |
666 | end |
667 | offset = offset + 1 |
668 | end |
669 | |
670 | return math.max(hl - 1, 0) |
671 | end |
811 | function BunkerSilo:interactionTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId) |
812 | if onEnter or onLeave then |
813 | if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then |
814 | if onEnter then |
815 | self.playerInRange = true |
816 | g_currentMission:removeActivatableObject(self.activatable) -- make sure it is not added twice |
817 | g_currentMission:addActivatableObject(self.activatable) |
818 | else |
819 | self.playerInRange = false |
820 | if self.numVehiclesInRange == 0 then |
821 | g_currentMission:removeActivatableObject(self.activatable) |
822 | end |
823 | end |
824 | else |
825 | --local vehicle = g_currentMission.nodeToObject[otherId] |
826 | local vehicle = g_currentMission.nodeToObject[otherShapeId] |
827 | if vehicle ~= nil then |
828 | if onEnter then |
829 | if self.vehiclesInRange[vehicle] == nil then |
830 | self.vehiclesInRange[vehicle] = true |
831 | self.numVehiclesInRange = self.numVehiclesInRange + 1 |
832 | |
833 | g_currentMission:removeActivatableObject(self.activatable) -- make sure it is not added twice |
834 | g_currentMission:addActivatableObject(self.activatable) |
835 | |
836 | -- add callback if shovel |
837 | if vehicle.setBunkerSiloInteractorCallback ~= nil then |
838 | vehicle:setBunkerSiloInteractorCallback(BunkerSilo.onChangedFillLevelCallback, self) |
839 | end |
840 | end |
841 | else |
842 | if self.vehiclesInRange[vehicle] then |
843 | self.vehiclesInRange[vehicle] = nil |
844 | self.numVehiclesInRange = self.numVehiclesInRange - 1 |
845 | |
846 | if self.numVehiclesInRange == 0 and not self.playerInRange then |
847 | g_currentMission:removeActivatableObject(self.activatable) |
848 | end |
849 | |
850 | -- remove callback if shovel |
851 | if vehicle.setBunkerSiloInteractorCallback ~= nil then |
852 | vehicle:setBunkerSiloInteractorCallback(nil) |
853 | end |
854 | end |
855 | end |
856 | end |
857 | end |
858 | end |
859 | end |
84 | function BunkerSilo:load(id, xmlFile, key) |
85 | |
86 | self.nodeId = id |
87 | |
88 | self.bunkerSiloArea.start = I3DUtil.indexToObject(id, getXMLString(xmlFile, key..".area#startNode")) |
89 | self.bunkerSiloArea.width = I3DUtil.indexToObject(id, getXMLString(xmlFile, key..".area#widthNode")) |
90 | self.bunkerSiloArea.height = I3DUtil.indexToObject(id, getXMLString(xmlFile, key..".area#heightNode")) |
91 | |
92 | self.bunkerSiloArea.sx, self.bunkerSiloArea.sy, self.bunkerSiloArea.sz = getWorldTranslation(self.bunkerSiloArea.start) |
93 | self.bunkerSiloArea.wx, self.bunkerSiloArea.wy, self.bunkerSiloArea.wz = getWorldTranslation(self.bunkerSiloArea.width) |
94 | self.bunkerSiloArea.hx, self.bunkerSiloArea.hy, self.bunkerSiloArea.hz = getWorldTranslation(self.bunkerSiloArea.height) |
95 | |
96 | self.bunkerSiloArea.dhx = self.bunkerSiloArea.hx - self.bunkerSiloArea.sx |
97 | self.bunkerSiloArea.dhy = self.bunkerSiloArea.hy - self.bunkerSiloArea.sy |
98 | self.bunkerSiloArea.dhz = self.bunkerSiloArea.hz - self.bunkerSiloArea.sz |
99 | self.bunkerSiloArea.dhx_norm, self.bunkerSiloArea.dhy_norm, self.bunkerSiloArea.dhz_norm = MathUtil.vector3Normalize(self.bunkerSiloArea.dhx, self.bunkerSiloArea.dhy, self.bunkerSiloArea.dhz) |
100 | |
101 | self.bunkerSiloArea.dwx = self.bunkerSiloArea.wx - self.bunkerSiloArea.sx |
102 | self.bunkerSiloArea.dwy = self.bunkerSiloArea.wy - self.bunkerSiloArea.sy |
103 | self.bunkerSiloArea.dwz = self.bunkerSiloArea.wz - self.bunkerSiloArea.sz |
104 | self.bunkerSiloArea.dwx_norm, self.bunkerSiloArea.dwy_norm, self.bunkerSiloArea.dwz_norm = MathUtil.vector3Normalize(self.bunkerSiloArea.dwx, self.bunkerSiloArea.dwy, self.bunkerSiloArea.dwz) |
105 | |
106 | self.interactionTriggerNode = I3DUtil.indexToObject(id, getXMLString(xmlFile, key..".interactionTrigger#node")) |
107 | if self.interactionTriggerNode ~= nil then |
108 | addTrigger(self.interactionTriggerNode, "interactionTriggerCallback", self) |
109 | end |
110 | |
111 | self.acceptedFillTypes = {} |
112 | local data = StringUtil.splitString(" ", getXMLString(xmlFile, key.."#acceptedFillTypes") or "chaff grass_windrow dryGrass_windrow") |
113 | for i=1, table.getn(data) do |
114 | local fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(data[i]) |
115 | if fillTypeIndex ~= nil then |
116 | self.acceptedFillTypes[fillTypeIndex] = true |
117 | else |
118 | g_logManager:warning("'%s' is an invalid fillType for bunkerSilo '%s'!", tostring(data[i]), key.."#acceptedFillTypes") |
119 | end |
120 | end |
121 | |
122 | local inputFillTypeName = getXMLString(xmlFile, key.."#inputFillType") or "chaff" |
123 | local inputFillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(inputFillTypeName) |
124 | if inputFillTypeIndex ~= nil then |
125 | self.inputFillType = inputFillTypeIndex |
126 | else |
127 | g_logManager:warning("'%s' is an invalid input fillType for bunkerSilo '%s'!", tostring(inputFillTypeName), key.."#inputFillType") |
128 | end |
129 | |
130 | local outputFillTypeName = getXMLString(xmlFile, key.."#outputFillType") or "silage" |
131 | local outputFillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(outputFillTypeName) |
132 | if outputFillTypeIndex ~= nil then |
133 | self.outputFillType = outputFillTypeIndex |
134 | else |
135 | g_logManager:warning("'%s' is an invalid output fillType for bunkerSilo '%s'!", tostring(outputFillTypeName), key.."#outputFillType") |
136 | end |
137 | |
138 | g_densityMapHeightManager:setConvertingFillTypeAreas(self.bunkerSiloArea, self.acceptedFillTypes, self.inputFillType) |
139 | |
140 | self.distanceToCompactedFillLevel = getXMLFloat(xmlFile, key.."#distanceToCompactedFillLevel") or self.distanceToCompactedFillLevel |
141 | self.fermentingDuration = (getXMLFloat(xmlFile, key.."#fermentingHours") or 6) * 1000 * 60 * 60 |
142 | self.openingLength = getXMLFloat(xmlFile, key.."#openingLength") or 5 |
143 | |
144 | self.fillLevel = 0 |
145 | |
146 | -- adjust timings to difficulty |
147 | local difficultyMultiplier = g_currentMission.missionInfo.economicDifficulty |
148 | self.fermentingDuration = self.fermentingDuration*difficultyMultiplier |
149 | self.distanceToCompactedFillLevel = self.distanceToCompactedFillLevel/difficultyMultiplier |
150 | |
151 | -- just for tutorial 12 (feeder) |
152 | self.isTutorialSilo = Utils.getNoNil(getXMLBool(xmlFile, key.."#isTutorialSilo"), false ) |
153 | |
154 | self:setState(BunkerSilo.STATE_FILL) |
155 | |
156 | return true |
157 | end |
264 | function BunkerSilo:loadFromXMLFile(xmlFile, key) |
265 | |
266 | local state = getXMLInt(xmlFile, key.."#state") |
267 | if state ~= nil then |
268 | if state >= 0 and state < BunkerSilo.NUM_STATES then |
269 | self:setState(state) |
270 | end |
271 | end |
272 | |
273 | local fillLevel = getXMLFloat(xmlFile, key.."#fillLevel") |
274 | if fillLevel ~= nil then |
275 | self.fillLevel = fillLevel |
276 | end |
277 | local compactedFillLevel = getXMLFloat(xmlFile, key.."#compactedFillLevel") |
278 | if compactedFillLevel ~= nil then |
279 | self.compactedFillLevel = MathUtil.clamp(compactedFillLevel, 0, self.fillLevel) |
280 | end |
281 | self.compactedPercent = MathUtil.getFlooredPercent(math.min(self.compactedFillLevel, self.fillLevel), self.fillLevel) |
282 | |
283 | local fermentingTime = getXMLFloat(xmlFile, key.."#fermentingTime") |
284 | if fermentingTime ~= nil then |
285 | self.fermentingTime = MathUtil.clamp(fermentingTime, 0, self.fermentingDuration) |
286 | self.fermentingPercent = MathUtil.getFlooredPercent(self.fermentingTime, self.fermentingDuration) |
287 | end |
288 | |
289 | self.isOpenedAtFront = Utils.getNoNil(getXMLBool(xmlFile, key.."#openedAtFront"), false) |
290 | self.isOpenedAtBack = Utils.getNoNil(getXMLBool(xmlFile, key.."#openedAtBack"), false) |
291 | |
292 | if self.isOpenedAtFront then |
293 | self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.outputFillType) |
294 | else |
295 | self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.fermentingFillType) |
296 | end |
297 | if self.isOpenedAtBack then |
298 | self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.outputFillType) |
299 | else |
300 | self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.fermentingFillType) |
301 | end |
302 | |
303 | if self.fillLevel > 0 and self.state == BunkerSilo.STATE_DRAIN then |
304 | local area = self.bunkerSiloArea |
305 | local offWx = area.wx - area.sx |
306 | local offWz = area.wz - area.sz |
307 | local offW = math.sqrt(offWx*offWx + offWz*offWz) |
308 | |
309 | local offHx = area.hx - area.sx |
310 | local offHz = area.hz - area.sz |
311 | local offH = math.sqrt(offHx*offHx + offHz*offHz) |
312 | |
313 | if offW > 0.001 and offH > 0.001 then |
314 | -- offset by 0.9m in each direction (and max 45%) |
315 | local offWScale = math.min(0.45, 0.9 / offW) |
316 | offWx = offWx * offWScale |
317 | offWz = offWz * offWScale |
318 | |
319 | local offHScale = math.min(0.45, 0.9 / offH) |
320 | offHx = offHx * offHScale |
321 | offHz = offHz * offHScale |
322 | |
323 | 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) |
324 | 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) |
325 | local innerFillLevel = innerFillLevel1 + innerFillLevel2 |
326 | if innerFillLevel < self.emptyThreshold*0.5 then |
327 | DensityMapHeightUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.fermentingFillType) |
328 | DensityMapHeightUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.outputFillType) |
329 | self:setState(BunkerSilo.STATE_FILL, false) |
330 | end |
331 | end |
332 | elseif self.state == BunkerSilo.STATE_FILL then |
333 | local area = self.bunkerSiloArea |
334 | local fermentingFillLevel, fermentingPixels, totalFermentingPixels = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz) |
335 | -- Set to fermented state if more than 50% of the area is filled with fermenting fill type |
336 | if fermentingFillLevel > self.emptyThreshold and fermentingPixels > 0.5 * totalFermentingPixels then |
337 | local inputFillLevel, inputPixels, totalInputPixels = DensityMapHeightUtil.getFillLevelAtArea(self.inputFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz) |
338 | -- Only change if less than 10% is filled with input type (chaff) (ie. the silo is not being filled) |
339 | if inputPixels < 0.1*totalInputPixels then |
340 | self:setState(BunkerSilo.STATE_FERMENTED, false) |
341 | end |
342 | end |
343 | end |
344 | |
345 | return true |
346 | end |
32 | function BunkerSilo:new(isServer, isClient, customMt) |
33 | |
34 | local self = Object:new(isServer, isClient, customMt or BunkerSilo_mt) |
35 | |
36 | self.nodeId = 0 |
37 | self.interactionTriggerNode = nil |
38 | |
39 | self.bunkerSiloArea = {} |
40 | self.bunkerSiloArea.offsetFront = 0 |
41 | self.bunkerSiloArea.offsetBack = 0 |
42 | |
43 | self.acceptedFillTypes = {} |
44 | |
45 | self.inputFillType = FillType.CHAFF |
46 | self.outputFillType = FillType.SILAGE |
47 | self.fermentingFillType = FillType.TARP |
48 | |
49 | self.isOpenedAtFront = false |
50 | self.isOpenedAtBack = false |
51 | self.distanceToCompactedFillLevel = 100 |
52 | |
53 | self.fermentingTime = 0 |
54 | self.fermentingDuration = 6*60*60*1000 -- 6hours (ingame) |
55 | self.fermentingPercent = 0 |
56 | |
57 | self.fillLevel = 0 |
58 | self.compactedFillLevel = 0 |
59 | self.compactedPercent = 0 |
60 | self.emptyThreshold = 100 |
61 | |
62 | self.playerInRange = false |
63 | self.vehiclesInRange = {} |
64 | self.numVehiclesInRange = 0 |
65 | |
66 | self.siloIsFullWarningTimer = 0 |
67 | self.siloIsFullWarningDuration = 2000 |
68 | |
69 | self.updateTimer = 0 |
70 | |
71 | self.activatable = BunkerSiloActivatable:new(self) |
72 | |
73 | self.state = BunkerSilo.STATE_FILL |
74 | |
75 | self.bunkerSiloDirtyFlag = self:getNextDirtyFlag() |
76 | |
77 | return self |
78 | end |
866 | function BunkerSilo.onChangedFillLevelCallback(self, vehicle, fillDelta, fillType) |
867 | if fillDelta >= 0 then |
868 | return |
869 | end |
870 | |
871 | local area = self.bunkerSiloArea |
872 | |
873 | local x,y,z = getWorldTranslation(vehicle.components[1].node) |
874 | local closerToFront = self:getIsCloserToFront(x,y,z) |
875 | |
876 | local shovelSpec = vehicle.spec_shovel |
877 | if shovelSpec ~= nil then |
878 | if #shovelSpec.shovelNodes > 0 then |
879 | self.shovelNodeIndex = (self.shovelNodeIndex or 0) + 1 |
880 | if self.shovelNodeIndex > #shovelSpec.shovelNodes then |
881 | self.shovelNodeIndex = 1 |
882 | end |
883 | |
884 | local shovelNode = shovelSpec.shovelNodes[self.shovelNodeIndex] |
885 | if shovelNode ~= nil then |
886 | x, y, z = localToWorld(shovelNode.node, 0, 0, shovelNode.zOffset+shovelNode.length) |
887 | end |
888 | end |
889 | end |
890 | |
891 | local length = self.openingLength |
892 | |
893 | if closerToFront then |
894 | if self.isOpenedAtFront then |
895 | local p1 = MathUtil.getProjectOnLineParameter(x,z, area.sx,area.sz, area.dhx_norm,area.dhz_norm) |
896 | if p1 > area.offsetFront - length then |
897 | local offset = self:getBunkerAreaOffset(true, area.offsetFront, self.fermentingFillType) |
898 | local targetOffset = math.max(p1, offset) + length |
899 | |
900 | self:switchFillTypeAtOffset(true, area.offsetFront, targetOffset - area.offsetFront) |
901 | area.offsetFront = targetOffset |
902 | end |
903 | end |
904 | else |
905 | if self.isOpenedAtBack then |
906 | local p1 = MathUtil.getProjectOnLineParameter(x,z, area.hx,area.hz, -area.dhx_norm,-area.dhz_norm) |
907 | if p1 > area.offsetBack - length then |
908 | local offset = self:getBunkerAreaOffset(true, area.offsetBack, self.fermentingFillType) |
909 | local targetOffset = math.max(p1, offset) + length |
910 | |
911 | self:switchFillTypeAtOffset(false, area.offsetBack, targetOffset - area.offsetBack) |
912 | area.offsetBack = targetOffset |
913 | end |
914 | end |
915 | end |
916 | end |
620 | function BunkerSilo:openSilo(px,py,pz) |
621 | self:setState(BunkerSilo.STATE_DRAIN, true) |
622 | |
623 | self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.fermentingFillType) |
624 | self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.fermentingFillType) |
625 | |
626 | -- check which side is closer to player |
627 | local openAtFront = self:getIsCloserToFront(px,py,pz) |
628 | if openAtFront and not self.isOpenedAtFront then |
629 | self:switchFillTypeAtOffset(true, self.bunkerSiloArea.offsetFront, self.openingLength) |
630 | self.isOpenedAtFront = true |
631 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
632 | elseif not self.isOpenedAtBack then |
633 | self:switchFillTypeAtOffset(false, self.bunkerSiloArea.offsetBack, self.openingLength) |
634 | self.isOpenedAtBack = true |
635 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
636 | end |
637 | end |
212 | function BunkerSilo:readUpdateStream(streamId, timestamp, connection) |
213 | BunkerSilo:superClass().readUpdateStream(self, streamId, timestamp, connection) |
214 | if connection:getIsServer() then |
215 | if streamReadBool(streamId) then |
216 | local state = streamReadUIntN(streamId, 3) |
217 | if state ~= self.state then |
218 | self:setState(state, true) |
219 | end |
220 | |
221 | self.fillLevel = streamReadFloat32(streamId) |
222 | self.isOpenedAtFront = streamReadBool(streamId) |
223 | self.isOpenedAtBack = streamReadBool(streamId) |
224 | |
225 | if self.state == BunkerSilo.STATE_FILL then |
226 | self.compactedPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5) |
227 | elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then |
228 | self.fermentingPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5) |
229 | end |
230 | end |
231 | end |
232 | end |
530 | function BunkerSilo:setState(state, showNotification) |
531 | |
532 | if state ~= self.state then |
533 | |
534 | if state == BunkerSilo.STATE_FILL then |
535 | |
536 | self.fermentingTime = 0 |
537 | self.fermentingPercent = 0 |
538 | self.compactedFillLevel = 0 |
539 | self.isOpenedAtFront = false |
540 | self.isOpenedAtBack = false |
541 | self.bunkerSiloArea.offsetFront = 0 |
542 | self.bunkerSiloArea.offsetBack = 0 |
543 | |
544 | if showNotification then |
545 | self:showBunkerMessage(g_i18n:getText("ingameNotification_bunkerSiloIsEmpty")) |
546 | end |
547 | |
548 | if self.isServer then |
549 | g_densityMapHeightManager:removeFixedFillTypesArea(self.bunkerSiloArea) |
550 | g_densityMapHeightManager:setConvertingFillTypeAreas(self.bunkerSiloArea, self.acceptedFillTypes, self.inputFillType) |
551 | end |
552 | |
553 | elseif state == BunkerSilo.STATE_CLOSED then |
554 | |
555 | if self.isServer then |
556 | -- change fillType |
557 | local area = self.bunkerSiloArea |
558 | local offsetFront = self:getBunkerAreaOffset(true, 0, self.inputFillType) |
559 | local offsetBack = self:getBunkerAreaOffset(false, 0, self.inputFillType) |
560 | |
561 | local x0 = area.sx + (offsetFront * area.dhx_norm) |
562 | local z0 = area.sz + (offsetFront * area.dhz_norm) |
563 | local x1 = x0 + area.dwx |
564 | local z1 = z0 + area.dwz |
565 | local x2 = area.sx + area.dhx - (offsetBack * area.dhx_norm) |
566 | local z2 = area.sz + area.dhz - (offsetBack * area.dhz_norm) |
567 | |
568 | local changed = DensityMapHeightUtil.changeFillTypeAtArea(x0,z0, x1,z1, x2,z2, self.inputFillType, self.fermentingFillType) |
569 | |
570 | g_densityMapHeightManager:removeFixedFillTypesArea(self.bunkerSiloArea) |
571 | g_densityMapHeightManager:removeConvertingFillTypeAreas(self.bunkerSiloArea) |
572 | end |
573 | |
574 | if showNotification then |
575 | self:showBunkerMessage(g_i18n:getText("ingameNotification_bunkerSiloCovered")) |
576 | end |
577 | |
578 | elseif state == BunkerSilo.STATE_FERMENTED then |
579 | |
580 | if showNotification then |
581 | self:showBunkerMessage(g_i18n:getText("ingameNotification_bunkerSiloDoneFermenting")) |
582 | end |
583 | |
584 | elseif state == BunkerSilo.STATE_DRAIN then |
585 | |
586 | self.bunkerSiloArea.offsetFront = 0 |
587 | self.bunkerSiloArea.offsetBack = 0 |
588 | |
589 | if showNotification then |
590 | self:showBunkerMessage(g_i18n:getText("ingameNotification_bunkerSiloOpened")) |
591 | end |
592 | |
593 | if self.isServer then |
594 | g_densityMapHeightManager:removeConvertingFillTypeAreas(self.bunkerSiloArea) |
595 | local fillTypes = {} |
596 | fillTypes[self.outputFillType] = true |
597 | g_densityMapHeightManager:setFixedFillTypesArea(self.bunkerSiloArea, fillTypes) |
598 | end |
599 | |
600 | end |
601 | |
602 | self.state = state |
603 | if self.isServer then |
604 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
605 | end |
606 | end |
607 | end |
678 | function BunkerSilo:switchFillTypeAtOffset(switchAtFront, offset, length) |
679 | |
680 | local fillType = self.fermentingFillType |
681 | local newFillType = self.outputFillType |
682 | |
683 | local a0x, a0z = nil, nil |
684 | local a1x, a1z = nil, nil |
685 | local a2x, a2z = nil, nil |
686 | |
687 | local area = self.bunkerSiloArea |
688 | |
689 | if switchAtFront then |
690 | a0x, a0z = area.sx + (offset * area.dhx_norm), area.sz + (offset * area.dhz_norm) |
691 | a1x, a1z = a0x + area.dwx, a0z + area.dwz |
692 | a2x, a2z = area.sx + ((offset + length) * area.dhx_norm), area.sz + ((offset + length) * area.dhz_norm) |
693 | else |
694 | a0x, a0z = area.hx - (offset * area.dhx_norm), area.hz - (offset * area.dhz_norm) |
695 | a1x, a1z = a0x + area.dwx, a0z + area.dwz |
696 | a2x, a2z = area.hx - ((offset + length) * area.dhx_norm), area.hz - ((offset + length) * area.dhz_norm) |
697 | end |
698 | |
699 | DensityMapHeightUtil.changeFillTypeAtArea(a0x,a0z, a1x,a1z, a2x,a2z, fillType, newFillType) |
700 | |
701 | end |
365 | function BunkerSilo:update(dt) |
366 | |
367 | if self:getCanInteract(true) then |
368 | local fillTypeIndex = self.inputFillType |
369 | if self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED or self.state == BunkerSilo.STATE_DRAIN then |
370 | fillTypeIndex = self.outputFillType |
371 | end |
372 | local fillTypeName = "" |
373 | local fillType = g_fillTypeManager:getFillTypeByIndex(fillTypeIndex) |
374 | if fillType ~= nil then |
375 | fillTypeName = fillType.title |
376 | end |
377 | if self.state == BunkerSilo.STATE_FILL then |
378 | g_currentMission:addExtraPrintText(g_i18n:getText("info_fillLevel")..string.format(" %s: %d", fillTypeName, self.fillLevel)) |
379 | g_currentMission:addExtraPrintText(g_i18n:getText("info_compacting")..string.format(" %d%%", self.compactedPercent)) |
380 | elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then |
381 | g_currentMission:addExtraPrintText(g_i18n:getText("info_fermenting")..string.format(" %s: %d%%", fillTypeName, self.fermentingPercent)) |
382 | elseif self.state == BunkerSilo.STATE_DRAIN then |
383 | g_currentMission:addExtraPrintText(g_i18n:getText("info_fillLevel")..string.format(" %s: %d", fillTypeName, self.fillLevel)) |
384 | end |
385 | end |
386 | |
387 | if self.state == BunkerSilo.STATE_CLOSED then |
388 | if self.isServer then |
389 | self.fermentingTime = math.min(self.fermentingDuration, self.fermentingTime + dt*g_currentMission.missionInfo.timeScale) |
390 | local fermentingPercent = MathUtil.getFlooredPercent(self.fermentingTime, self.fermentingDuration) |
391 | if fermentingPercent ~= self.fermentingPercent then |
392 | self.fermentingPercent = fermentingPercent |
393 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
394 | end |
395 | if self.fermentingTime >= self.fermentingDuration then |
396 | self:setState(BunkerSilo.STATE_FERMENTED, true) |
397 | end |
398 | end |
399 | end |
400 | |
401 | if self.isServer then |
402 | if self.state == BunkerSilo.STATE_FILL then |
403 | for vehicle,state in pairs(self.vehiclesInRange) do |
404 | if state then |
405 | if vehicle:getIsActive() then |
406 | local distance = vehicle.lastMovedDistance |
407 | if distance > 0 then |
408 | local mass = vehicle:getTotalMass(false) |
409 | |
410 | local refNode = vehicle.components[1].node |
411 | local scale = (mass / BunkerSilo.COMPACTING_BASE_MASS) |
412 | |
413 | local bunkerSiloCompacter |
414 | if vehicle.getBunkerSiloCompacter ~= nil then |
415 | bunkerSiloCompacter = vehicle:getBunkerSiloCompacter() |
416 | end |
417 | if bunkerSiloCompacter ~= nil then |
418 | if bunkerSiloCompacter.refNode ~= nil then |
419 | refNode = bunkerSiloCompacter.refNode |
420 | end |
421 | if bunkerSiloCompacter.scale ~= nil then |
422 | scale = (mass / BunkerSilo.COMPACTING_BASE_MASS) * bunkerSiloCompacter.scale |
423 | end |
424 | end |
425 | |
426 | local deltaCompact = distance*scale*self.distanceToCompactedFillLevel |
427 | |
428 | if vehicle.getWheels ~= nil then |
429 | local wheels = vehicle:getWheels() |
430 | local numWheels = #wheels |
431 | if numWheels > 0 then |
432 | local wheelsOnSilo = 0 |
433 | local wheelsInAir = 0 |
434 | for _, wheel in ipairs(wheels) do |
435 | if wheel.contact == Wheels.WHEEL_GROUND_HEIGHT_CONTACT then |
436 | wheelsOnSilo = wheelsOnSilo + 1 |
437 | elseif wheel.contact == Wheels.WHEEL_NO_CONTACT then |
438 | wheelsInAir = wheelsInAir + 1 |
439 | end |
440 | end |
441 | if wheelsOnSilo > 0 then |
442 | deltaCompact = deltaCompact * ((wheelsOnSilo + wheelsInAir) / numWheels) |
443 | else |
444 | deltaCompact = 0 |
445 | end |
446 | end |
447 | end |
448 | |
449 | if deltaCompact > 0 then |
450 | local compactedFillLevel = math.min(self.compactedFillLevel + deltaCompact, self.fillLevel) |
451 | if compactedFillLevel ~= self.compactedFillLevel then |
452 | self.compactedFillLevel = compactedFillLevel |
453 | self.compactedPercent = MathUtil.getFlooredPercent(math.min(self.compactedFillLevel, self.fillLevel), self.fillLevel) |
454 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
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() |
472 | end |
498 | function BunkerSilo:updateFillLevel() |
499 | local area = self.bunkerSiloArea |
500 | local fillLevel = self.fillLevel |
501 | local fillType = self.inputFillType |
502 | |
503 | if fillType ~= FillType.UNKNOWN then |
504 | if self.state == BunkerSilo.STATE_FILL then |
505 | fillLevel = DensityMapHeightUtil.getFillLevelAtArea(fillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz) |
506 | elseif self.state == BunkerSilo.STATE_CLOSED then |
507 | fillLevel = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz) |
508 | elseif self.state == BunkerSilo.STATE_FERMENTED then |
509 | local fillLevel1 = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz) |
510 | local fillLevel2 = DensityMapHeightUtil.getFillLevelAtArea(self.outputFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz) |
511 | fillLevel = fillLevel1 + fillLevel2 |
512 | elseif self.state == BunkerSilo.STATE_DRAIN then |
513 | local fillLevel1 = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz) |
514 | local fillLevel2 = DensityMapHeightUtil.getFillLevelAtArea(self.outputFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz) |
515 | fillLevel = fillLevel1 + fillLevel2 |
516 | if fillLevel < self.emptyThreshold then |
517 | DensityMapHeightUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.fermentingFillType) |
518 | DensityMapHeightUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.outputFillType) |
519 | self:setState(BunkerSilo.STATE_FILL, true) |
520 | end |
521 | end |
522 | end |
523 | self.fillLevel = fillLevel |
524 | end |
239 | function BunkerSilo:writeUpdateStream(streamId, connection, dirtyMask) |
240 | BunkerSilo:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) |
241 | if not connection:getIsServer() then |
242 | if streamWriteBool(streamId, bitAND(dirtyMask, self.bunkerSiloDirtyFlag) ~= 0) then |
243 | streamWriteUIntN(streamId, self.state, 3) |
244 | |
245 | streamWriteFloat32(streamId, self.fillLevel) |
246 | streamWriteBool(streamId, self.isOpenedAtFront) |
247 | streamWriteBool(streamId, self.isOpenedAtBack) |
248 | |
249 | if self.state == BunkerSilo.STATE_FILL then |
250 | streamWriteUIntN(streamId, 2.55*self.compactedPercent, 8) |
251 | elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then |
252 | streamWriteUIntN(streamId, 2.55*self.fermentingPercent, 8) |
253 | end |
254 | end |
255 | end |
256 | end |