LUADOC - Farming Simulator 17

Printable Version

Train

Description
Class for not self driving trains
Functions

onCreate

Description
Creating train
Definition
onCreate(integer id)
Arguments
integeridnode id
Code
17function Train:onCreate(id)
18 local train = Train:new(g_server ~= nil, g_client ~= nil);
19 if train:load(id) then
20 g_currentMission:addOnCreateLoadedObject(train);
21 g_currentMission:addOnCreateLoadedObjectToSave(train);
22 train:register(true);
23 else
24 train:delete();
25 end;
26end;

new

Description
Creating train
Definition
new(boolean isServer, boolean isClient, table mt)
Arguments
booleanisServeris server
booleanisClientis client
tablemtcustom metatable
Return Values
tableinstanceInstance of object
Code
34function Train:new(isServer, isClient, customMt)
35 local mt = customMt;
36 if mt == nil then
37 mt = Train_mt;
38 end;
39
40 local self = Object:new(isServer, isClient, mt);
41
42 return self;
43end;

load

Description
Loading train
Definition
load(integer id)
Arguments
integeridnode id
Return Values
booleansuccesssuccess
Code
49function Train:load(id)
50 self.nodeId = id;
51 self.splineId = id;
52 local xmlFileName = getUserAttribute(id, "xmlFilename");
53 if xmlFileName == nil then
54 print("Warning: No Train 'xmlFilename' specified!");
55 return nil;
56 end;
57
58 xmlFileName = Utils.getFilename(xmlFileName, g_currentMission.loadingMapBaseDirectory);
59 local xmlFile = loadXMLFile("TrainConfigFile", xmlFileName);
60 if xmlFile == nil then
61 print("Warning: Could not open Train config file!");
62 return nil;
63 end;
64
65 local trainType = Utils.getNoNil(getUserAttribute(id, "trainType"), "grainTrain");
66 local i=0;
67 local trainTypeKey = nil;
68 while true do
69 local key = string.format("trainSystem.train(%d)", i);
70 if (not hasXMLProperty(xmlFile, key)) then
71 break;
72 end;
73
74 local currentTrainType = getXMLString(xmlFile, key .. "#type");
75 if currentTrainType == trainType then
76 trainTypeKey = key;
77 break;
78 end;
79 i = i + 1;
80 end;
81
82 if trainTypeKey == nil then
83 print("Warning: Traintype '"..trainType.."' not found in '"..xmlFileName.."'!");
84 return nil;
85 end;
86
87 self.isTrainStarted = false;
88
89 self.fruitToVehicle = {};
90 self.trainLength = 0;
91 self.train = {};
92 self.trainTransform = createTransformGroup("trainRoot");
93
94 self.isCurved = Utils.getNoNil(getUserAttribute(id, "isCurved"), false);
95
96 link(getRootNode(), self.trainTransform);
97 local i = 0;
98 while true do
99 local key = string.format(trainTypeKey..".asset(%d)", i);
100 if (not hasXMLProperty(xmlFile, key)) then
101 break;
102 end;
103 local filename = getXMLString(xmlFile, key .. "#filename");
104
105 local root = Utils.loadSharedI3DFile(filename, g_currentMission.loadingMapBaseDirectory, false, true, false);
106 local vehicle = {};
107 vehicle.filename = filename;
108 vehicle.id = getChildAt(root, 0);
109 link(self.trainTransform, vehicle.id);
110 delete(root);
111 vehicle.dir = Utils.getNoNil(getXMLInt(xmlFile, key.."#direction"), 1);
112 vehicle.length = Utils.getNoNil(getXMLFloat(xmlFile, key.."#length"), 2);
113 vehicle.wheels = {};
114 vehicle.radius = getXMLFloat(xmlFile, key.."#wheelRadius");
115 local numWheels = Utils.getNoNil(getXMLInt(xmlFile, key.."#numWheels"), 0);
116 for i=0, numWheels-1 do
117 if vehicle.radius == nil then
118 local _,y,_ = getTranslation(getChildAt(vehicle.id, i));
119 vehicle.radius = y;
120 end;
121 table.insert(vehicle.wheels, getChildAt(vehicle.id, i));
122 end;
123
124 if self.isCurved then
125 local _, _, wheel1PosZ = getTranslation(vehicle.wheels[1]);
126 vehicle.wheel1PosZ = wheel1PosZ;
127 local _, _, wheel4Z = getTranslation(vehicle.wheels[numWheels]);
128 vehicle.wheelToWheelLength = math.abs(vehicle.wheel1PosZ-wheel4Z);
129 vehicle.wheel1Offset = math.abs(vehicle.length*0.5 - vehicle.wheel1PosZ);
130 vehicle.wheel4Offset = math.abs(wheel4Z + vehicle.length*0.5);
131 end;
132
133 -- wood sell trigger directly on the train
134 local trainWoodSellTriggerIndex = getXMLString(xmlFile, key.."#woodSellTriggerIndex");
135 if trainWoodSellTriggerIndex ~= nil then
136 local trainWoodSellTriggerNode = Utils.indexToObject(vehicle.id, trainWoodSellTriggerIndex);
137 self.trainWoodSellTrigger = WoodSellTrigger:new(trainWoodSellTriggerNode);
138 g_currentMission:addNonUpdateable(self.trainWoodSellTrigger);
139 self.trainWoodSellTrigger:addUpdateEventListener(self);
140 end;
141
142 vehicle.fillLevels = {};
143 local fillTypes = getXMLString(xmlFile, key.."#fillTypes");
144 if fillTypes ~= nil then
145 local types = Utils.splitString(" ", fillTypes);
146 for _,fillTypeName in pairs(types) do
147 local desc = FillUtil.fillTypeNameToDesc[fillTypeName];
148 if desc ~= nil then
149 self.fruitToVehicle[desc.index] = vehicle;
150 vehicle.fillLevels[desc.index] = 0;
151 end;
152 end;
153 end;
154
155 self.woodFillLevel = 0; -- for lumber trains which don't have a tip trigger
156
157 setTranslation(vehicle.id, 0, 0, self.trainLength + (vehicle.length / 2));
158 if vehicle.dir == -1 then
159 setRotation(vehicle.id, 0, math.pi, 0);
160 end;
161 self.trainLength = self.trainLength + vehicle.length;
162 table.insert(self.train, vehicle);
163
164 i = i + 1;
165 end;
166
167 -- attach sound to the last train wagon
168 local soundAttacherNode = self.trainTransform;
169 if getNumOfChildren(self.trainTransform) > 0 then
170 soundAttacherNode = getChildAt(self.trainTransform, getNumOfChildren(self.trainTransform) - 1);
171 end;
172
173 self.trainSound = SoundUtil.loadSampleFromFilename({}, "data/maps/sounds/train.wav", "", soundAttacherNode, 1, 160, 10);
174
175 self.maxSpeed = Utils.getNoNil(getXMLFloat(xmlFile, trainTypeKey .. "#maxSpeed"), 20) / 3600;
176 self.maxAcceleration = Utils.getNoNil(getXMLFloat(xmlFile, trainTypeKey .. "#maxAcc"), 5) * 1000;
177
178 self.endTime = 0;
179 self.endTimeOffset = Utils.getNoNil(getUserAttribute(id, "unloadingWaitTime"), 10) * 1000;
180 self.lastSpeed = 0;
181 self.startTimeOffset = 2000;
182 self.startTime = 0;
183
184 self.doBrake = false;
185 self.direction = 1;
186
187 self.currentTime = 0;
188
189 self.splineLength = getSplineLength(self.splineId);
190 self.startOffsetTime = self.trainLength/self.splineLength;
191 self.brakeTime = self.startOffsetTime / table.getn(self.train);
192 self:updateTrainPosition(self.startOffsetTime);
193
194 local tipTriggerIndex = getUserAttribute(id, "tipTriggerIndex");
195 if tipTriggerIndex ~= nil then
196 local tipTriggerNode = Utils.indexToObject(id, tipTriggerIndex);
197 self.tipTrigger = TipTrigger:new(self.isServer, self.isClient);
198 self.tipTrigger:load(tipTriggerNode);
199 g_currentMission:addOnCreateLoadedObject(self.tipTrigger);
200 self.tipTrigger:register(true);
201 self.tipTrigger:addUpdateEventListener(self);
202 self.tipTrigger.handleFilling = false
203 end;
204
205 -- wood sell trigger on the ground
206 local woodSellTriggerIndex = getUserAttribute(id, "woodSellTriggerIndex");
207 if woodSellTriggerIndex ~= nil then
208 local woodSellTriggerNode = Utils.indexToObject(id, woodSellTriggerIndex);
209 self.woodSellTrigger = WoodSellTrigger:new(woodSellTriggerNode);
210 g_currentMission:addNonUpdateable(self.woodSellTrigger);
211 self.woodSellTrigger:addUpdateEventListener(self);
212 end;
213
214 local triggerIndex = getUserAttribute(id, "triggerIndex");
215 if triggerIndex ~= nil then
216 self.triggerId = Utils.indexToObject(id, triggerIndex);
217 if g_currentMission:getIsClient() then
218 addTrigger(self.triggerId, "triggerCallback", self);
219 end;
220 self.leverId = getChildAt(getChildAt(self.triggerId, 0), 0);
221
222 self.leverSound = SoundUtil.loadSampleFromFilename({}, "data/maps/sounds/lever.wav", "", self.leverId, 1, 30, 3);
223
224 self.activateText = g_i18n:getText("action_sendOffTrain");
225 self.isEnabled = true;
226 self.objectActivated = false;
227 end;
228 self.leverStarted = false;
229 self.leverTimer = 0;
230
231 self.leverRotCurve = AnimCurve:new(linearInterpolator1);
232 self.leverRotCurve:addKeyframe({v=Utils.degToRad(0), time = 0});
233 self.leverRotCurve:addKeyframe({v=Utils.degToRad(12), time = 250});
234 self.leverRotCurve:addKeyframe({v=Utils.degToRad(20), time = 500});
235 self.leverRotCurve:addKeyframe({v=Utils.degToRad(26), time = 750});
236 self.leverRotCurve:addKeyframe({v=Utils.degToRad(30), time = 1000});
237 self.leverRotCurve:addKeyframe({v=Utils.degToRad(10), time = 1500});
238 self.leverRotCurve:addKeyframe({v=Utils.degToRad(0), time = 2000});
239
240 self.saveId = Utils.getNoNil(getXMLString(xmlFile, trainTypeKey .. "#saveId"), "Train_"..trainType.."_"..getName(id));
241
242 g_currentMission:addNodeObject(self.nodeId, self);
243
244 return true;
245end;

