Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
2 | |
3 | BunkerSilo = {}; |
4 | BunkerSilo_mt = Class(BunkerSilo, Object); |
5 | |
6 | InitStaticObjectClass(BunkerSilo, "BunkerSilo", ObjectIds.OBJECT_BUNKER_SILO); |
7 | |
8 | BunkerSilo.STATE_FILL = 0; |
9 | BunkerSilo.STATE_CLOSED = 1; |
10 | BunkerSilo.STATE_DRAIN = 2; |
11 | BunkerSilo.NUM_STATES = 3; |
12 | |
13 | BunkerSilo.COMPACTING_BASE_MASS = 5; |
14 | |
15 | function BunkerSilo:onCreate(id) |
16 | local object = BunkerSilo:new(g_server ~= nil, g_client ~= nil); |
17 | if object:load(id) then |
18 | g_currentMission:addOnCreateLoadedObject(object); |
19 | object:register(true); |
20 | else |
21 | object:delete(); |
22 | end; |
23 | end; |
24 | |
25 | function BunkerSilo:new(isServer, isClient, customMt) |
26 | local mt = customMt; |
27 | if mt == nil then |
28 | mt = BunkerSilo_mt; |
29 | end; |
30 | |
31 | local self = Object:new(isServer, isClient, mt); |
32 | self.nodeId = 0; |
33 | self.tipTriggers = {}; |
34 | self.movingPlanes = {}; |
35 | self.sortedMovingPlanes = {}; |
36 | self.movingPlanesMinY = 0; |
37 | self.movingPlanesMaxY = 1; |
38 | self.movingPlanesYPower = 0.5; |
39 | self.silagePlaneMinY = 0; |
40 | self.silagePlaneMaxY = 1; |
41 | self.shovelTriggersMinY = 0; |
42 | self.silagePlaneMoveDuration = 3000; |
43 | self.silagePlaneMoveAlpha = 0; |
44 | self.silagePlaneMoveAlphaMax = 1; |
45 | self.silagePlaneMoveMinOffset = 0.1; |
46 | self.fillLevel = 0; |
47 | self.capacity = 20000; |
48 | self.fermentingTime = 0; |
49 | self.fermentingDuration = 6*60*60; -- 6hours (ingame) |
50 | |
51 | self.distanceToCompactedFillLevel = 300; |
52 | |
53 | self.playerInRange = false; |
54 | self.numVehiclesInRange = 0; |
55 | self.vehiclesInRange = {}; |
56 | self.compactingVehicles = {}; |
57 | |
58 | self.compactedFillLevel = 0; |
59 | |
60 | self.emptyThreshold = 10; |
61 | |
62 | self.showMovingPlaneFullWarningTimer = 0; |
63 | |
64 | self.warningTimerTime = 2000; |
65 | |
66 | self.activatable = BunkerSiloActivatable:new(self); |
67 | |
68 | self.state = BunkerSilo.STATE_FILL; |
69 | |
70 | self.bunkerSiloDirtyFlag = self:getNextDirtyFlag(); |
71 | return self; |
72 | end; |
73 | |
74 | function BunkerSilo:delete() |
75 | g_currentMission:removeOnCreateLoadedObjectToSave(self); |
76 | if self.isServer then |
77 | for _,trigger in pairs(self.tipTriggers) do |
78 | if trigger.isRegistered then |
79 | trigger:unregister(); |
80 | trigger:delete(); |
81 | end; |
82 | end; |
83 | end; |
84 | if self.interactionTriggerId ~= nil then |
85 | removeTrigger(self.interactionTriggerId); |
86 | end; |
87 | if self.compactingTriggerId ~= nil then |
88 | removeTrigger(self.compactingTriggerId); |
89 | end; |
90 | if self.isServer then |
91 | for _, movingPlane in pairs(self.movingPlanes) do |
92 | movingPlane.shovelTrigger:delete(); |
93 | end; |
94 | end; |
95 | if self.silageCatcherId ~= nil then |
96 | g_currentMission:removeNodeObject(self.silageCatcherId); |
97 | end; |
98 | if self.nodeId ~= 0 then |
99 | g_currentMission:removeNodeObject(self.nodeId); |
100 | end; |
101 | g_currentMission:removeActivatableObject(self.activatable); |
102 | BunkerSilo:superClass().delete(self); |
103 | end; |
104 | |
105 | function BunkerSilo:readStream(streamId, connection) |
106 | BunkerSilo:superClass().readStream(self, streamId, connection); |
107 | if connection:getIsServer() then |
108 | |
109 | local state = streamReadUIntN(streamId, 2); |
110 | self:setState(state); |
111 | |
112 | for i=1, table.getn(self.movingPlanes) do |
113 | local movingPlane = self.movingPlanes[i]; |
114 | |
115 | local fillLevel = streamReadFloat32(streamId); |
116 | self:setMovingPlaneFillLevel(movingPlane, fillLevel, false); |
117 | end; |
118 | |
119 | self.compactedFillLevel = streamReadFloat32(streamId); |
120 | self.fermentingTime = streamReadFloat32(streamId); |
121 | self.silagePlaneMoveAlphaMax = streamReadFloat32(streamId); |
122 | self.silagePlaneMoveAlpha = streamReadFloat32(streamId); |
123 | if self.state ~= BunkerSilo.STATE_CLOSED then |
124 | self.silagePlaneMoveAlpha = 0; |
125 | end; |
126 | if self.silagePlaneId ~= nil then |
127 | local x,_,z = getTranslation(self.silagePlaneId); |
128 | setTranslation(self.silagePlaneId, x, self.silagePlaneMinY + self.silagePlaneMoveAlpha*(self.silagePlaneMaxY- self.silagePlaneMinY), z); |
129 | end; |
130 | end; |
131 | end; |
132 | |
133 | function BunkerSilo:writeStream(streamId, connection) |
134 | BunkerSilo:superClass().writeStream(self, streamId, connection); |
135 | if not connection:getIsServer() then |
136 | streamWriteUIntN(streamId, self.state, 2); |
137 | for i=1, table.getn(self.movingPlanes) do |
138 | local movingPlane = self.movingPlanes[i]; |
139 | |
140 | streamWriteFloat32(streamId, movingPlane.fillLevel); |
141 | end; |
142 | |
143 | streamWriteFloat32(streamId, self.compactedFillLevel); |
144 | streamWriteFloat32(streamId, self.fermentingTime); |
145 | streamWriteFloat32(streamId, self.silagePlaneMoveAlphaMax); |
146 | streamWriteFloat32(streamId, self.silagePlaneMoveAlpha); |
147 | end; |
148 | end; |
149 | |
150 | function BunkerSilo:readUpdateStream(streamId, timestamp, connection) |
151 | BunkerSilo:superClass().readUpdateStream(self, streamId, timestamp, connection) |
152 | if connection:getIsServer() then |
153 | self.showMovingPlaneFullWarningTimer = streamReadUIntN(streamId, 4)/15*self.warningTimerTime; |
154 | |
155 | for i=1, table.getn(self.movingPlanes) do |
156 | local movingPlane = self.movingPlanes[i]; |
157 | |
158 | local fillLevel = streamReadFloat32(streamId); |
159 | self:setMovingPlaneFillLevel(movingPlane, fillLevel, false); |
160 | end; |
161 | self.compactedFillLevel = streamReadFloat32(streamId); |
162 | local state = streamReadUIntN(streamId, 2); |
163 | self:setState(state); |
164 | end; |
165 | end; |
166 | |
167 | function BunkerSilo:writeUpdateStream(streamId, connection, dirtyMask) |
168 | BunkerSilo:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) |
169 | if not connection:getIsServer() then |
170 | local percent = Utils.clamp(self.showMovingPlaneFullWarningTimer / self.warningTimerTime, 0, 1); |
171 | streamWriteUIntN(streamId, math.floor(percent*15), 4); |
172 | |
173 | for i=1, table.getn(self.movingPlanes) do |
174 | local movingPlane = self.movingPlanes[i]; |
175 | |
176 | streamWriteFloat32(streamId, movingPlane.fillLevel); |
177 | end; |
178 | streamWriteFloat32(streamId, self.compactedFillLevel); |
179 | streamWriteUIntN(streamId, self.state, 2); |
180 | end; |
181 | end; |
182 | |
183 | function BunkerSilo:load(nodeId) |
184 | |
185 | self.nodeId = nodeId; |
186 | |
187 | local tipTriggersIndex = getUserAttribute(nodeId, "tipTriggersIndex"); |
188 | if tipTriggersIndex ~= nil then |
189 | local tipTriggersId = Utils.indexToObject(nodeId, tipTriggersIndex); |
190 | if tipTriggersId ~= nil then |
191 | local numChildren = getNumOfChildren(tipTriggersId); |
192 | for i=1,numChildren do |
193 | local childId = getChildAt(tipTriggersId, i-1); |
194 | local tipTrigger = BunkerSiloTipTrigger:new(self.isServer, self.isClient); |
195 | tipTrigger:load(childId, self); |
196 | g_currentMission:addOnCreateLoadedObject(tipTrigger); |
197 | tipTrigger:register(true); |
198 | table.insert(self.tipTriggers, tipTrigger); |
199 | end; |
200 | end; |
201 | end; |
202 | |
203 | |
204 | local minY, maxY = Utils.getVectorFromString(getUserAttribute(nodeId, "movingPlanesMinMaxY")); |
205 | if minY ~= nil and maxY ~= nil then |
206 | self.movingPlanesMinY = minY; |
207 | self.movingPlanesMaxY = maxY; |
208 | end; |
209 | self.movingPlanesYPower = Utils.getNoNil(tonumber(getUserAttribute(nodeId, "movingPlanesYPower")), self.movingPlanesYPower); |
210 | |
211 | local movingPlanesIndex = getUserAttribute(nodeId, "movingPlanesIndex"); |
212 | if movingPlanesIndex ~= nil then |
213 | local movingPlanesId = Utils.indexToObject(nodeId, movingPlanesIndex); |
214 | if movingPlanesId ~= nil then |
215 | local numChildren = getNumOfChildren(movingPlanesId); |
216 | for i=1,numChildren do |
217 | local childId = getChildAt(movingPlanesId, i-1); |
218 | local x,y,z = getTranslation(childId); |
219 | local wx,wy,wz = getWorldTranslation(childId); |
220 | |
221 | for _, tipTrigger in ipairs(self.tipTriggers) do |
222 | setPairCollision(getChildAt(childId, 0), tipTrigger.rootNode, false); |
223 | setPairCollision(getChildAt(childId, 1), tipTrigger.rootNode, false); |
224 | setPairCollision(getChildAt(childId, 0), getChildAt(childId, 1), false); |
225 | end |
226 | |
227 | local movingPlane = {index=table.getn(self.movingPlanes)+1, nodeId=childId, fillLevel = 0, sentFillLevel = 0, x=x, y=y, z=z}; |
228 | |
229 | if self.isServer then |
230 | local shovelTrigger = BunkerSiloShovelTrigger:new(); |
231 | shovelTrigger:load(getChildAt(childId, 0), self, movingPlane); |
232 | movingPlane.shovelTrigger = shovelTrigger; |
233 | end; |
234 | |
235 | table.insert(self.movingPlanes, movingPlane); |
236 | end; |
237 | end; |
238 | end; |
239 | -- we need at least 3 moving planes |
240 | assert(table.getn(self.movingPlanes) >= 3); |
241 | |
242 | for i=1,table.getn(self.movingPlanes) do |
243 | local movingPlane1 = self.movingPlanes[i].nodeId; |
244 | for j=i+1,table.getn(self.movingPlanes) do |
245 | local movingPlane2 = self.movingPlanes[j].nodeId; |
246 | setPairCollision(getChildAt(movingPlane1, 0), getChildAt(movingPlane2, 0), false); |
247 | setPairCollision(getChildAt(movingPlane1, 1), getChildAt(movingPlane2, 0), false); |
248 | setPairCollision(getChildAt(movingPlane1, 0), getChildAt(movingPlane2, 1), false); |
249 | setPairCollision(getChildAt(movingPlane1, 1), getChildAt(movingPlane2, 1), false); |
250 | end |
251 | end |
252 | |
253 | local silagePlaneIndex = getUserAttribute(nodeId, "silagePlaneIndex"); |
254 | if silagePlaneIndex ~= nil then |
255 | self.silagePlaneId = Utils.indexToObject(nodeId, silagePlaneIndex); |
256 | end; |
257 | local minY, maxY = Utils.getVectorFromString(getUserAttribute(nodeId, "silagePlaneMinMaxY")); |
258 | if minY ~= nil and maxY ~= nil then |
259 | self.silagePlaneMinY = minY; |
260 | self.silagePlaneMaxY = maxY; |
261 | end; |
262 | local silagePlaneMoveMinOffset = getUserAttribute(nodeId, "silagePlaneMoveMinOffset"); |
263 | if silagePlaneMoveMinOffset ~= nil then |
264 | self.silagePlaneMoveMinOffset = Utils.getNoNil(tonumber(silagePlaneMoveMinOffset), self.silagePlaneMoveMinOffset); |
265 | end; |
266 | |
267 | local shovelTriggersMinY = getUserAttribute(nodeId, "shovelTriggersMinY"); |
268 | if shovelTriggersMinY ~= nil then |
269 | self.shovelTriggersMinY = Utils.getNoNil(tonumber(shovelTriggersMinY), self.shovelTriggersMinY); |
270 | end; |
271 | |
272 | local distanceToCompactedFillLevelStr = getUserAttribute(nodeId, "distanceToCompactedFillLevel"); |
273 | if distanceToCompactedFillLevelStr ~= nil then |
274 | self.distanceToCompactedFillLevel = Utils.getNoNil(tonumber(distanceToCompactedFillLevelStr), self.distanceToCompactedFillLevel); |
275 | end; |
276 | |
277 | local fermentingDurationStr = getUserAttribute(nodeId, "fermentingDuration"); |
278 | if fermentingDurationStr ~= nil then |
279 | self.fermentingDuration = Utils.getNoNil(tonumber(fermentingDurationStr), self.fermentingDuration); |
280 | end; |
281 | |
282 | local capacityStr = getUserAttribute(nodeId, "capacity"); |
283 | if capacityStr ~= nil then |
284 | self.capacity = Utils.getNoNil(tonumber(capacityStr), self.capacity); |
285 | end; |
286 | self.fillLevel = 0; |
287 | |
288 | local movingPlaneCapacity = self.capacity / table.getn(self.movingPlanes) / 0.8; -- each plane should be filled 80% in average to get the full filllevel |
289 | for i,movingPlane in pairs(self.movingPlanes) do |
290 | if i==1 or i == table.getn(self.movingPlanes) then |
291 | movingPlane.capacity = movingPlaneCapacity*(1+0.1/0.8); -- take the 0.1 weight of the boundary plane that is missing |
292 | else |
293 | movingPlane.capacity = movingPlaneCapacity; |
294 | end; |
295 | end; |
296 | |
297 | if self.isClient then |
298 | local interactionTriggerIndex = getUserAttribute(nodeId, "interactionTriggerIndex"); |
299 | if interactionTriggerIndex ~= nil then |
300 | self.interactionTriggerId = Utils.indexToObject(nodeId, interactionTriggerIndex); |
301 | if self.interactionTriggerId ~= nil then |
302 | addTrigger(self.interactionTriggerId, "interactionTriggerCallback", self); |
303 | end; |
304 | end; |
305 | end; |
306 | if self.isServer then |
307 | local compactingTriggerIndex = getUserAttribute(nodeId, "compactingTriggerIndex"); |
308 | if compactingTriggerIndex ~= nil then |
309 | self.compactingTriggerId = Utils.indexToObject(nodeId, compactingTriggerIndex); |
310 | if self.compactingTriggerId ~= nil then |
311 | addTrigger(self.compactingTriggerId, "compactingTriggerCallback", self); |
312 | end; |
313 | end; |
314 | |
315 | local silageCatcherIndex = getUserAttribute(nodeId, "silageCatcherIndex"); |
316 | if silageCatcherIndex ~= nil then |
317 | self.silageCatcherId = Utils.indexToObject(nodeId, silageCatcherIndex); |
318 | if self.silageCatcherId ~= nil then |
319 | g_currentMission:addNodeObject(self.silageCatcherId, self); |
320 | end; |
321 | end; |
322 | end; |
323 | |
324 | |
325 | for _,movingPlane in pairs(self.movingPlanes) do |
326 | self:setMovingPlaneFillLevel(movingPlane, 0, false); |
327 | end; |
328 | if self.silagePlaneId ~= nil then |
329 | setVisibility(self.silagePlaneId, false); |
330 | local x,_,z = getTranslation(self.silagePlaneId); |
331 | setTranslation(self.silagePlaneId, x, self.silagePlaneMinY + self.silagePlaneMoveAlpha*(self.silagePlaneMaxY- self.silagePlaneMinY), z); |
332 | end; |
333 | |
334 | -- adjust timings to difficulty |
335 | local difficultyMultiplier = g_currentMission.missionStats.difficulty; -- 1 2 3 |
336 | self.fermentingDuration = self.fermentingDuration*difficultyMultiplier; |
337 | self.distanceToCompactedFillLevel = self.distanceToCompactedFillLevel/difficultyMultiplier; |
338 | |
339 | -- just for tutorial 12 (feeder) |
340 | self.isTutorialSilo = Utils.getNoNil( getUserAttribute(nodeId, "isTutorialSilo"), false ); |
341 | |
342 | g_currentMission:addNodeObject(self.nodeId, self); |
343 | |
344 | g_currentMission:addOnCreateLoadedObjectToSave(self); |
345 | return true; |
346 | end; |
347 | |
348 | function BunkerSilo:loadFromAttributesAndNodes(xmlFile, key) |
349 | |
350 | local state = getXMLInt(xmlFile, key.."#state"); |
351 | if state ~= nil then |
352 | if state >= 0 and state < BunkerSilo.NUM_STATES then |
353 | self:setState(state); |
354 | end; |
355 | end; |
356 | |
357 | for i=1, table.getn(self.movingPlanes) do |
358 | local planeKey = key..string.format(".movingPlane%d", i); |
359 | if not hasXMLProperty(xmlFile, planeKey) then |
360 | break; |
361 | end; |
362 | local fillLevel = getXMLFloat(xmlFile, planeKey.."#fillLevel"); |
363 | if fillLevel ~= nil then |
364 | self:setMovingPlaneFillLevel(self.movingPlanes[i], fillLevel, false); |
365 | end; |
366 | end; |
367 | |
368 | local compactedFillLevel = getXMLFloat(xmlFile, key.."#compactedFillLevel"); |
369 | if compactedFillLevel ~= nil then |
370 | self.compactedFillLevel = Utils.clamp(compactedFillLevel, 0, self.fillLevel); |
371 | end; |
372 | |
373 | local fermentingTime = getXMLFloat(xmlFile, key.."#fermentingTime"); |
374 | if fermentingTime ~= nil then |
375 | self.fermentingTime = Utils.clamp(fermentingTime, 0, self.fermentingDuration); |
376 | end; |
377 | |
378 | self:calculateSilagePlaneMoveAlphaMax(); |
379 | local silagePlaneMoveAlphaMax = getXMLFloat(xmlFile, key.."#silagePlaneMoveAlphaMax"); |
380 | if silagePlaneMoveAlphaMax ~= nil then |
381 | self.silagePlaneMoveAlphaMax = math.max(silagePlaneMoveAlphaMax, self.silagePlaneMoveAlphaMax); |
382 | end; |
383 | |
384 | local silagePlaneMoveAlpha = getXMLFloat(xmlFile, key.."#silagePlaneMoveAlpha"); |
385 | if silagePlaneMoveAlpha ~= nil then |
386 | self.silagePlaneMoveAlpha = Utils.clamp(silagePlaneMoveAlpha, 0, self.silagePlaneMoveAlphaMax); |
387 | if self.state ~= BunkerSilo.STATE_CLOSED then |
388 | self.silagePlaneMoveAlpha = 0; |
389 | end; |
390 | if self.silagePlaneId ~= nil then |
391 | local x,_,z = getTranslation(self.silagePlaneId); |
392 | setTranslation(self.silagePlaneId, x, self.silagePlaneMinY + self.silagePlaneMoveAlpha*(self.silagePlaneMaxY- self.silagePlaneMinY), z); |
393 | end; |
394 | end; |
395 | return true; |
396 | end; |
397 | |
398 | function BunkerSilo:getSaveAttributesAndNodes(nodeIdent) |
399 | |
400 | |
401 | local attributes = 'state="'..self.state..'" compactedFillLevel="'..self.compactedFillLevel..'" fermentingTime="'..self.fermentingTime..'" silagePlaneMoveAlpha="'..self.silagePlaneMoveAlpha..'" silagePlaneMoveAlphaMax="'..self.silagePlaneMoveAlphaMax..'"'; |
402 | |
403 | local nodes = ""; |
404 | for i=1, table.getn(self.movingPlanes) do |
405 | if i>1 then |
406 | nodes = nodes.."\n"; |
407 | end; |
408 | local movingPlane = self.movingPlanes[i]; |
409 | nodes = nodes.. nodeIdent..'<movingPlane'..i..' fillLevel="'..movingPlane.fillLevel..'" />'; |
410 | end; |
411 | |
412 | return attributes, nodes; |
413 | end; |
414 | |
415 | function BunkerSilo:update(dt) |
416 | self.showMovingPlaneFullWarningTimer = self.showMovingPlaneFullWarningTimer - dt; |
417 | |
418 | if self:getCanInteract() then |
419 | if self.showMovingPlaneFullWarningTimer > 0 then |
420 | g_currentMission:showBlinkingWarning(g_i18n:getText("Here_is_no_space")); |
421 | end; |
422 | |
423 | if self.state == BunkerSilo.STATE_FILL then |
424 | local fillLevelPrint = Utils.getFlooredBounded(self.fillLevel, 0, math.floor(self.capacity)) |
425 | local fillLevelPercent = Utils.getFlooredPercent(self.fillLevel, self.capacity); |
426 | local compactedPercent = Utils.getFlooredPercent(math.min(self.compactedFillLevel, self.fillLevel), self.fillLevel); |
427 | g_currentMission:addExtraPrintText(g_i18n:getText("fill_level")..string.format(" %d (%d%%)", fillLevelPrint, fillLevelPercent)); |
428 | g_currentMission:addExtraPrintText(g_i18n:getText("compacting")..string.format(" %d%%", compactedPercent)); |
429 | elseif self.state == BunkerSilo.STATE_CLOSED then |
430 | local fermentingPercent = Utils.getFlooredPercent(self.fermentingTime, self.fermentingDuration); |
431 | g_currentMission:addExtraPrintText(g_i18n:getText("fermenting")..string.format(" %d%%", fermentingPercent)); |
432 | elseif self.state == BunkerSilo.STATE_DRAIN then |
433 | local fillLevelPrint = Utils.getFlooredBounded(self.fillLevel, 0, math.floor(self.capacity)) |
434 | local fillLevelPercent = Utils.getFlooredPercent(self.fillLevel, self.capacity); |
435 | g_currentMission:addExtraPrintText(g_i18n:getText("fill_level")..string.format(" %d (%d%%)", fillLevelPrint, fillLevelPercent)); |
436 | end; |
437 | end; |
438 | if self.state == BunkerSilo.STATE_CLOSED then |
439 | if self.silagePlaneId ~= nil then |
440 | if self.silagePlaneMoveAlpha < self.silagePlaneMoveAlphaMax then |
441 | self.silagePlaneMoveAlpha = math.min(self.silagePlaneMoveAlpha + dt/self.silagePlaneMoveDuration, self.silagePlaneMoveAlphaMax); |
442 | local x,_,z = getTranslation(self.silagePlaneId); |
443 | setTranslation(self.silagePlaneId, x, self.silagePlaneMinY + self.silagePlaneMoveAlpha*(self.silagePlaneMaxY- self.silagePlaneMinY), z); |
444 | end; |
445 | end; |
446 | self.fermentingTime = self.fermentingTime + dt*0.001*g_currentMission.missionStats.timeScale; |
447 | if self.isServer then |
448 | if self.fermentingTime > self.fermentingDuration then |
449 | self:setState(BunkerSilo.STATE_DRAIN); |
450 | end; |
451 | else |
452 | self.fermentingTime = math.min(self.fermentingTime, self.fermentingDuration); |
453 | end; |
454 | elseif self.state == BunkerSilo.STATE_DRAIN then |
455 | if self.isServer then |
456 | if self.fillLevel <= self.emptyThreshold then |
457 | self:setState(BunkerSilo.STATE_FILL); |
458 | end; |
459 | end; |
460 | end; |
461 | |
462 | if self.isServer then |
463 | if self.state == BunkerSilo.STATE_FILL then |
464 | for vehicle, mass in pairs(self.compactingVehicles) do |
465 | if vehicle:getIsActive() then |
466 | local distance = vehicle.lastMovedDistance; |
467 | if distance > 0 then |
468 | local refNode = vehicle.bunkerSiloCompactingRefNode; |
469 | if refNode == nil then |
470 | refNode = vehicle.components[1].node; |
471 | end; |
472 | local scale = (mass / BunkerSilo.COMPACTING_BASE_MASS) * Utils.getNoNil(vehicle.bunkerSiloCompactingScale, 1); |
473 | local deltaCompact = distance*scale*self.distanceToCompactedFillLevel; |
474 | self.compactedFillLevel = math.min(self.compactedFillLevel + deltaCompact, self.fillLevel); |
475 | |
476 | local x,y,z = getWorldTranslation(refNode); |
477 | -- find the moving plane that is nearest |
478 | local nearestDistance = math.huge; |
479 | local nearestPlaneI = 1; |
480 | for i,movingPlane in pairs(self.movingPlanes) do |
481 | local wx,_,wz = getWorldTranslation(movingPlane.nodeId); |
482 | local distance = (wx-x)*(wx-x) + (wz-z)*(wz-z); |
483 | if distance < nearestDistance then |
484 | nearestPlaneI = i; |
485 | nearestDistance = distance; |
486 | end; |
487 | end; |
488 | local nearestPlane = self.movingPlanes[nearestPlaneI]; |
489 | local plane1; |
490 | local plane2; |
491 | local delta1 = 0; |
492 | local delta2 = 0; |
493 | if nearestPlaneI > 1 then |
494 | plane1 = self.movingPlanes[nearestPlaneI-1]; |
495 | delta1 = Utils.clamp(nearestPlane.fillLevel - plane1.fillLevel, 0, plane1.capacity - plane1.fillLevel); |
496 | end; |
497 | if nearestPlaneI < table.getn(self.movingPlanes) then |
498 | plane2 = self.movingPlanes[nearestPlaneI+1]; |
499 | delta2 = Utils.clamp(nearestPlane.fillLevel - plane2.fillLevel, 0, plane2.capacity - plane2.fillLevel); |
500 | end; |
501 | local delta = delta1 + delta2; |
502 | if delta > 0 then |
503 | |
504 | local maxDelta = math.min(distance*self.distanceToCompactedFillLevel*4, nearestPlane.fillLevel); |
505 | local deltaScale = math.min(delta*0.5, maxDelta)/delta; -- *0.5 because we want to match both planes in the middle |
506 | delta1 = delta1*deltaScale; |
507 | delta2 = delta2*deltaScale; |
508 | |
509 | self:setMovingPlaneFillLevel(nearestPlane, nearestPlane.fillLevel - delta1 - delta2, false); |
510 | if plane1 ~= nil and delta1 > 0 then |
511 | self:setMovingPlaneFillLevel(plane1, plane1.fillLevel + delta1, false); |
512 | end; |
513 | if plane2 ~= nil and delta2 > 0 then |
514 | self:setMovingPlaneFillLevel(plane2, plane2.fillLevel + delta2, false); |
515 | end; |
516 | end; |
517 | end; |
518 | end; |
519 | end; |
520 | end; |
521 | end; |
522 | |
523 | -- for chaff tutorial: always take the highest fill level of all bunker silos |
524 | if g_currentMission ~= nil and g_currentMission.bunkerScore ~= nil then |
525 | if g_currentMission.bunkerScore < self.fillLevel then |
526 | g_currentMission.bunkerScore = self.fillLevel; |
527 | end; |
528 | end; |
529 | end; |
530 | |
531 | function BunkerSilo:updateTick(dt) |
532 | if self.isServer then |
533 | local dirty = false; |
534 | for _, movingPlane in pairs(self.movingPlanes) do |
535 | if movingPlane.sentFillLevel ~= movingPlane.fillLevel then |
536 | dirty = true; |
537 | movingPlane.sentFillLevel = movingPlane.fillLevel; |
538 | end; |
539 | movingPlane.shovelTrigger:update(dt); |
540 | end; |
541 | if self.sentCompactedFillLevel ~= self.compactedFillLevel then |
542 | self.sentCompactedFillLevel = self.compactedFillLevel; |
543 | dirty = true; |
544 | end; |
545 | if dirty then |
546 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag); |
547 | end; |
548 | end; |
549 | end; |
550 | |
551 | function BunkerSilo:setMovingPlaneFillLevel(movingPlane, fillLevel, addWarnings) |
552 | local oldFillLevel = movingPlane.fillLevel; |
553 | local newFillLevel = fillLevel; |
554 | |
555 | if newFillLevel > movingPlane.capacity then |
556 | newFillLevel = movingPlane.capacity; |
557 | |
558 | if addWarnings then |
559 | self.showMovingPlaneFullWarningTimer = self.warningTimerTime; |
560 | if self.isServer then |
561 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag); |
562 | end; |
563 | end; |
564 | end; |
565 | if newFillLevel < 0 then |
566 | newFillLevel = 0; |
567 | end; |
568 | |
569 | local deltaFillLevel = newFillLevel - movingPlane.fillLevel; |
570 | if deltaFillLevel > self.capacity - self.fillLevel then |
571 | -- limit the delta such that the total fill level is not over the capacity |
572 | deltaFillLevel = self.capacity - self.fillLevel; |
573 | end; |
574 | movingPlane.fillLevel = movingPlane.fillLevel + deltaFillLevel; |
575 | |
576 | local totalFillLevel = 0; |
577 | for _,p in pairs(self.movingPlanes) do |
578 | totalFillLevel = totalFillLevel + p.fillLevel; |
579 | end; |
580 | self.fillLevel = Utils.clamp(totalFillLevel, 0, self.capacity); |
581 | |
582 | local alpha = movingPlane.fillLevel/movingPlane.capacity; |
583 | |
584 | movingPlane.y = self.movingPlanesMinY + (alpha^self.movingPlanesYPower) * (self.movingPlanesMaxY - self.movingPlanesMinY); |
585 | setTranslation(movingPlane.nodeId, movingPlane.x, movingPlane.y, movingPlane.z); |
586 | |
587 | if self.isServer then |
588 | setTranslation(movingPlane.shovelTrigger.nodeId , 0, math.max(self.shovelTriggersMinY-movingPlane.y, 0), 0); |
589 | end; |
590 | end; |
591 | |
592 | function BunkerSilo:setTrailerFillDelta(trailer, trailerFillDelta, fillType) |
593 | if trailerFillDelta < 0 then |
594 | local x,y,z; |
595 | if trailer.tipReferencePoints ~= nil and trailer.tipReferencePoints[trailer.currentTipReferencePointIndex] ~= nil then |
596 | x,y,z = getWorldTranslation(trailer.tipReferencePoints[trailer.currentTipReferencePointIndex].node); |
597 | else |
598 | x,y,z = getWorldTranslation(trailer.components[1].node); |
599 | end |
600 | self:setFillDeltaAt(x,y,z, -trailerFillDelta, true); |
601 | end; |
602 | end; |
603 | |
604 | function BunkerSilo:setFillDeltaAt(x,y,z, delta, addWarnings) |
605 | if delta ~= 0 then |
606 | -- find the moving plane that is nearest |
607 | local nearestDistance = math.huge; |
608 | local nearestPlaneI = 1; |
609 | for i,movingPlane in pairs(self.movingPlanes) do |
610 | local wx,_,wz = getWorldTranslation(movingPlane.nodeId); |
611 | local distance = (wx-x)*(wx-x) + (wz-z)*(wz-z); |
612 | if distance < nearestDistance then |
613 | nearestPlaneI = i; |
614 | nearestDistance = distance; |
615 | end; |
616 | end; |
617 | |
618 | local plane1 = self.movingPlanes[nearestPlaneI]; |
619 | local weight1 = 0.8; |
620 | local weight2 = 0.1; |
621 | local weight3 = 0.1; |
622 | local plane2; |
623 | local plane3; |
624 | if nearestPlaneI > 1 then |
625 | plane2 = self.movingPlanes[nearestPlaneI-1]; |
626 | |
627 | if weight2*delta > plane2.capacity - plane2.fillLevel then |
628 | -- give everything we cant handle to plane1 |
629 | local newWeight = (plane2.capacity - plane2.fillLevel) / delta; |
630 | weight1 = weight1 + weight2 - newWeight; |
631 | weight2 = newWeight; |
632 | end; |
633 | if weight2 > 0 then |
634 | self:setMovingPlaneFillLevel(plane2, plane2.fillLevel + weight2*delta, addWarnings); |
635 | end; |
636 | else |
637 | weight1 = weight1 + weight2; |
638 | weight2 = 0; |
639 | end; |
640 | if nearestPlaneI < table.getn(self.movingPlanes) then |
641 | plane3 = self.movingPlanes[nearestPlaneI+1]; |
642 | |
643 | if weight3*delta > plane3.capacity - plane3.fillLevel then |
644 | -- give everything we cant handle to plane1 |
645 | local newWeight = (plane3.capacity - plane3.fillLevel) / delta; |
646 | weight1 = weight1 + weight3 - newWeight; |
647 | weight3 = newWeight; |
648 | end; |
649 | if weight3 > 0 then |
650 | self:setMovingPlaneFillLevel(plane3, plane3.fillLevel + weight3*delta, addWarnings); |
651 | end; |
652 | else |
653 | weight1 = weight1 + weight3; |
654 | weight3 = 0; |
655 | end; |
656 | |
657 | self:setMovingPlaneFillLevel(plane1, plane1.fillLevel + weight1*delta, addWarnings); |
658 | end; |
659 | end; |
660 | |
661 | function BunkerSilo:setState(state) |
662 | if state ~= self.state then |
663 | if state == BunkerSilo.STATE_FILL then |
664 | if self.silagePlaneId ~= nil then |
665 | self.silagePlaneMoveAlpha = 0; |
666 | local x,_,z = getTranslation(self.silagePlaneId); |
667 | setTranslation(self.silagePlaneId, x, self.silagePlaneMinY, z); |
668 | setVisibility(self.silagePlaneId, false); |
669 | end; |
670 | elseif state == BunkerSilo.STATE_CLOSED then |
671 | self.fermentingTime = 0; |
672 | self:calculateSilagePlaneMoveAlphaMax(); |
673 | if self.silagePlaneId ~= nil then |
674 | setVisibility(self.silagePlaneId, true); |
675 | end; |
676 | elseif state == BunkerSilo.STATE_DRAIN then |
677 | if self.silagePlaneId ~= nil then |
678 | self.silagePlaneMoveAlpha = 0; |
679 | local x,_,z = getTranslation(self.silagePlaneId); |
680 | setTranslation(self.silagePlaneId, x, self.silagePlaneMinY, z); |
681 | setVisibility(self.silagePlaneId, false); |
682 | end; |
683 | -- change the color of the moving planes to silage |
684 | local colorScaleR = 0.3176; |
685 | local colorScaleG = 0.1882; |
686 | local colorScaleB = 0.0588; |
687 | for _,movingPlane in pairs(self.movingPlanes) do |
688 | setShaderParameter(movingPlane.nodeId, "colorScale", colorScaleR, colorScaleG, colorScaleB, 0, false); |
689 | end; |
690 | end; |
691 | if self.state == BunkerSilo.STATE_DRAIN then |
692 | -- if we switch from state drain to fill or closed, change the moving planes to chaff |
693 | for _,movingPlane in pairs(self.movingPlanes) do |
694 | setShaderParameter(movingPlane.nodeId, "colorScale", 1, 1, 1, 0, false); |
695 | end; |
696 | end |
697 | if state == BunkerSilo.STATE_DRAIN then |
698 | for _,trigger in pairs(self.tipTriggers) do |
699 | trigger.acceptedFillTypes[Fillable.FILLTYPE_SILAGE] = true; |
700 | trigger.acceptedFillTypes[Fillable.FILLTYPE_CHAFF] = false; |
701 | trigger.acceptedFillTypes[Fillable.FILLTYPE_GRASS_WINDROW] = false; |
702 | trigger.acceptedFillTypes[Fillable.FILLTYPE_DRYGRASS_WINDROW] = false; |
703 | end; |
704 | else |
705 | for _,trigger in pairs(self.tipTriggers) do |
706 | trigger.acceptedFillTypes[Fillable.FILLTYPE_SILAGE] = false; |
707 | trigger.acceptedFillTypes[Fillable.FILLTYPE_CHAFF] = true; |
708 | trigger.acceptedFillTypes[Fillable.FILLTYPE_GRASS_WINDROW] = true; |
709 | trigger.acceptedFillTypes[Fillable.FILLTYPE_DRYGRASS_WINDROW] = true; |
710 | end; |
711 | end; |
712 | self.state = state; |
713 | if self.isServer then |
714 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag); |
715 | end; |
716 | end; |
717 | end; |
718 | |
719 | function BunkerSilo:calculateSilagePlaneMoveAlphaMax() |
720 | self.silagePlaneMoveAlphaMax = 0; |
721 | for _,p in pairs(self.movingPlanes) do |
722 | self.silagePlaneMoveAlphaMax = math.max(self.silagePlaneMoveAlphaMax, p.fillLevel/p.capacity) |
723 | end; |
724 | self.silagePlaneMoveAlphaMax = self.silagePlaneMoveAlphaMax ^ self.movingPlanesYPower; |
725 | self.silagePlaneMoveAlphaMax = math.min(self.silagePlaneMoveAlphaMax + (1-self.silagePlaneMoveAlphaMax)*self.silagePlaneMoveMinOffset, 1); |
726 | end; |
727 | |
728 | function BunkerSilo:getCanInteract() |
729 | if (g_currentMission.controlPlayer and self.playerInRange) then |
730 | return true; |
731 | end; |
732 | if not g_currentMission.controlPlayer then |
733 | for vehicle in pairs(self.vehiclesInRange) do |
734 | if vehicle:getIsActiveForInput() then |
735 | return true; |
736 | end; |
737 | end; |
738 | end; |
739 | return false; |
740 | end; |
741 | |
742 | function BunkerSilo:getCanCloseSilo() |
743 | return self.state == BunkerSilo.STATE_FILL and self.fillLevel > 0.10*self.capacity and self.compactedFillLevel >= self.fillLevel-0.01; |
744 | end; |
745 | |
746 | function BunkerSilo:interactionTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
747 | if onEnter or onLeave then |
748 | if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then |
749 | if onEnter then |
750 | self.playerInRange = true; |
751 | g_currentMission:removeActivatableObject(self.activatable); -- make sure it is not added twice |
752 | g_currentMission:addActivatableObject(self.activatable); |
753 | else |
754 | self.playerInRange = false; |
755 | if self.numVehiclesInRange == 0 then |
756 | g_currentMission:removeActivatableObject(self.activatable); |
757 | end; |
758 | end; |
759 | else |
760 | local vehicle = g_currentMission.nodeToVehicle[otherId]; |
761 | if vehicle ~= nil then |
762 | if onEnter then |
763 | if self.vehiclesInRange[vehicle] == nil then |
764 | self.vehiclesInRange[vehicle] = true; |
765 | self.numVehiclesInRange = self.numVehiclesInRange + 1; |
766 | |
767 | g_currentMission:removeActivatableObject(self.activatable); -- make sure it is not added twice |
768 | g_currentMission:addActivatableObject(self.activatable); |
769 | end; |
770 | else |
771 | if self.vehiclesInRange[vehicle] then |
772 | self.vehiclesInRange[vehicle] = nil; |
773 | self.numVehiclesInRange = self.numVehiclesInRange - 1; |
774 | |
775 | if self.numVehiclesInRange == 0 and not self.playerInRange then |
776 | g_currentMission:removeActivatableObject(self.activatable); |
777 | end; |
778 | end; |
779 | end; |
780 | end; |
781 | end; |
782 | end; |
783 | end; |
784 | |
785 | function BunkerSilo:compactingTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
786 | if onEnter or onLeave then |
787 | local vehicle = g_currentMission.nodeToVehicle[otherId]; |
788 | if vehicle ~= nil and (vehicle.onEnter ~= nil or vehicle.bunkerSiloCompactingScale ~= nil) then |
789 | if onEnter then |
790 | self.compactingVehicles[vehicle] = Utils.getNoNil(vehicle:getTotalMass(true), BunkerSilo.COMPACTING_BASE_MASS); |
791 | else |
792 | self.compactingVehicles[vehicle] = nil; |
793 | end; |
794 | end; |
795 | end; |
796 | end; |
797 | |
798 | |
799 | -- functions to act as a shovel target |
800 | function BunkerSilo:getAllowShovelFillType(fillType) |
801 | if self.state == BunkerSilo.STATE_DRAIN then |
802 | return fillType == Fillable.FILLTYPE_SILAGE; -- only allow unloading of silage into a silo with silage in it |
803 | elseif self.state == BunkerSilo.STATE_FILL then |
804 | -- In fill state, accept the same fillTypes as the tip triggers |
805 | for _,tipTrigger in pairs(self.tipTriggers) do |
806 | if tipTrigger.acceptedFillTypes[fillType] then |
807 | return true; |
808 | end |
809 | end |
810 | end |
811 | return false; |
812 | end; |
813 | |
814 | function BunkerSilo:addShovelFillLevel(shovel, fillLevelDelta, fillType) |
815 | if self:getAllowShovelFillType(fillType) then |
816 | local oldFillLevel = self.fillLevel; |
817 | |
818 | local x,y,z = 0,0,0; |
819 | if shovel.shovelTipReferenceNode ~= nil then |
820 | x,y,z = getWorldTranslation(shovel.shovelTipReferenceNode); |
821 | end; |
822 | self:setFillDeltaAt(x,y,z, fillLevelDelta, true); |
823 | |
824 | return self.fillLevel - oldFillLevel; |
825 | end; |
826 | return 0; |
827 | end; |
828 | |
829 | --function BunkerSilo:testScope(x,y,z, coeff) |
830 | --end; |
831 | |
832 | --function BunkerSilo:getUpdatePriority(skipCount, x, y, z, coeff, connection) |
833 | --end; |
834 | |
835 | --function BunkerSilo:onGhostRemove() |
836 | --end; |
837 | |
838 | --function BunkerSilo:onGhostAdd() |
839 | --end; |
840 | |
841 | |
842 | |
843 | |
844 | BunkerSiloActivatable = {} |
845 | local BunkerSiloActivatable_mt = Class(BunkerSiloActivatable); |
846 | |
847 | function BunkerSiloActivatable:new(bunkerSilo) |
848 | local self = {}; |
849 | setmetatable(self, BunkerSiloActivatable_mt); |
850 | |
851 | self.bunkerSilo = bunkerSilo; |
852 | self.activateText = "unknown"; |
853 | |
854 | return self; |
855 | end; |
856 | |
857 | |
858 | function BunkerSiloActivatable:getIsActivatable() |
859 | if self.bunkerSilo:getCanInteract() then |
860 | if self.bunkerSilo:getCanCloseSilo() then |
861 | self:updateActivateText(); |
862 | return true; |
863 | end; |
864 | end; |
865 | return false; |
866 | end; |
867 | |
868 | function BunkerSiloActivatable:onActivateObject() |
869 | if self.bunkerSilo:getCanCloseSilo() then |
870 | self.bunkerSilo:setState(BunkerSilo.STATE_CLOSED); |
871 | if g_server == nil then |
872 | g_client:getServerConnection():sendEvent(BunkerSiloCloseEvent:new(self.bunkerSilo)); |
873 | end; |
874 | end; |
875 | self:updateActivateText(); |
876 | g_currentMission:addActivatableObject(self); |
877 | end; |
878 | |
879 | function BunkerSiloActivatable:drawActivate() |
880 | -- TODO draw icon |
881 | end; |
882 | |
883 | function BunkerSiloActivatable:updateActivateText() |
884 | self.activateText = "unknown"; |
885 | if self.bunkerSilo.state == BunkerSilo.STATE_FILL then |
886 | self.activateText = g_i18n:getText("blanket_silo"); |
887 | end; |
888 | end;
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de