LUADOC - Farming Simulator 17

Printable Version

BunkerSilo

Description
Class for bunker silo
Functions

onCreate

Description
Creating bunker silo object
Definition
onCreate(integer id)
Arguments
integeridnode id
Code
23function BunkerSilo:onCreate(id)
24 local object = BunkerSilo:new(g_server ~= nil, g_client ~= nil);
25 if object:load(id) then
26 g_currentMission:addOnCreateLoadedObject(object);
27 g_currentMission:addOnCreateLoadedObjectToSave(object);
28 object:register(true);
29 else
30 object:delete();
31 end;
32end;

new

Description
Creating bunker silo object
Definition
new(boolean isServer, boolean isClient, table customMt)
Arguments
booleanisServeris server
booleanisClientis client
tablecustomMtcustomMt
Return Values
tableinstanceInstance of object
Code
40function BunkerSilo:new(isServer, isClient, customMt)
41 local mt = customMt;
42 if mt == nil then
43 mt = BunkerSilo_mt;
44 end;
45
46 local self = Object:new(isServer, isClient, mt);
47 self.nodeId = 0;
48
49 self.acceptedFillTypes = {};
50 self.inputFillType = FillUtil.FILLTYPE_CHAFF;
51 self.fermentingFillType = FillUtil.FILLTYPE_TARP;
52 self.outputFillType = FillUtil.FILLTYPE_SILAGE;
53
54 self.fillLevel = 0;
55 self.compactedFillLevel = 0;
56 self.compactedPercent = 0;
57
58 self.emptyThreshold = 100;
59
60 self.fermentingTime = 0;
61 self.fermentingDuration = 6*60*60; -- 6hours (ingame)
62 self.fermentingPercent = 0;
63
64 self.isOpenedAtFront = false;
65 self.isOpenedAtBack = false;
66
67 self.distanceToCompactedFillLevel = 100;
68
69 self.interactionTriggerId = nil;
70
71 self.playerInRange = false;
72 self.vehiclesInRange = {};
73 self.numVehiclesInRange = 0;
74
75 self.bunkerSiloArea = {};
76 self.bunkerSiloArea.offsetFront = 0;
77 self.bunkerSiloArea.offsetBack = 0
78
79 self.siloIsFullWarningTimer = 0;
80 self.siloIsFullWarningDuration = 2000;
81
82 self.updateTimer = 0;
83
84 self.activatable = BunkerSiloActivatable:new(self);
85
86 self.state = BunkerSilo.STATE_FILL;
87
88 self.bunkerSiloDirtyFlag = self:getNextDirtyFlag();
89 return self;
90end;

delete

Description
Deleting bunker silo object
Definition
delete()
Code
94function BunkerSilo:delete()
95 g_currentMission:removeOnCreateLoadedObjectToSave(self);
96 if self.interactionTriggerId ~= nil then
97 removeTrigger(self.interactionTriggerId);
98 end;
99 if self.nodeId ~= 0 then
100 g_currentMission:removeNodeObject(self.nodeId);
101 end;
102 g_currentMission:removeActivatableObject(self.activatable);
103 BunkerSilo:superClass().delete(self);
104end;

readStream

Description
Called on client side on join
Definition
readStream(integer streamId, table connection)
Arguments
integerstreamIdstream ID
tableconnectionconnection
Code
110function BunkerSilo:readStream(streamId, connection)
111 BunkerSilo:superClass().readStream(self, streamId, connection);
112 if connection:getIsServer() then
113 local state = streamReadUIntN(streamId, 3);
114 self:setState(state);
115 self.isOpenedAtFront = streamReadBool(streamId);
116 self.isOpenedAtBack = streamReadBool(streamId);
117 self.fillLevel = streamReadFloat32(streamId);
118 self.compactedPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5);
119 self.fermentingPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5);
120 end;
121end;

writeStream

Description
Called on server side on join
Definition
writeStream(integer streamId, table connection)
Arguments
integerstreamIdstream ID
tableconnectionconnection
Code
127function BunkerSilo:writeStream(streamId, connection)
128 BunkerSilo:superClass().writeStream(self, streamId, connection);
129 if not connection:getIsServer() then
130 streamWriteUIntN(streamId, self.state, 3);
131 streamWriteBool(streamId, self.isOpenedAtFront);
132 streamWriteBool(streamId, self.isOpenedAtBack);
133 streamWriteFloat32(streamId, self.fillLevel);
134 streamWriteUIntN(streamId, 2.55*self.compactedPercent, 8);
135 streamWriteUIntN(streamId, 2.55*self.fermentingPercent, 8);
136 end;
137end;

readUpdateStream