delete

Description
Deleting train
Definition
delete()
Code
249function Train:delete()
250 g_currentMission:removeOnCreateLoadedObjectToSave(self);
251
252 if g_currentMission:getIsClient() then
253 for _, vehicle in pairs(self.train) do
254 ParticleUtil.deleteParticleSystems(vehicle.exhaustParticleSystems)
255 Utils.releaseSharedI3DFile(vehicle.filename, g_currentMission.loadingMapBaseDirectory, true);
256 end;
257 if self.triggerId ~= nil then
258 removeTrigger(self.triggerId);
259 end;
260 if self.trainWoodSellTrigger ~= nil then
261 self.trainWoodSellTrigger:delete();
262 end;
263 end;
264
265 if self.leverSound ~= nil then
266 SoundUtil.deleteSample(self.leverSound);
267 self.leverSound = nil;
268 end;
269
270 if self.trainSound ~= nil then
271 SoundUtil.deleteSample(self.trainSound);
272 self.trainSound = nil;
273 end;
274
275 if self.trainTransform ~= nil then
276 delete(self.trainTransform);
277 end
278
279 if self.nodeId ~= 0 then
280 g_currentMission:removeNodeObject(self.nodeId);
281 end;
282 g_currentMission:removeActivatableObject(self);
283 Train:superClass().delete(self);
284end;

