Script v1.4.4.0
- Handtools
- Events
- Objects
- AnimatedObject
- Bale
- Basketball
- Bga
- BunkerSilo
- FieldDefinition
- FillablePallet
- HelpIcons
- MountableObject
- Nightlight2
- NightlightFlicker
- PhysicsObject
- Rotator
- SaplingPallet
- ShovelTarget
- SimParticleSystem
- StationCrane
- Storage
- TourIcons
- Train
- VehicleSellingPoint
- Placeables
- Triggers
- Utils
- Vehicles
- Specializations
Engine v7.0.0.2
- General
- Entity
- Node
- Scenegraph
- Lighting
- Camera
- Shape
- Particle System
- Physics
- Spline
- Animation
- Overlays
- Sound
- Input
- XML
- Network
- Callbacks
- Text Rendering
- Terrain Detail
- Tire Track
- Editor
- Rendering
- String
- Math
- I3D
- Fillplanes
Foundation Reference
Train
DescriptionClass for not self driving trainsFunctions
- onCreate
- new
- load
- delete
- readStream
- writeStream
- update
- startTrain
- updateTrainPosition
- onUpdateEvent
- onWoodSellingUpdateEvent
- setTriggersEnabled
- unloadTrain
- getIsActivatable
- onActivateObject
- loadFromAttributesAndNodes
- getSaveAttributesAndNodes
- triggerCallback
- getNextPosition
onCreate
DescriptionCreating trainDefinition
onCreate(integer id)Arguments
integer | id | node id |
17 | function 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; |
26 | end; |
new
DescriptionCreating trainDefinition
new(boolean isServer, boolean isClient, table mt)Arguments
boolean | isServer | is server |
boolean | isClient | is client |
table | mt | custom metatable |
table | instance | Instance of object |
34 | function 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; |
43 | end; |
load
DescriptionLoading trainDefinition
load(integer id)Arguments
integer | id | node id |
boolean | success | success |
49 | function 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; |
245 | end; |
delete
DescriptionDeleting trainDefinition
delete()Code
249 | function 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); |
284 | end; |
readStream
DescriptionCalled on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
290 | function 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; |
302 | end; |
writeStream
DescriptionCalled on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
308 | function 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; |
317 | end; |
update
DescriptionUpdateDefinition
update(float dt)Arguments
float | dt | time since last call in ms |
322 | function 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 | |
363 | end; |
startTrain
DescriptionStart trainDefinition
startTrain(boolean noEventSend)Arguments
boolean | noEventSend | no event send |
368 | function 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; |
380 | end; |
updateTrainPosition
DescriptionUpdate train positionDefinition
updateTrainPosition(float t)Arguments
float | t | new time |
385 | function 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; |
455 | end; |
onUpdateEvent
DescriptionOn update eventDefinition
onUpdateEvent(table trigger, float fillDelta, integer fillType, table trailer)Arguments
table | trigger | trigger |
float | fillDelta | fill delta |
integer | fillType | fill type |
table | trailer | trailer |
463 | function 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; |
470 | end; |
onWoodSellingUpdateEvent
DescriptionOn wood selling update eventDefinition
onWoodSellingUpdateEvent(table trigger, float sellValue)Arguments
table | trigger | trigger |
float | sellValue | sell value |
476 | function Train:onWoodSellingUpdateEvent(trigger, sellValue) |
477 | self.woodFillLevel = self.woodFillLevel + sellValue; |
478 | end; |
setTriggersEnabled
DescriptionSet triggers enabledDefinition
setTriggersEnabled(boolean isEnabled)Arguments
boolean | isEnabled | is enabled |
483 | function 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; |
493 | end; |
unloadTrain
DescriptionUnload trainDefinition
unloadTrain()Code
497 | function 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 | |
533 | end; |
getIsActivatable
DescriptionGet is activateableDefinition
getIsActivatable()Return Values
boolean | isActivateable | is activateable |
538 | function Train:getIsActivatable() |
539 | return self.isEnabled and g_currentMission.controlPlayer and self.currentTime == self.startOffsetTime and not self.isTrainStarted; |
540 | end; |
onActivateObject
DescriptionOn activate objectDefinition
onActivateObject()Code
548 | function Train:onActivateObject() |
549 | self.objectActivated = true; |
550 | g_currentMission:addActivatableObject(self); |
551 | self:startTrain(); |
552 | end; |
loadFromAttributesAndNodes
DescriptionLoading from attributes and nodesDefinition
loadFromAttributesAndNodes(integer xmlFile, string key)Arguments
integer | xmlFile | id of xml object |
string | key | key |
boolean | success | success |
559 | function 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; |
603 | end; |
getSaveAttributesAndNodes
DescriptionGet save attributes and nodesDefinition
getSaveAttributesAndNodes(string nodeIdent)Arguments
string | nodeIdent | node ident |
string | attributes | attributes |
string | nodes | nodes |
610 | function 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; |
642 | end; |
triggerCallback
DescriptionTrigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
integer | triggerId | id of trigger |
integer | otherId | id of actor |
boolean | onEnter | on enter |
boolean | onLeave | on leave |
boolean | onStay | on stay |
651 | function 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; |
669 | end; |
getNextPosition
DescriptionGet next train positionDefinition
getNextPosition(table self, float startTime, float length)Arguments
table | self | self |
float | startTime | start time |
float | length | length |
float | t | new time |
float | posX | new x position |
float | posY | new y position |
float | posZ | new z position |
680 | function 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; |
693 | end; |