Description
Called on client side on update
Definition
readUpdateStream(integer streamId, integer timestamp, table connection)
Arguments
integerstreamIdstream ID
integertimestamptimestamp
tableconnectionconnection
Code
144function BunkerSilo:readUpdateStream(streamId, timestamp, connection)
145 BunkerSilo:superClass().readUpdateStream(self, streamId, timestamp, connection)
146 if connection:getIsServer() then
147 if streamReadBool(streamId) then
148 local state = streamReadUIntN(streamId, 3);
149 if state ~= self.state then
150 self:setState(state, true);
151 end
152
153 self.fillLevel = streamReadFloat32(streamId);
154 self.isOpenedAtFront = streamReadBool(streamId);
155 self.isOpenedAtBack = streamReadBool(streamId);
156
157 if self.state == BunkerSilo.STATE_FILL then
158 self.compactedPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5);
159 elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then
160 self.fermentingPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5);
161 end
162 end
163 end;
164end;

writeUpdateStream

Description
Called on server side on update
Definition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)
Arguments
integerstreamIdstream ID
tableconnectionconnection
integerdirtyMaskdirty mask
Code
171function BunkerSilo:writeUpdateStream(streamId, connection, dirtyMask)
172 BunkerSilo:superClass().writeUpdateStream(self, streamId, connection, dirtyMask)
173 if not connection:getIsServer() then
174 if streamWriteBool(streamId, bitAND(dirtyMask, self.bunkerSiloDirtyFlag) ~= 0) then
175 streamWriteUIntN(streamId, self.state, 3);
176
177 streamWriteFloat32(streamId, self.fillLevel);
178 streamWriteBool(streamId, self.isOpenedAtFront);
179 streamWriteBool(streamId, self.isOpenedAtBack);
180
181 if self.state == BunkerSilo.STATE_FILL then
182 streamWriteUIntN(streamId, 2.55*self.compactedPercent, 8);
183 elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then
184 streamWriteUIntN(streamId, 2.55*self.fermentingPercent, 8);
185 end
186 end
187 end;
188end;

load