readStream

Description
Called on client side on join
Definition
readStream(integer streamId, table connection)
Arguments
integerstreamIdstream ID
tableconnectionconnection
Code
290function Train:readStream(streamId, connection)
291 Train:superClass().readStream(self, streamId, connection);
292 if connection:getIsServer() then
293 local isTrainStarted = streamReadBool(streamId);
294 if isTrainStarted then
295 self:startTrain(true);
296 self.lastSpeed = Utils.getNoNil(streamReadFloat32(streamId), 0);
297 self.currentTime = Utils.getNoNil(streamReadFloat32(streamId), self.currentTime);
298 self:updateTrainPosition(self.currentTime);
299 self.startTime = self.startTimeOffset + 1; -- make sure that train is driving
300 end;
301 end;
302end;

writeStream

Description
Called on server side on join
Definition
writeStream(integer streamId, table connection)
Arguments
integerstreamIdstream ID
tableconnectionconnection
Code
308function Train:writeStream(streamId, connection)
309 Train:superClass().writeStream(self, streamId, connection);
310 if not connection:getIsServer() then
311 streamWriteBool(streamId, self.isTrainStarted)
312 if self.isTrainStarted then
313 streamWriteFloat32(streamId, self.lastSpeed);
314 streamWriteFloat32(streamId, self.currentTime);
315 end;
316 end;
317end;

