Sprache Deutsch Language English

Script Dokumentation LS 2015 - BunkerSilo (Patch 1.3)

Script Dokumentation Übersicht

scripts/objects/BunkerSilo.lua

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
3BunkerSilo = {};
4BunkerSilo_mt = Class(BunkerSilo, Object);
5
6InitStaticObjectClass(BunkerSilo, "BunkerSilo", ObjectIds.OBJECT_BUNKER_SILO);
7
8BunkerSilo.STATE_FILL = 0;
9BunkerSilo.STATE_CLOSED = 1;
10BunkerSilo.STATE_DRAIN = 2;
11BunkerSilo.NUM_STATES = 3;
12
13BunkerSilo.COMPACTING_BASE_MASS = 5;
14
15function 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;
23end;
24
25function 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;
72end;
73
74function 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);
103end;
104
105function 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;
131end;
132
133function 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;
148end;
149
150function 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;
165end;
166
167function 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;
181end;
182
183function 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;
346end;
347
348function 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;
396end;
397
398function 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;
413end;
414
415function 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;
529end;
530
531function 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;
549end;
550
551function 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;
590end;
591
592function 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;
602end;
603
604function 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;
659end;
660
661function 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;
717end;
718
719function 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);
726end;
727
728function 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;
740end;
741
742function BunkerSilo:getCanCloseSilo()
743 return self.state == BunkerSilo.STATE_FILL and self.fillLevel > 0.10*self.capacity and self.compactedFillLevel >= self.fillLevel-0.01;
744end;
745
746function 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;
783end;
784
785function 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;
796end;
797
798
799-- functions to act as a shovel target
800function 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;
812end;
813
814function 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;
827end;
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
844BunkerSiloActivatable = {}
845local BunkerSiloActivatable_mt = Class(BunkerSiloActivatable);
846
847function BunkerSiloActivatable:new(bunkerSilo)
848 local self = {};
849 setmetatable(self, BunkerSiloActivatable_mt);
850
851 self.bunkerSilo = bunkerSilo;
852 self.activateText = "unknown";
853
854 return self;
855end;
856
857
858function 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;
866end;
867
868function 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);
877end;
878
879function BunkerSiloActivatable:drawActivate()
880 -- TODO draw icon
881end;
882
883function 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;
888end;
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
Script Dokumentation Übersicht