Description
Load bunker silo
Definition
load(integer nodeId)
Arguments
integernodeIdnode id
Return Values
booleansuccesssuccess
Code
194function BunkerSilo:load(nodeId)
195
196 self.nodeId = nodeId;
197
198 local areaNode = Utils.indexToObject(nodeId, getUserAttribute(nodeId, "bunkerSiloArea"));
199 if areaNode == nil then
200 print("Warning: BunkerSilo, could not resolve attribute 'bunkerSiloArea' into a valid node!");
201 return false;
202 end
203 self.bunkerSiloArea.start = getChildAt(areaNode, 0);
204 self.bunkerSiloArea.width = getChildAt(areaNode, 1);
205 self.bunkerSiloArea.height = getChildAt(areaNode, 2);
206
207 self.bunkerSiloArea.sx, self.bunkerSiloArea.sy, self.bunkerSiloArea.sz = getWorldTranslation(self.bunkerSiloArea.start);
208 self.bunkerSiloArea.wx, self.bunkerSiloArea.wy, self.bunkerSiloArea.wz = getWorldTranslation(self.bunkerSiloArea.width);
209 self.bunkerSiloArea.hx, self.bunkerSiloArea.hy, self.bunkerSiloArea.hz = getWorldTranslation(self.bunkerSiloArea.height);
210
211 self.bunkerSiloArea.dhx = self.bunkerSiloArea.hx - self.bunkerSiloArea.sx;
212 self.bunkerSiloArea.dhy = self.bunkerSiloArea.hy - self.bunkerSiloArea.sy;
213 self.bunkerSiloArea.dhz = self.bunkerSiloArea.hz - self.bunkerSiloArea.sz;
214 local dhLength = Utils.vector3Length(self.bunkerSiloArea.dhx, self.bunkerSiloArea.dhy, self.bunkerSiloArea.dhz);
215 self.bunkerSiloArea.dhx_norm = self.bunkerSiloArea.dhx / dhLength;
216 self.bunkerSiloArea.dhy_norm = self.bunkerSiloArea.dhy / dhLength;
217 self.bunkerSiloArea.dhz_norm = self.bunkerSiloArea.dhz / dhLength;
218
219 self.bunkerSiloArea.dwx = self.bunkerSiloArea.wx - self.bunkerSiloArea.sx;
220 self.bunkerSiloArea.dwy = self.bunkerSiloArea.wy - self.bunkerSiloArea.sy;
221 self.bunkerSiloArea.dwz = self.bunkerSiloArea.wz - self.bunkerSiloArea.sz;
222 local dwLength = Utils.vector3Length(self.bunkerSiloArea.dwx, self.bunkerSiloArea.dwy, self.bunkerSiloArea.dwz);
223 self.bunkerSiloArea.dwx_norm = self.bunkerSiloArea.dwx / dwLength;
224 self.bunkerSiloArea.dwy_norm = self.bunkerSiloArea.dwy / dwLength;
225 self.bunkerSiloArea.dwz_norm = self.bunkerSiloArea.dwz / dwLength;
226
227
228 self.acceptedFillTypes = {};
229 local dataString = Utils.getNoNil(getUserAttribute(nodeId, "acceptedFillTypes"), "chaff grass_windrow dryGrass_windrow");
230 local data = Utils.splitString(" ", dataString);
231 for i=1,table.getn(data) do
232 local fillType = FillUtil.fillTypeNameToInt[data[i] ];
233 if fillType ~= nil then
234 self.acceptedFillTypes[fillType] = true;
235 else
236 print("Warning: BunkerSilo has an invalid fillType (acceptedFillTypes): "..tostring(data[i]));
237 end
238 end
239
240 local dataString = Utils.getNoNil(getUserAttribute(nodeId, "inputFillType"), "chaff");
241 local fillType = FillUtil.fillTypeNameToInt[dataString];
242 if fillType ~= nil then
243 self.inputFillType = fillType;
244 else
245 print("Warning: BunkerSilo has an invalid fillType (inputFillType): "..tostring(dataString));
246 end
247
248 local dataString = Utils.getNoNil(getUserAttribute(nodeId, "outputFillType"), "silage");
249 local fillType = FillUtil.fillTypeNameToInt[dataString];
250 if fillType ~= nil then
251 self.outputFillType = fillType;
252 else
253 print("Warning: BunkerSilo has an invalid fillType (outputFillType): "..tostring(dataString));
254 end
255
256 if self.isServer then
257 TipUtil.setConvertingFillTypeAreas(self.bunkerSiloArea, self.acceptedFillTypes, self.inputFillType);
258 end
259
260
261 local distanceToCompactedFillLevelStr = getUserAttribute(nodeId, "distanceToCompactedFillLevel");
262 if distanceToCompactedFillLevelStr ~= nil then
263 self.distanceToCompactedFillLevel = Utils.getNoNil(tonumber(distanceToCompactedFillLevelStr), self.distanceToCompactedFillLevel);
264 end;
265
266 local fermentingDurationStr = getUserAttribute(nodeId, "fermentingDuration");
267 if fermentingDurationStr ~= nil then
268 self.fermentingDuration = Utils.getNoNil(tonumber(fermentingDurationStr), self.fermentingDuration);
269 end;
270
271 self.fillLevel = 0;
272
273 local interactionTriggerIndex = getUserAttribute(nodeId, "interactionTriggerIndex");
274 if interactionTriggerIndex ~= nil then
275 self.interactionTriggerId = Utils.indexToObject(nodeId, interactionTriggerIndex);
276 if self.interactionTriggerId ~= nil then
277 addTrigger(self.interactionTriggerId, "interactionTriggerCallback", self);
278 end;
279 end;
280
281 self.openingLength = Utils.getNoNil(getUserAttribute(nodeId, "openingLength"), 5);
282
283 -- adjust timings to difficulty
284 local difficultyMultiplier = g_currentMission.missionInfo.difficulty;
285 self.fermentingDuration = self.fermentingDuration*difficultyMultiplier;
286 self.distanceToCompactedFillLevel = self.distanceToCompactedFillLevel/difficultyMultiplier;
287
288 -- just for tutorial 12 (feeder)
289 self.isTutorialSilo = Utils.getNoNil( getUserAttribute(nodeId, "isTutorialSilo"), false );
290
291 self.saveId = Utils.getNoNil( getUserAttribute(nodeId, "saveId"), "BunkerSilo_"..getName(nodeId) );
292
293 g_currentMission:addNodeObject(self.nodeId, self);
294
295 self:setState(BunkerSilo.STATE_FILL);
296
297
298 return true;
299end;

loadFromAttributesAndNodes