update

Description
Update
Definition
update(float dt)
Arguments
floatdttime since last call in ms
Code
322function Train:update(dt)
323 if g_currentMission ~= nil and (self.startTime > self.startTimeOffset or self.endTime > self.endTimeOffset) then
324 -- handle braking and acceleration
325 if not self.doBrake then
326 self.lastSpeed = math.min(math.max(self.lastSpeed - dt/self.maxAcceleration, -1), 1);
327 else
328 if self.direction == 1 then
329 self.lastSpeed = self.startBrakeSpeed * math.max(0.1, (((1 - self.currentTime)/self.brakeTime)*0.8));
330 else
331 self.lastSpeed = self.startBrakeSpeed * math.max(0.1, (((self.currentTime-self.startOffsetTime)/self.brakeTime)*0.8));
332 end;
333 end;
334
335 -- update train
336 local lastTime = Utils.clamp(self.currentTime + (self.maxSpeed/self.splineLength)*-self.lastSpeed*self.direction*dt, self.startOffsetTime, 1);
337 if lastTime <= 1 then
338 if lastTime ~= self.currentTime then
339 self:updateTrainPosition(lastTime);
340 end;
341 end;
342 else
343 if self.isTrainStarted then
344 if self.direction == 1 then
345 self.startTime = self.startTime + dt;
346 else
347 self.endTime = self.endTime + dt;
348 end;
349 end;
350 end;
351
352 if g_currentMission ~= nil and self.leverStarted and self.leverId ~= nil then
353 local rx = self.leverRotCurve:get(self.leverTimer);
354 setRotation(self.leverId, rx, 0, 0);
355 self.leverTimer = self.leverTimer + dt;
356 if self.leverTimer > 2000 then
357 self.leverTimer = 0;
358 self.leverStarted = false;
359 SoundUtil.stop3DSample(self.leverSound);
360 end;
361 end;
362
363end;

startTrain

Description
Start train
Definition
startTrain(boolean noEventSend)
Arguments
booleannoEventSendno event send
Code
368function Train:startTrain(noEventSend)
369 if not self.isTrainStarted then
370 TrainStartEvent.sendEvent(self, noEventSend);
371 self.startTime = 0;
372 self.doBrake = false;
373 self.isTrainStarted = true;
374 self.leverStarted = true;
375 SoundUtil.play3DSample(self.leverSound);
376 SoundUtil.play3DSample(self.trainSound);
377 self.leverTimer = 0;
378 self:setTriggersEnabled(false);
379 end;
380end;

updateTrainPosition