Description
Loading from attributes and nodes
Definition
loadFromAttributesAndNodes(integer xmlFile, string key)
Arguments
integerxmlFileid of xml object
stringkeykey
Return Values
booleansuccesssuccess
Code
306function BunkerSilo:loadFromAttributesAndNodes(xmlFile, key)
307
308 local state = getXMLInt(xmlFile, key.."#state");
309 if state ~= nil then
310 if state >= 0 and state < BunkerSilo.NUM_STATES then
311 self:setState(state);
312 end;
313 end;
314
315 local fillLevel = getXMLFloat(xmlFile, key.."#fillLevel");
316 if fillLevel ~= nil then
317 self.fillLevel = fillLevel;
318 end;
319 local compactedFillLevel = getXMLFloat(xmlFile, key.."#compactedFillLevel");
320 if compactedFillLevel ~= nil then
321 self.compactedFillLevel = Utils.clamp(compactedFillLevel, 0, self.fillLevel);
322 end;
323 self.compactedPercent = Utils.getFlooredPercent(math.min(self.compactedFillLevel, self.fillLevel), self.fillLevel);
324
325 local fermentingTime = getXMLFloat(xmlFile, key.."#fermentingTime");
326 if fermentingTime ~= nil then
327 self.fermentingTime = Utils.clamp(fermentingTime, 0, self.fermentingDuration);
328 self.fermentingPercent = Utils.getFlooredPercent(self.fermentingTime, self.fermentingDuration);
329 end;
330
331 self.isOpenedAtFront = Utils.getNoNil(getXMLBool(xmlFile, key.."#openedAtFront"), false);
332 self.isOpenedAtBack = Utils.getNoNil(getXMLBool(xmlFile, key.."#openedAtBack"), false);
333
334 if self.isOpenedAtFront then
335 self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.outputFillType);
336 else
337 self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.fermentingFillType);
338 end
339 if self.isOpenedAtBack then
340 self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.outputFillType);
341 else
342 self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.fermentingFillType);
343 end
344
345 if self.fillLevel > 0 and self.state == BunkerSilo.STATE_DRAIN then
346 local area = self.bunkerSiloArea;
347 local offWx = area.wx - area.sx;
348 local offWz = area.wz - area.sz;
349 local offW = math.sqrt(offWx*offWx + offWz*offWz);
350
351 local offHx = area.hx - area.sx;
352 local offHz = area.hz - area.sz;
353 local offH = math.sqrt(offHx*offHx + offHz*offHz);
354
355 if offW > 0.001 and offH > 0.001 then
356 -- offset by 0.9m in each direction (and max 45%)
357 local offWScale = math.min(0.45, 0.9 / offW);
358 offWx = offWx * offWScale;
359 offWz = offWz * offWScale;
360
361 local offHScale = math.min(0.45, 0.9 / offH);
362 offHx = offHx * offHScale;
363 offHz = offHz * offHScale;
364
365 local innerFillLevel1 = TipUtil.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)
366 local innerFillLevel2 = TipUtil.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)
367 local innerFillLevel = innerFillLevel1 + innerFillLevel2;
368 if innerFillLevel < self.emptyThreshold*0.5 then
369 TipUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.fermentingFillType);
370 TipUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.outputFillType);
371 self:setState(BunkerSilo.STATE_FILL, false);
372 end
373 end
374 elseif self.state == BunkerSilo.STATE_FILL then
375 local area = self.bunkerSiloArea;
376 local fermentingFillLevel, fermentingPixels, totalFermentingPixels = TipUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz);
377 -- Set to fermented state if more than 50% of the area is filled with fermenting fill type
378 if fermentingFillLevel > self.emptyThreshold and fermentingPixels > 0.5 * totalFermentingPixels then
379 local inputFillLevel, inputPixels, totalInputPixels = TipUtil.getFillLevelAtArea(self.inputFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz);
380 -- Only change if less than 10% is filled with input type (chaff) (ie. the silo is not being filled)
381 if inputPixels < 0.1*totalInputPixels then
382 self:setState(BunkerSilo.STATE_FERMENTED, false);
383 end
384 end
385 end
386
387 return true;
388end;

getSaveAttributesAndNodes

Description
Get save attributes and nodes
Definition
getSaveAttributesAndNodes(string nodeIdent)
Arguments
stringnodeIdentnode ident
Return Values
stringattributesattributes
stringnodesnodes
Code
395function BunkerSilo:getSaveAttributesAndNodes(nodeIdent)
396
397 local attributes = 'state="'..self.state..'" fillLevel="'..self.fillLevel..'" compactedFillLevel="'..self.compactedFillLevel..'"';
398 attributes = attributes .. ' fermentingTime="'..self.fermentingTime..'"';
399 attributes = attributes .. ' openedAtFront="' .. tostring(self.isOpenedAtFront) ..'" openedAtBack="' .. tostring(self.isOpenedAtBack) .. '"';
400 local nodes = "";
401
402 return attributes, nodes;
403end;

update

Description
Update
Definition
update(float dt)
Arguments
floatdttime since last call in ms
Code
408function BunkerSilo:update(dt)
409
410 if self:getCanInteract(true) then
411 local fillType = self.inputFillType;
412 if self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED or self.state == BunkerSilo.STATE_DRAIN then
413 fillType = self.outputFillType;
414 end
415 local fillTypeName = "";
416 if FillUtil.fillTypeIndexToDesc[fillType] ~= nil then
417 fillTypeName = FillUtil.fillTypeIndexToDesc[fillType].nameI18N;
418 end
419 if self.state == BunkerSilo.STATE_FILL then
420 g_currentMission:addExtraPrintText(g_i18n:getText("info_fillLevel")..string.format(" %s: %d", fillTypeName, self.fillLevel));
421 g_currentMission:addExtraPrintText(g_i18n:getText("info_compacting")..string.format(" %d%%", self.compactedPercent));
422 elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then
423 g_currentMission:addExtraPrintText(g_i18n:getText("info_fermenting")..string.format(" %s: %d%%", fillTypeName, self.fermentingPercent));
424 elseif self.state == BunkerSilo.STATE_DRAIN then
425 g_currentMission:addExtraPrintText(g_i18n:getText("info_fillLevel")..string.format(" %s: %d", fillTypeName, self.fillLevel));
426 end;
427 end;
428
429 if self.state == BunkerSilo.STATE_CLOSED then
430 if self.isServer then
431 self.fermentingTime = math.min(self.fermentingDuration, self.fermentingTime + dt*0.001*g_currentMission.missionInfo.timeScale);
432 local fermentingPercent = Utils.getFlooredPercent(self.fermentingTime, self.fermentingDuration);
433 if fermentingPercent ~= self.fermentingPercent then
434 self.fermentingPercent = fermentingPercent;
435 self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
436 end
437 if self.fermentingTime >= self.fermentingDuration then
438 self:setState(BunkerSilo.STATE_FERMENTED, true);
439 end;
440 end;
441 end;
442
443 if self.isServer then
444 if self.state == BunkerSilo.STATE_FILL then
445 for vehicle,state in pairs(self.vehiclesInRange) do
446 if state then
447 if vehicle:getIsActive() then
448 local distance = vehicle.lastMovedDistance;
449 if distance > 0 then
450 local mass = vehicle:getTotalMass(false);
451
452 local refNode = vehicle.bunkerSiloCompactingRefNode;
453 if refNode == nil then
454 refNode = vehicle.components[1].node;
455 end
456 local scale = (mass / BunkerSilo.COMPACTING_BASE_MASS) * Utils.getNoNil(vehicle.bunkerSiloCompactingScale, 1);
457
458 local deltaCompact = distance*scale*self.distanceToCompactedFillLevel;
459
460 local numWheels = table.getn(vehicle.wheels);
461 if numWheels > 0 then
462 local wheelsOnSilo = 0;
463 local wheelsInAir = 0;
464 for i=1,numWheels do
465 local wheel = vehicle.wheels[i];
466 if wheel.contact == Vehicle.WHEEL_GROUND_HEIGHT_CONTACT then
467 wheelsOnSilo = wheelsOnSilo + 1;
468 elseif wheel.contact == Vehicle.WHEEL_NO_CONTACT then
469 wheelsInAir = wheelsInAir + 1;
470 end
471 end
472 if wheelsOnSilo > 0 then
473 deltaCompact = deltaCompact * ((wheelsOnSilo + wheelsInAir) / numWheels);
474 else
475 deltaCompact = 0;
476 end
477 end
478
479 if deltaCompact > 0 then
480 local compactedFillLevel = math.min(self.compactedFillLevel + deltaCompact, self.fillLevel);
481 if compactedFillLevel ~= self.compactedFillLevel then
482 self.compactedFillLevel = compactedFillLevel;
483 self.compactedPercent = Utils.getFlooredPercent(math.min(self.compactedFillLevel, self.fillLevel), self.fillLevel);
484 self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
485 end
486 end
487 end;
488 end;
489 end
490 end;
491 end;
492 end;
493
494 -- for chaff tutorial: always take the highest fill level of all bunker silos
495 if g_currentMission ~= nil and g_currentMission.bunkerScore ~= nil then
496 if g_currentMission.bunkerScore < self.fillLevel then
497 g_currentMission.bunkerScore = self.fillLevel;
498 end;
499 end;
500end;

updateTick