Description
Update train position
Definition
updateTrainPosition(float t)
Arguments
floattnew time
Code
385function Train:updateTrainPosition(t)
386 local dt = self.currentTime - t;
387 local x, y, z = getSplinePosition(self.splineId, t);
388 local rx, ry, rz = getSplineOrientation(self.splineId, t, 0, -1, 0);
389 setTranslation(self.trainTransform, x, y, z);
390 setRotation(self.trainTransform, rx, ry, rz);
391
392 local distance = dt * self.splineLength;
393 for _, vehicle in pairs(self.train) do
394 for _, wheel in pairs(vehicle.wheels) do
395 rotate(wheel, (1/vehicle.radius) * distance * vehicle.dir, 0, 0);
396 end;
397 end;
398 self.currentTime = t;
399
400 if self.direction == 1 then
401 if self.currentTime >= (1-self.brakeTime) then
402 if not self.doBrake then
403 self.startBrakeSpeed = self.lastSpeed;
404 end;
405 self.doBrake = true;
406 end;
407 if self.currentTime == 1 then
408 self.doBrake = false;
409 self.endTime = 0;
410 self.startTime = 0;
411 self.direction = self.direction * -1;
412 self:unloadTrain();
413 SoundUtil.stop3DSample(self.trainSound);
414 end;
415 else
416 if self.currentTime <= (self.startOffsetTime+self.brakeTime) then
417 if not self.doBrake then
418 self.startBrakeSpeed = self.lastSpeed;
419 end;
420 self.doBrake = true;
421 end;
422 if self.currentTime == self.startOffsetTime then
423 if self.isTrainStarted then
424 self.doBrake = false;
425 self.endTime = 0;
426 self.startTime = 0;
427 self.direction = self.direction * -1;
428 SoundUtil.stop3DSample(self.trainSound);
429 end;
430 self.isTrainStarted = false;
431 self:setTriggersEnabled(true);
432 end;
433 end;
434 if self.isCurved then
435
436 local currentTime = t;
437 local startPosX, startPosY, startPosZ = getSplinePosition(self.splineId, currentTime);
438 local offset = nil;
439
440 for _, vehicle in pairs(self.train) do
441 if offset ~= nil then
442 currentTime, startPosX, startPosY, startPosZ = Train.getNextPosition(self, currentTime, vehicle.wheel1Offset+offset);
443 end;
444 local endTime, endPosX, endPosY, endPosZ = Train.getNextPosition(self, currentTime, vehicle.wheelToWheelLength);
445 local dirX, dirY, dirZ = endPosX-startPosX, endPosY-startPosY, endPosZ-startPosZ;
446 Utils.setWorldDirection(vehicle.id, dirX, dirY, dirZ, 0, 1, 0);
447 local length = Utils.vector3Length(dirX, dirY, dirZ);
448 dirX, dirY, dirZ = dirX /length, dirY / length, dirZ / length;
449 startPosX,startPosY,startPosXZ = startPosX+dirX*vehicle.wheel1PosZ, startPosY+dirY*vehicle.wheel1PosZ, startPosZ+dirZ*vehicle.wheel1PosZ;
450 setTranslation(vehicle.id, worldToLocal(getParent(vehicle.id), startPosX, startPosY, startPosXZ));
451 offset = vehicle.wheel4Offset;
452 currentTime = endTime;
453 end;
454 end;
455end;

onUpdateEvent

Description
On update event
Definition
onUpdateEvent(table trigger, float fillDelta, integer fillType, table trailer)
Arguments
tabletriggertrigger
floatfillDeltafill delta
integerfillTypefill type
tabletrailertrailer
Code
463function Train:onUpdateEvent(trigger, fillDelta, fillType, trailer)
464 local vehicle = self.fruitToVehicle[fillType];
465 if vehicle ~= nil then
466 if vehicle.fillLevels[fillType] ~= nil then
467 vehicle.fillLevels[fillType] = vehicle.fillLevels[fillType] + fillDelta;
468 end;
469 end;
470end;

onWoodSellingUpdateEvent

Description
On wood selling update event
Definition
onWoodSellingUpdateEvent(table trigger, float sellValue)
Arguments
tabletriggertrigger
floatsellValuesell value
Code
476function Train:onWoodSellingUpdateEvent(trigger, sellValue)
477 self.woodFillLevel = self.woodFillLevel + sellValue;
478end;

setTriggersEnabled