Description
UpdateTick
Definition
updateTick(float dt)
Arguments
floatdttime since last call in ms
Code
505function BunkerSilo:updateTick(dt)
506 if self.isServer then
507 self.updateTimer = self.updateTimer - dt;
508 if self.updateTimer <= 0 then
509 self.updateTimer = 200 + math.random()*100 -- update every 200 to 300ms
510
511 local oldFillLevel = self.fillLevel;
512 self:updateFillLevel();
513 if oldFillLevel ~= self.fillLevel then
514 self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
515 end;
516 end
517 end;
518 if not self.adjustedOpeningLength then
519 self.adjustedOpeningLength = true;
520 self.openingLength = math.max(self.openingLength, TipUtil.getDefaultMaxRadius(self.outputFillType)+1);
521 end
522end;

setState

Description
Set state
Definition
setState(boolean state, boolean showNotification)
Arguments
booleanstatenew state
booleanshowNotificationshow notification
Code
556function BunkerSilo:setState(state, showNotification)
557
558 if state ~= self.state then
559
560 if state == BunkerSilo.STATE_FILL then
561
562 self.fermentingTime = 0;
563 self.fermentingPercent = 0;
564 self.compactedFillLevel = 0;
565 self.isOpenedAtFront = false;
566 self.isOpenedAtBack = false;
567 self.bunkerSiloArea.offsetFront = 0;
568 self.bunkerSiloArea.offsetBack = 0;
569
570 if showNotification then
571 g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloIsEmpty"));
572 end
573
574 if self.isServer then
575 TipUtil.removeFixedFillTypesArea(self.bunkerSiloArea);
576 TipUtil.setConvertingFillTypeAreas(self.bunkerSiloArea, self.acceptedFillTypes, self.inputFillType);
577 end
578
579 elseif state == BunkerSilo.STATE_CLOSED then
580
581 if self.isServer then
582 -- change fillType
583 local area = self.bunkerSiloArea;
584 local offsetFront = self:getBunkerAreaOffset(true, 0, self.inputFillType);
585 local offsetBack = self:getBunkerAreaOffset(false, 0, self.inputFillType);
586
587 local x0 = area.sx + (offsetFront * area.dhx_norm);
588 local z0 = area.sz + (offsetFront * area.dhz_norm);
589 local x1 = x0 + area.dwx;
590 local z1 = z0 + area.dwz;
591 local x2 = area.sx + area.dhx - (offsetBack * area.dhx_norm);
592 local z2 = area.sz + area.dhz - (offsetBack * area.dhz_norm);
593
594 local changed = TipUtil.changeFillTypeAtArea(x0,z0, x1,z1, x2,z2, self.inputFillType, self.fermentingFillType);
595
596 TipUtil.removeFixedFillTypesArea(self.bunkerSiloArea);
597 TipUtil.removeConvertingFillTypeAreas(self.bunkerSiloArea);
598 end
599
600 if showNotification then
601 g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloCovered"));
602 end
603
604 elseif state == BunkerSilo.STATE_FERMENTED then
605
606 if showNotification then
607 g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloDoneFermenting"));
608 end
609
610 elseif state == BunkerSilo.STATE_DRAIN then
611
612 self.bunkerSiloArea.offsetFront = 0;
613 self.bunkerSiloArea.offsetBack = 0;
614
615 if showNotification then
616 g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloOpened"));
617 end
618
619 if self.isServer then
620 TipUtil.removeConvertingFillTypeAreas(self.bunkerSiloArea);
621 local fillTypes = {};
622 fillTypes[self.outputFillType] = true;
623 TipUtil.setFixedFillTypesArea(self.bunkerSiloArea, fillTypes);
624 end
625
626 end;
627
628 self.state = state;
629 if self.isServer then
630 self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
631 end;
632 end;
633end;

openSilo

Description
Open silo
Definition
openSilo(float px, float py, float pz)
Arguments
floatpxx player position
floatpyy player position
floatpzz player position
Code
640function BunkerSilo:openSilo(px,py,pz)
641 self:setState(BunkerSilo.STATE_DRAIN, true);
642
643 self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.fermentingFillType);
644 self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.fermentingFillType);
645
646 -- check which side is closer to player
647 local openAtFront = self:getIsCloserToFront(px,py,pz);
648 if openAtFront and not self.isOpenedAtFront then
649 self:switchFillTypeAtOffset(true, self.bunkerSiloArea.offsetFront, self.openingLength);
650 self.isOpenedAtFront = true;
651 self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
652 elseif not self.isOpenedAtBack then
653 self:switchFillTypeAtOffset(false, self.bunkerSiloArea.offsetBack, self.openingLength);
654 self.isOpenedAtBack = true;
655 self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
656 end
657end

getBunkerAreaOffset