Description
Set triggers enabled
Definition
setTriggersEnabled(boolean isEnabled)
Arguments
booleanisEnabledis enabled
Code
483function Train:setTriggersEnabled(isEnabled)
484 if self.tipTrigger ~= nil then
485 self.tipTrigger.isEnabled = isEnabled;
486 end;
487 if self.woodSellTrigger ~= nil then
488 self.woodSellTrigger.isEnabled = isEnabled;
489 end;
490 if self.trainWoodSellTrigger ~= nil then
491 self.trainWoodSellTrigger.isEnabled = isEnabled;
492 end;
493end;

unloadTrain

Description
Unload train
Definition
unloadTrain()
Code
497function Train:unloadTrain()
498
499 local soldSomething = false;
500
501 for _, vehicle in pairs(self.train) do
502 if vehicle.fillLevels ~= nil then
503 for fillType, fillLevel in pairs(vehicle.fillLevels) do
504 -- update total amount of this fill type
505 local desc = FillUtil.fillTypeIndexToDesc[fillType];
506 desc.totalAmount = desc.totalAmount + fillLevel;
507 local money = 0
508 if self.tipTrigger ~= nil then
509 money = self.tipTrigger:sellFillType(fillLevel, fillType)
510 end
511
512 vehicle.fillLevels[fillType] = 0;
513
514 if money > 0 then soldSomething = true; end;
515
516 end;
517 end;
518 end;
519
520 if self.woodFillLevel > 0 then
521 if self.isServer then
522 g_currentMission:addSharedMoney(self.woodFillLevel, "soldWood");
523 g_currentMission:addMoneyChange(self.woodFillLevel, FSBaseMission.MONEY_TYPE_SINGLE, true, g_i18n:getText("finance_soldWood"));
524 end;
525 self.woodFillLevel = 0;
526 soldSomething = true;
527 end;
528
529 if soldSomething then
530 playSample(g_currentMission.cashRegistrySound, 1, 1, 0);
531 end;
532
533end;

getIsActivatable

Description
Get is activateable
Definition
getIsActivatable()
Return Values
booleanisActivateableis activateable
Code
538function Train:getIsActivatable()
539 return self.isEnabled and g_currentMission.controlPlayer and self.currentTime == self.startOffsetTime and not self.isTrainStarted;
540end;

onActivateObject

Description
On activate object
Definition
onActivateObject()
Code
548function Train:onActivateObject()
549 self.objectActivated = true;
550 g_currentMission:addActivatableObject(self);
551 self:startTrain();
552end;

loadFromAttributesAndNodes