Description
Get bunker area offset
Definition
getBunkerAreaOffset(boolean updateAtFront, float offset, integer fillType)
Arguments
booleanupdateAtFrontupdate at front
floatoffsetoffset
integerfillTypefill type
Return Values
floatoffsetoffset
Code
665function BunkerSilo:getBunkerAreaOffset(updateAtFront, offset, fillType)
666 local area = self.bunkerSiloArea;
667
668 local hx, hz = area.dhx_norm, area.dhz_norm;
669 local hl = Utils.vector3Length(area.dhx, area.dhy, area.dhz);
670
671 while offset <= (hl - 1) do
672 local pos = offset;
673 if not updateAtFront then
674 pos = hl - offset - 1;
675 end
676 local d1x,d1z = pos*hx, pos*hz;
677 local d2x,d2z = (pos+1)*hx, (pos+1)*hz;
678
679 local a0x, a0z = area.sx + d1x, area.sz + d1z;
680 local a1x, a1z = area.wx + d1x, area.wz + d1z;
681 local a2x, a2z = area.sx + d2x, area.sz + d2z;
682
683 local fillLevel = TipUtil.getFillLevelAtArea(fillType, a0x,a0z, a1x,a1z, a2x,a2z);
684 if fillLevel > 0 then
685 return offset;
686 end
687 offset = offset + 1;
688 end
689
690 return math.max(hl - 1, 0);
691end

switchFillTypeAtOffset

Description
Switch fill type at offset
Definition
switchFillTypeAtOffset(boolean switchAtFront, float offset, float length)
Arguments
booleanswitchAtFrontswitch at front
floatoffsetoffset
floatlengthlength
Code
698function BunkerSilo:switchFillTypeAtOffset(switchAtFront, offset, length)
699
700 local fillType = self.fermentingFillType;
701 local newFillType = self.outputFillType;
702
703 local a0x, a0z = nil, nil;
704 local a1x, a1z = nil, nil;
705 local a2x, a2z = nil, nil;
706
707 local area = self.bunkerSiloArea;
708
709 if switchAtFront then
710 a0x, a0z = area.sx + (offset * area.dhx_norm), area.sz + (offset * area.dhz_norm);
711 a1x, a1z = a0x + area.dwx, a0z + area.dwz;
712 a2x, a2z = area.sx + ((offset + length) * area.dhx_norm), area.sz + ((offset + length) * area.dhz_norm);
713 else
714 a0x, a0z = area.hx - (offset * area.dhx_norm), area.hz - (offset * area.dhz_norm);
715 a1x, a1z = a0x + area.dwx, a0z + area.dwz;
716 a2x, a2z = area.hx - ((offset + length) * area.dhx_norm), area.hz - ((offset + length) * area.dhz_norm);
717 end
718
719 TipUtil.changeFillTypeAtArea(a0x,a0z, a1x,a1z, a2x,a2z, fillType, newFillType);
720
721end

getIsCloserToFront

Description
Get is closer to front
Definition
getIsCloserToFront(float ix, float iy, float iz)
Arguments
floatixx position
floatiyy position
floatizz position
Return Values
booleanisCloserToFrontis closer to front
Code
729function BunkerSilo:getIsCloserToFront(ix,iy,iz)
730 local area = self.bunkerSiloArea;
731
732 local x = area.sx + (0.5*area.dwx) + (area.offsetFront * area.dhx_norm);
733 local y = area.sy + (0.5*area.dwy) + (area.offsetFront * area.dhy_norm);
734 local z = area.sz + (0.5*area.dwz) + (area.offsetFront * area.dhz_norm);
735 local distFront = Utils.vector3Length(x-ix, y-iy, z-iz);
736
737 local x = area.sx + (0.5*area.dwx) + area.dhx - (area.offsetBack * area.dhx_norm);
738 local y = area.sy + (0.5*area.dwy) + area.dhy - (area.offsetBack * area.dhy_norm);
739 local z = area.sz + (0.5*area.dwz) + area.dhz - (area.offsetBack * area.dhz_norm);
740 local distBack = Utils.vector3Length(x-ix, y-iy, z-iz);
741
742 return distFront < distBack;
743end

getCanInteract

Description
Get can interact with silo
Definition
getCanInteract(boolean showInformationOnly)
Arguments
booleanshowInformationOnlyshow information only
Return Values
booleancanInteractcan interact
Code
749function BunkerSilo:getCanInteract(showInformationOnly)
750 if showInformationOnly then
751 if (g_currentMission.controlPlayer and self.playerInRange) then
752 return true;
753 end;
754 if not g_currentMission.controlPlayer then
755 for vehicle in pairs(self.vehiclesInRange) do
756 if vehicle:getIsActiveForInput(false) then
757 return true;
758 end;
759 end;
760 end;
761 else
762 if (g_currentMission.controlPlayer and self.playerInRange) then
763 --if next(self.vehiclesInRange) == nil then
764 return true;
765 --end
766 end;
767 end
768 return false;
769end;

getCanCloseSilo

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

getCanOpenSilo

Description
Get can open silo
Definition
getCanOpenSilo()
Return Values
booleancanOpencan open silo
Code
781function BunkerSilo:getCanOpenSilo()
782 if not (self.state == BunkerSilo.STATE_FERMENTED or self.state == BunkerSilo.STATE_DRAIN) then
783 return false;
784 end
785 local ix,iy,iz = self:getInteractionPosition();
786 if ix ~= nil then
787 local closerToFront = self:getIsCloserToFront(ix,iy,iz);
788 if closerToFront and not self.isOpenedAtFront then
789 return true;
790 end
791 if not closerToFront and not self.isOpenedAtBack then
792 return true;
793 end
794 end
795 return false;
796end

getInteractionPosition

Description
Get interact position
Definition
getInteractionPosition()
Return Values
floatxx world position
floatyy world position
floatzz world position
Code
803function BunkerSilo:getInteractionPosition()
804 if g_currentMission.controlPlayer and self.playerInRange then
805 return getWorldTranslation(g_currentMission.player.rootNode);
806 else
807 if self.vehiclesInRange[g_currentMission.currentVehicle] ~= nil then
808 return getWorldTranslation(self.vehiclesInRange[g_currentMission.currentVehicle].components[1].node);
809 end
810 end
811 return nil;
812end

interactionTriggerCallback

Description
interactionTriggerCallback
Definition
interactionTriggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay, integer otherId)
Arguments
integertriggerIdid of trigger
integerotherIdid of actor
booleanonEnteron enter
booleanonLeaveon leave
booleanonStayon stay
integerotherIdid of other actor
Code
822function BunkerSilo:interactionTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId)
823 if onEnter or onLeave then
824 if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then
825 if onEnter then
826 self.playerInRange = true;
827 g_currentMission:removeActivatableObject(self.activatable); -- make sure it is not added twice
828 g_currentMission:addActivatableObject(self.activatable);
829 else
830 self.playerInRange = false;
831 if self.numVehiclesInRange == 0 then
832 g_currentMission:removeActivatableObject(self.activatable);
833 end;
834 end;
835 else
836 --local vehicle = g_currentMission.nodeToVehicle[otherId];
837 local vehicle = g_currentMission.nodeToVehicle[otherShapeId];
838 if vehicle ~= nil then
839 if onEnter then
840 if self.vehiclesInRange[vehicle] == nil then
841 self.vehiclesInRange[vehicle] = true;
842 self.numVehiclesInRange = self.numVehiclesInRange + 1;
843
844 g_currentMission:removeActivatableObject(self.activatable); -- make sure it is not added twice
845 g_currentMission:addActivatableObject(self.activatable);
846
847 -- add callback if shovel
848 if vehicle.setChangedFillLevelCallback ~= nil then
849 vehicle:setChangedFillLevelCallback(BunkerSilo.onChangedFillLevelCallback, self);
850 end
851 end;
852 else
853 if self.vehiclesInRange[vehicle] then
854 self.vehiclesInRange[vehicle] = nil;
855 self.numVehiclesInRange = self.numVehiclesInRange - 1;
856
857 if self.numVehiclesInRange == 0 and not self.playerInRange then
858 g_currentMission:removeActivatableObject(self.activatable);
859 end;
860
861 -- remove callback if shovel
862 if vehicle.setChangedFillLevelCallback ~= nil then
863 vehicle:setChangedFillLevelCallback(nil);
864 end
865 end;
866 end;
867 end;
868 end;
869 end;
870end;

onChangedFillLevelCallback

Description
Called if fill level changed
Definition
onChangedFillLevelCallback(table shovel, integer fillDelta, integer fillType)
Arguments
tableshovelshovel
integerfillDeltafill delta
integerfillTypefill type
Code
877function BunkerSilo.onChangedFillLevelCallback(self, shovel, fillDelta, fillType)
878 if fillDelta >= 0 then
879 return;
880 end
881
882 local area = self.bunkerSiloArea;
883
884 local x,y,z = getWorldTranslation(shovel.components[1].node);
885 local closerToFront = self:getIsCloserToFront(x,y,z);
886
887 if shovel.pickUp ~= nil and shovel.pickUp.node ~= nil then
888 x,y,z = localToWorld(shovel.pickUp.node, 0, 0, shovel.pickUp.length);
889 end;
890
891 local length = self.openingLength;
892
893 if closerToFront then
894 if self.isOpenedAtFront then
895 local p1 = Utils.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 = Utils.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
916end