Description
Loading from attributes and nodes
Definition
loadFromAttributesAndNodes(integer xmlFile, string key)
Arguments
integerxmlFileid of xml object
stringkeykey
Return Values
booleansuccesssuccess
Code
559function Train:loadFromAttributesAndNodes(xmlFile, key)
560 local isTrainStarted = Utils.getNoNil(getXMLBool(xmlFile, key .."#isTrainStarted"), false);
561 if isTrainStarted then
562 self:startTrain(true);
563 self.lastSpeed = Utils.getNoNil(getXMLFloat(xmlFile, key.."#lastSpeed"), 0);
564 self.currentTime = Utils.getNoNil(getXMLFloat(xmlFile, key.."#currentTime"), self.currentTime);
565 self.direction = Utils.getNoNil(getXMLInt(xmlFile, key.."#direction"), self.direction);
566 self:updateTrainPosition(self.currentTime);
567 self.startTime = self.startTimeOffset + 1; -- make sure that train is driving
568 end;
569
570 for i, vehicle in pairs(self.train) do
571 if vehicle.fillLevels ~= nil then
572 local vehicleKey = key..string.format(".vehicle%d", i);
573 local fillLevels = getXMLString(xmlFile, vehicleKey .. "#fillLevels");
574 if fillLevels ~= nil then
575 local levels = Utils.splitString(" ", fillLevels);
576 local fillTypesStr = getXMLString(xmlFile, vehicleKey .. "#fillTypes");
577 if fillTypesStr ~= nil then
578 local fillTypes = Utils.splitString(" ", fillTypesStr);
579 local numFillLevels = math.min(#levels, #fillTypes)
580 for i=1, numFillLevels do
581 local fillType = FillUtil.fillTypeNameToInt[fillTypes[i]];
582 if fillType ~= nil then
583 vehicle.fillLevels[fillType] = levels[i];
584 end
585 end
586 else
587 -- Old savegame loading (not deterministic with pairs order and has issues if train xml is changed)
588 local i = 1;
589 for fillType, level in pairs(vehicle.fillLevels) do
590 if levels[i] ~= nil then
591 vehicle.fillLevels[fillType] = levels[i];
592 end
593 i = i + 1;
594 end;
595 end
596 end;
597 end;
598 end;
599
600 self.woodFillLevel = Utils.getNoNil(getXMLFloat(xmlFile, key .. ".wood#fillLevel"), 0);
601
602 return true;
603end;

getSaveAttributesAndNodes

Description
Get save attributes and nodes
Definition
getSaveAttributesAndNodes(string nodeIdent)
Arguments
stringnodeIdentnode ident
Return Values
stringattributesattributes
stringnodesnodes
Code
610function Train:getSaveAttributesAndNodes(nodeIdent)
611 local attributes = 'isTrainStarted="'..tostring(self.isTrainStarted)..'" currentTime="'..self.currentTime..'" direction="'..self.direction..'" lastSpeed="'..self.lastSpeed..'"';
612 local nodes = "";
613 for i, vehicle in pairs(self.train) do
614 if i>1 then
615 nodes = nodes.."\n";
616 end;
617 local fillLevels = "";
618 local fillTypes = "";
619 if vehicle.fillLevels ~= nil then
620 local j = 0;
621 for fillType, level in pairs(vehicle.fillLevels) do
622 local fillTypeName = FillUtil.fillTypeIntToName[fillType];
623 if fillTypeName ~= nil then
624 if j > 0 then
625 fillLevels = fillLevels .. " ";
626 fillTypes = fillTypes .. " ";
627 end
628 fillLevels = fillLevels .. level;
629 fillTypes = fillTypes .. fillTypeName;
630 j = j + 1;
631 end
632 end
633 fillLevels = ' fillLevels="'..fillLevels..'"';
634 fillTypes = ' fillTypes="'..fillTypes..'"';
635 end
636 nodes = nodes.. nodeIdent..'<vehicle'..i..fillLevels..fillTypes..' />';
637 end
638
639 nodes = nodes .. '\n' .. nodeIdent .. '<wood fillLevel="' .. self.woodFillLevel .. '" />';
640
641 return attributes, nodes;
642end;

triggerCallback

Description
Trigger callback
Definition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)
Arguments
integertriggerIdid of trigger
integerotherIdid of actor
booleanonEnteron enter
booleanonLeaveon leave
booleanonStayon stay
Code
651function Train:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay)
652 if self.isEnabled then
653 if onEnter or onLeave then
654 if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then
655 if onEnter then
656 if not self.objectActivated then
657 g_currentMission:addActivatableObject(self);
658 self.objectActivated = true;
659 end
660 else
661 if self.objectActivated then
662 g_currentMission:removeActivatableObject(self);
663 self.objectActivated = false;
664 end
665 end;
666 end;
667 end;
668 end;
669end;

getNextPosition

Description
Get next train position
Definition
getNextPosition(table self, float startTime, float length)
Arguments
tableselfself
floatstartTimestart time
floatlengthlength
Return Values
floattnew time
floatposXnew x position
floatposYnew y position
floatposZnew z position
Code
680function Train.getNextPosition(self, startTime, length)
681 local x, y, z = getSplinePosition(self.splineId, startTime);
682 local posX, posY, posZ = x,y,z;
683 local t = startTime - (length*0.75)/self.splineLength;
684 local l = Utils.vector3Length(x-posX, y-posY, z-posZ);
685
686 while l < length do
687 t = t - 0.2 / self.splineLength;
688 posX, posY, posZ = getSplinePosition(self.splineId, t);
689 l = Utils.vector3Length(x-posX, y-posY, z-posZ);
690 end;
691
692 return t, posX, posY, posZ;
693end;