Script v1_7_1_0
- AI
- Animals
- Collections
- Contracts
- Debug
- Economy
- Elements
- EnvironmentalScore
- Errors
- Events
- GUI
- Handtools
- Hud
- I3d
- Input
- Jobs
- Maps
- Materials
- Misc
- Objects
- Parameters
- Placeables
- Placement
- Player
- Shop
- Sounds
- Specialization
- Specializations
- AIConveyorBelt
- AIDrivable
- AIFieldWorker
- AIImplement
- AIJobVehicle
- AIVehicle
- AIVehicleObstacle
- AnimatedVehicle
- ArticulatedAxis
- Attachable
- AttacherJointControl
- AttacherJoints
- AutoLoader
- BaleGrab
- BaleLoader
- Baler
- BaleWrapper
- BaseMaterial
- BigBag
- BunkerSiloCompacter
- BunkerSiloInteractor
- CCTDrivable
- Combine
- ConnectionHoses
- ConveyorBelt
- Cover
- CrabSteering
- Crawlers
- CropSensor
- Cultivator
- Cutter
- Cylindered
- CylinderedFoldable
- Dashboard
- Dischargeable
- Drivable
- DynamicallyLoadedParts
- DynamicMountAttacher
- Enterable
- ExtendedAIVehicle
- ExtendedCombine
- ExtendedMotorized
- ExtendedMower
- ExtendedSowingMachine
- ExtendedSprayer
- ExtendedWearable
- FertilizingCultivator
- FertilizingSowingMachine
- FillTriggerVehicle
- FillUnit
- FillVolume
- Foldable
- FoliageBending
- ForageWagon
- FrontloaderAttacher
- FruitPreparer
- GroundAdjustedNodes
- GroundReference
- HeadlandAnimation
- Honk
- HookLiftContainer
- HookLiftTrailer
- IKChains
- InlineWrapper
- JigglingParts
- Leveler
- LicensePlates
- Lights
- LivestockTrailer
- Locomotive
- LogGrab
- ManureBarrel
- ManureSensor
- MixerWagon
- Motorized
- Mountable
- Mower
- Mulcher
- MultipleItemPurchase
- Pallet
- Pickup
- Pipe
- PlaceableAI
- PlaceableAnimatedObjects
- PlaceableBeehive
- PlaceableBeehivePalletSpa...
- PlaceableBunkerSilo
- PlaceableBuyingStation
- PlaceableCartridgePlayer
- PlaceableChargingStation
- PlaceableClearAreas
- PlaceableColorable
- PlaceableDeletedNodes
- PlaceableDoghouse
- PlaceableDynamicallyLoade...
- PlaceableFarmhouse
- PlaceableFence
- PlaceableFoliageAreas
- PlaceableGreenhouse
- PlaceableHighPressureWash...
- PlaceableHotspots
- PlaceableHusbandry
- PlaceableHusbandryAnimals
- PlaceableHusbandryFeeding...
- PlaceableHusbandryFence
- PlaceableHusbandryFood
- PlaceableHusbandryLiquidM...
- PlaceableHusbandryMilk
- PlaceableHusbandryPallets
- PlaceableHusbandryStraw
- PlaceableHusbandryWater
- PlaceableIncomePerHour
- PlaceableIndoorAreas
- PlaceableInfoTrigger
- PlaceableLeveling
- PlaceableLights
- PlaceableManureHeap
- PlaceablePlacement
- PlaceableProductionPoint
- PlaceableSellingStation
- PlaceableSilo
- PlaceableSiloExtension
- PlaceableSolarPanels
- PlaceableTipOcclusionArea...
- PlaceableTrainSystem
- PlaceableTriggerMarkers
- PlaceableVine
- PlaceableWardrobe
- PlaceableWeatherStation
- PlaceableWeighingStation
- PlaceableWindTurbine
- PlaceableWorkshop
- Plow
- PlowPacker
- PowerConsumer
- PowerTakeOffs
- PrecisionFarmingStatistic
- PushHandTool
- RandomlyMovingParts
- ReceivingHopper
- ReverseDriving
- Rideable
- RidgeMarker
- Roller
- Ropes
- RTKStation
- SaltSpreader
- SemiTrailerFront
- Shovel
- SlopeCompensation
- SmartAttach
- SoilSampler
- SowingMachine
- SpeedRotatingParts
- SplineVehicle
- Sprayer
- StonePicker
- StrawBlower
- StumpCutter
- SupportVehicle
- Suspensions
- Tedder
- TensionBeltObject
- TensionBelts
- TestAreas
- TipOccluder
- Trailer
- TreePlanter
- TreeSaplingPallet
- TreeSaw
- TurnOnVehicle
- VariableWorkWidth
- VehicleSettings
- VineCutter
- VineDetector
- VinePrepruner
- Washable
- WaterTrailer
- Wearable
- Weeder
- WeedSpotSpray
- Wheels
- WindBending
- Windrower
- Wipers
- WoodCrusher
- WoodHarvester
- WorkArea
- WorkMode
- WorkParticles
- StateMachine
- Statistics
- Tasks
- Triggers
- Utils
- Vehicles
Engine v1_7_1_0
- AI
- Animation
- Camera
- Entity
- Fillplanes
- general
- General
- I3D
- Input
- Lighting
- Math
- Network
- Node
- NoteNode
- Overlays
- Particle System
- Physics
- Rendering
- Scenegraph
- Shape
- Sound
- Spline
- String
- Terrain Detail
- Text Rendering
- Tire Track
- VoiceChat
- XML
Foundation Reference
Locomotive
DescriptionSpecialization for locomotives (railroad vehicle)Functions
- alignToSplineTime
- getAreSurfaceSoundsActive
- getBrakeAcceleration
- getCanBeReset
- getDistanceToRequestedPosition
- getDownhillForce
- getFullName
- getIsEnterable
- getIsMapHotspotVisible
- getIsMotorStarted
- getIsReadyForAutomatedTrainTravel
- getTraveledDistanceStatsActive
- initSpecialization
- notifyPlayerFarmChanged
- onDelete
- onEnterVehicle
- onLeaveVehicle
- onLoad
- onReadStream
- onUpdate
- onWriteStream
- prerequisitesPresent
- registerEventListeners
- registerEvents
- registerFunctions
- registerOverwrittenFunctions
- setLocomotiveState
- setRequestedSplinePosition
- setTrainSystem
- startAutomatedTrainTravel
- updateVehiclePhysics
alignToSplineTime
DescriptionDefinitionalignToSplineTime()Code
567 | function Locomotive:alignToSplineTime(superFunc, spline, yOffset, tFront) |
568 | local retValue = superFunc(self, spline, yOffset, tFront) |
569 | |
570 | if retValue ~= nil then |
571 | local spec = self.spec_locomotive |
572 | if spec.powerArm ~= nil and spec.electricitySpline ~= nil then |
573 | retValue = SplineUtil.getValidSplineTime(retValue) |
574 | local _ |
575 | local x, y, z = getWorldTranslation(spec.powerArm) |
576 | x, y, z, _ = getLocalClosestSplinePosition(spec.electricitySpline, retValue, spec.electricitySplineSearchTime, x, y, z, 0.01) |
577 | |
578 | _, y, _ = worldToLocal(getParent(spec.powerArm), x, y, z) |
579 | x, _, z = getTranslation(spec.powerArm) |
580 | setTranslation(spec.powerArm, x, y, z) |
581 | if spec.powerArm ~= nil then |
582 | self:setMovingToolDirty(spec.powerArm) |
583 | end |
584 | |
585 | -- local x, y, z = getSplinePosition(spec.electricitySpline, newTime) |
586 | -- local dx, dy, dz = getSplineDirection(spec.electricitySpline, newTime) |
587 | -- log(string.format("%.4f %.4f %.4f %.4f", retValue, electricityTime, dif, newTime)) |
588 | -- DebugUtil.drawDebugGizmoAtWorldPos(x, y, z, dx, dy, dz, 0, 1, 0, "E "..tFront.." " ..electricityTime.." " ..newTime) |
589 | |
590 | end |
591 | end |
592 | |
593 | if not self.isServer then |
594 | self:updateMapHotspot() |
595 | end |
596 | |
597 | return retValue |
598 | end |
getAreSurfaceSoundsActive
DescriptionDefinitiongetAreSurfaceSoundsActive()Code
247 | function Locomotive:getAreSurfaceSoundsActive(superFunc) |
248 | return self:getLastSpeed() > 0.1 |
249 | end |
getBrakeAcceleration
DescriptionDefinitiongetBrakeAcceleration()Code
429 | function Locomotive.getBrakeAcceleration(self) |
430 | local spec = self.spec_locomotive |
431 | local downhillForce = self:getDownhillForce() |
432 | local maxBrakeForce = self.serverMass * 9.81 * 0.18 |
433 | local brakeForce |
434 | |
435 | if math.abs(spec.speed) < 0.3 or not self:getIsControlled() then |
436 | brakeForce = maxBrakeForce |
437 | else |
438 | brakeForce = maxBrakeForce * 0.05 |
439 | end |
440 | |
441 | brakeForce = brakeForce * MathUtil.sign(spec.speed) |
442 | |
443 | return (1/self.serverMass) * (-brakeForce - downhillForce) |
444 | end |
getCanBeReset
DescriptionDefinitiongetCanBeReset()Code
602 | function Locomotive:getCanBeReset(superFunc) |
603 | return false |
604 | end |
getDistanceToRequestedPosition
DescriptionDefinitiongetDistanceToRequestedPosition()Code
314 | function Locomotive:getDistanceToRequestedPosition() |
315 | local spec = self.spec_locomotive |
316 | if spec.state == Locomotive.STATE_REQUESTED_POSITION_BRAKING or spec.state == Locomotive.STATE_REQUESTED_POSITION then |
317 | local currentPosition = self:getCurrentSplinePosition() |
318 | local requestedPosition = spec.requestedSplinePosition |
319 | |
320 | local distanceToGo = math.abs(requestedPosition - currentPosition) |
321 | if spec.state == Locomotive.STATE_REQUESTED_POSITION_BRAKING and distanceToGo > 0.5 then |
322 | -- if we brake a bit late and go over the requested position |
323 | return 0 |
324 | end |
325 | |
326 | return distanceToGo * self.trainSystem:getSplineLength() |
327 | end |
328 | |
329 | return 0 |
330 | end |
getDownhillForce
DescriptionDefinitiongetDownhillForce()Code
420 | function Locomotive:getDownhillForce() |
421 | local dirX, dirY, dirZ = localDirectionToWorld(self.rootNode, 0,0,1) |
422 | local angleX = math.acos(dirY / MathUtil.vector3Length(dirX, dirY, dirZ)) - 0.5*math.pi |
423 | |
424 | return self.serverMass * 9.81 * math.sin(-angleX) |
425 | end |
getFullName
DescriptionDefinitiongetFullName()Code
240 | function Locomotive:getFullName(superFunc) |
241 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
242 | return storeItem.name |
243 | end |
getIsEnterable
DescriptionDefinitiongetIsEnterable()Code
260 | function Locomotive:getIsEnterable(superFunc) |
261 | if not superFunc(self) then |
262 | return false |
263 | end |
264 | |
265 | if self.trainSystem ~= nil and not self.trainSystem:getIsTrainInDriveableRange() then |
266 | return false |
267 | end |
268 | |
269 | local spec = self.spec_locomotive |
270 | return spec.state == Locomotive.STATE_MANUAL_TRAVEL_ACTIVE or spec.state == Locomotive.STATE_MANUAL_TRAVEL_INACTIVE |
271 | end |
getIsMapHotspotVisible
DescriptionDefinitiongetIsMapHotspotVisible()Code
275 | function Locomotive:getIsMapHotspotVisible(superFunc) |
276 | if not superFunc(self) then |
277 | return false |
278 | end |
279 | |
280 | if self.trainSystem ~= nil and not self.trainSystem:getIsTrainInDriveableRange() then |
281 | return false |
282 | end |
283 | |
284 | local x, _, z = getWorldTranslation(self.rootNode) |
285 | if math.abs(x) > g_currentMission.terrainSize * 0.5 or math.abs(z) > g_currentMission.terrainSize * 0.5 then |
286 | return false |
287 | end |
288 | |
289 | return true |
290 | end |
getIsMotorStarted
DescriptionDefinitiongetIsMotorStarted()Code
410 | function Locomotive:getIsMotorStarted(superFunc) |
411 | local spec = self.spec_locomotive |
412 | return superFunc(self) |
413 | or spec.state == Locomotive.STATE_AUTOMATIC_TRAVEL_ACTIVE |
414 | or spec.state == Locomotive.STATE_REQUESTED_POSITION |
415 | or spec.state == Locomotive.STATE_REQUESTED_POSITION_BRAKING |
416 | end |
getIsReadyForAutomatedTrainTravel
DescriptionDefinitiongetIsReadyForAutomatedTrainTravel()Code
400 | function Locomotive:getIsReadyForAutomatedTrainTravel(superFunc) |
401 | if self:getIsControlled() then |
402 | return false |
403 | end |
404 | |
405 | return superFunc(self) |
406 | end |
getTraveledDistanceStatsActive
DescriptionDefinitiongetTraveledDistanceStatsActive()Code
253 | function Locomotive:getTraveledDistanceStatsActive(superFunc) |
254 | local spec = self.spec_locomotive |
255 | return spec.state == Locomotive.STATE_MANUAL_TRAVEL_ACTIVE |
256 | end |
initSpecialization
DescriptionDefinitioninitSpecialization()Code
36 | function Locomotive.initSpecialization() |
37 | local schema = Vehicle.xmlSchema |
38 | schema:setXMLSpecializationType("Locomotive") |
39 | |
40 | schema:register(XMLValueType.NODE_INDEX, "vehicle.locomotive.powerArm#node", "Power arm node") |
41 | |
42 | schema:setXMLSpecializationType() |
43 | end |
notifyPlayerFarmChanged
DescriptionDefinitionnotifyPlayerFarmChanged()Code
360 | function Locomotive:notifyPlayerFarmChanged() |
361 | if self.trainSystem ~= nil then |
362 | self:setIsTabbable(self.trainSystem.isRented and g_currentMission.player.farmId == self.trainSystem.rentFarmId) |
363 | end |
364 | end |
onDelete
DescriptionDefinitiononDelete()Code
123 | function Locomotive:onDelete() |
124 | g_messageCenter:unsubscribeAll(self) |
125 | end |
onEnterVehicle
DescriptionDefinitiononEnterVehicle()Code
384 | function Locomotive:onEnterVehicle() |
385 | local spec = self.spec_locomotive |
386 | spec.requestedSplinePosition = nil |
387 | spec.automaticTravelStartTime = nil |
388 | |
389 | if not g_currentMission.missionInfo.automaticMotorStartEnabled then |
390 | if spec.state == Locomotive.STATE_AUTOMATIC_TRAVEL_ACTIVE or spec.state == Locomotive.STATE_REQUESTED_POSITION or spec.state == Locomotive.STATE_REQUESTED_POSITION_BRAKING then |
391 | self:startMotor(true) |
392 | end |
393 | end |
394 | |
395 | self:setLocomotiveState(Locomotive.STATE_MANUAL_TRAVEL_ACTIVE) |
396 | end |
onLeaveVehicle
DescriptionDefinitiononLeaveVehicle()Code
368 | function Locomotive:onLeaveVehicle() |
369 | local spec = self.spec_locomotive |
370 | if spec.state ~= Locomotive.STATE_AUTOMATIC_TRAVEL_ACTIVE then |
371 | if self:getIsReadyForAutomatedTrainTravel() then |
372 | spec.automaticTravelStartTime = g_time + Locomotive.AUTOMATIC_DRIVE_DELAY |
373 | end |
374 | |
375 | self:setLocomotiveState(Locomotive.STATE_MANUAL_TRAVEL_INACTIVE) |
376 | self:raiseActive() |
377 | |
378 | spec.requestedSplinePosition = nil |
379 | end |
380 | end |
onLoad
DescriptionCalled on loadingDefinition
onLoad(table savegame)Arguments
table | savegame | savegame |
94 | function Locomotive:onLoad(savegame) |
95 | local spec = self.spec_locomotive |
96 | |
97 | self.serverMass = 1 -- set serverMass ~= 0 to prevent dib by zero issues after load |
98 | |
99 | spec.powerArm = self.xmlFile:getValue("vehicle.locomotive.powerArm#node", nil, self.components, self.i3dMappings) |
100 | spec.electricitySpline = nil |
101 | |
102 | spec.lastVirtualRpm = self:getMotor():getMinRpm() |
103 | spec.speed = 0 |
104 | spec.lastAcceleration = 0 |
105 | spec.nextMovingDirection = 0 |
106 | spec.sellingDirection = 1 |
107 | |
108 | spec.startBrakeDistance = 0 |
109 | spec.startBrakeSpeed = 0 |
110 | |
111 | self:setLocomotiveState(Locomotive.STATE_NONE) |
112 | |
113 | spec.motor = self:getMotor() |
114 | |
115 | spec.doStartCheck = true |
116 | |
117 | g_messageCenter:subscribe(MessageType.PLAYER_FARM_CHANGED, self.notifyPlayerFarmChanged, self) |
118 | g_messageCenter:subscribe(MessageType.PLAYER_CREATED, self.notifyPlayerFarmChanged, self) |
119 | end |
onReadStream
DescriptionCalled on client side on joinDefinition
onReadStream(integer streamId, integer connection)Arguments
integer | streamId | streamId |
integer | connection | connection |
131 | function Locomotive:onReadStream(streamId, connection) |
132 | self.spec_locomotive.state = streamReadUIntN(streamId, Locomotive.NUM_BITS_STATE) |
133 | end |
onUpdate
DescriptionDefinitiononUpdate()Code
145 | function Locomotive:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
146 | local spec = self.spec_locomotive |
147 | |
148 | if spec.doStartCheck then |
149 | if self.trainSystem ~= nil then |
150 | if not self.trainSystem:getIsRented() then |
151 | self:startAutomatedTrainTravel() |
152 | else |
153 | self:setLocomotiveState(Locomotive.STATE_MANUAL_TRAVEL_ACTIVE) |
154 | end |
155 | |
156 | self:raiseActive() |
157 | spec.doStartCheck = false |
158 | end |
159 | end |
160 | |
161 | -- update motor as long as locomotive is still moving but no player has entered |
162 | if self.isServer then |
163 | if spec.state == Locomotive.STATE_AUTOMATIC_TRAVEL_ACTIVE then |
164 | self:raiseActive() |
165 | self:updateVehiclePhysics(1, 0, 0, dt) |
166 | SpecializationUtil.raiseEvent(self, "onAutomatedTrainTravelActive", dt) |
167 | elseif spec.state == Locomotive.STATE_REQUESTED_POSITION then |
168 | if spec.requestedSplinePosition ~= nil then |
169 | local splineLength = self.trainSystem:getSplineLength() |
170 | local currentPosition = self:getCurrentSplinePosition() |
171 | local requestedPosition = spec.requestedSplinePosition |
172 | |
173 | local targetDirection = MathUtil.sign(requestedPosition-currentPosition) |
174 | local brakeAcceleration = Locomotive.getBrakeAcceleration(self) |
175 | local brakeDistance = math.abs((spec.speed^2)/(2*brakeAcceleration)) |
176 | local brakePoint = (requestedPosition - (brakeDistance / splineLength) * targetDirection) % 1 |
177 | local pendingDirectionChange = not (targetDirection == self.movingDirection or self.movingDirection == 0) |
178 | if not pendingDirectionChange and ((targetDirection >= 0 and (currentPosition > brakePoint and currentPosition < brakePoint + 0.5)) |
179 | or (targetDirection < 0 and (currentPosition < brakePoint and currentPosition > brakePoint - 0.5))) then |
180 | self:setLocomotiveState(Locomotive.STATE_REQUESTED_POSITION_BRAKING) |
181 | |
182 | spec.startBrakeDistance = brakeDistance |
183 | spec.startBrakeSpeed = spec.speed |
184 | else |
185 | self:updateVehiclePhysics(targetDirection, 0, 0, dt) |
186 | end |
187 | self:raiseActive() |
188 | end |
189 | elseif spec.state == Locomotive.STATE_REQUESTED_POSITION_BRAKING then |
190 | -- if we detect that we could stop way earlier (e.g. due to changing slope) we release the brake again |
191 | local brakeAcceleration = Locomotive.getBrakeAcceleration(self) |
192 | local brakeDistance = math.abs((spec.startBrakeSpeed^2)/(2*brakeAcceleration)) |
193 | if brakeDistance < spec.startBrakeDistance - 10 then |
194 | self:setLocomotiveState(Locomotive.STATE_REQUESTED_POSITION) |
195 | end |
196 | |
197 | if self.movingDirection > 0 then |
198 | self:updateVehiclePhysics(-1, 0, 0, dt) |
199 | else |
200 | self:updateVehiclePhysics(1, 0, 0, dt) |
201 | end |
202 | |
203 | self:raiseActive() |
204 | if spec.speed == 0 then |
205 | self:setLocomotiveState(Locomotive.STATE_MANUAL_TRAVEL_ACTIVE) |
206 | |
207 | self:stopMotor() |
208 | end |
209 | elseif spec.state == Locomotive.STATE_MANUAL_TRAVEL_INACTIVE then |
210 | if self.movingDirection > 0 then |
211 | self:updateVehiclePhysics(-1, 0, 0, dt) |
212 | else |
213 | self:updateVehiclePhysics(1, 0, 0, dt) |
214 | end |
215 | self:raiseActive() |
216 | end |
217 | end |
218 | end |
onWriteStream
DescriptionCalled on server side on joinDefinition
onWriteStream(integer streamId, integer connection)Arguments
integer | streamId | streamId |
integer | connection | connection |
139 | function Locomotive:onWriteStream(streamId, connection) |
140 | streamWriteUIntN(streamId, self.spec_locomotive.state, Locomotive.NUM_BITS_STATE) |
141 | end |
prerequisitesPresent
DescriptionChecks if all prerequisite specializations are loadedDefinition
prerequisitesPresent(table specializations)Arguments
table | specializations | specializations |
boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
30 | function Locomotive.prerequisitesPresent(specializations) |
31 | return SpecializationUtil.hasSpecialization(SplineVehicle, specializations) and SpecializationUtil.hasSpecialization(Drivable, specializations) |
32 | end |
registerEventListeners
DescriptionDefinitionregisterEventListeners()Code
80 | function Locomotive.registerEventListeners(vehicleType) |
81 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", Locomotive) |
82 | SpecializationUtil.registerEventListener(vehicleType, "onDelete", Locomotive) |
83 | SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Locomotive) |
84 | SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Locomotive) |
85 | SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Locomotive) |
86 | SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", Locomotive) |
87 | SpecializationUtil.registerEventListener(vehicleType, "onEnterVehicle", Locomotive) |
88 | end |
registerEvents
DescriptionDefinitionregisterEvents()Code
47 | function Locomotive.registerEvents(vehicleType) |
48 | SpecializationUtil.registerEvent(vehicleType, "onAutomatedTrainTravelActive") |
49 | end |
registerFunctions
DescriptionDefinitionregisterFunctions()Code
53 | function Locomotive.registerFunctions(vehicleType) |
54 | SpecializationUtil.registerFunction(vehicleType, "getDownhillForce", Locomotive.getDownhillForce) |
55 | SpecializationUtil.registerFunction(vehicleType, "setRequestedSplinePosition", Locomotive.setRequestedSplinePosition) |
56 | SpecializationUtil.registerFunction(vehicleType, "getDistanceToRequestedPosition", Locomotive.getDistanceToRequestedPosition) |
57 | SpecializationUtil.registerFunction(vehicleType, "setLocomotiveState", Locomotive.setLocomotiveState) |
58 | SpecializationUtil.registerFunction(vehicleType, "startAutomatedTrainTravel", Locomotive.startAutomatedTrainTravel) |
59 | SpecializationUtil.registerFunction(vehicleType, "notifyPlayerFarmChanged", Locomotive.notifyPlayerFarmChanged) |
60 | end |
registerOverwrittenFunctions
DescriptionDefinitionregisterOverwrittenFunctions()Code
64 | function Locomotive.registerOverwrittenFunctions(vehicleType) |
65 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsMotorStarted", Locomotive.getIsMotorStarted) |
66 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "updateVehiclePhysics", Locomotive.updateVehiclePhysics) |
67 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsReadyForAutomatedTrainTravel", Locomotive.getIsReadyForAutomatedTrainTravel) |
68 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "alignToSplineTime", Locomotive.alignToSplineTime) |
69 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "setTrainSystem", Locomotive.setTrainSystem) |
70 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getFullName", Locomotive.getFullName) |
71 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAreSurfaceSoundsActive", Locomotive.getAreSurfaceSoundsActive) |
72 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getTraveledDistanceStatsActive", Locomotive.getTraveledDistanceStatsActive) |
73 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsEnterable", Locomotive.getIsEnterable) |
74 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsMapHotspotVisible", Locomotive.getIsMapHotspotVisible) |
75 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanBeReset", Locomotive.getCanBeReset) |
76 | end |
setLocomotiveState
DescriptionDefinitionsetLocomotiveState()Code
334 | function Locomotive:setLocomotiveState(state, noEventSend) |
335 | local spec = self.spec_locomotive |
336 | spec.state = state |
337 | |
338 | if state == Locomotive.STATE_AUTOMATIC_TRAVEL_ACTIVE then |
339 | if self.setRandomVehicleCharacter ~= nil then |
340 | self:setRandomVehicleCharacter() |
341 | end |
342 | elseif state == Locomotive.STATE_MANUAL_TRAVEL_ACTIVE then |
343 | self:restoreVehicleCharacter() |
344 | end |
345 | |
346 | if g_server ~= nil and not noEventSend then |
347 | g_server:broadcastEvent(LocomotiveStateEvent.new(self, state), nil, nil, self) |
348 | end |
349 | end |
setRequestedSplinePosition
DescriptionDefinitionsetRequestedSplinePosition()Code
294 | function Locomotive:setRequestedSplinePosition(splinePosition, noEventSend) |
295 | local spec = self.spec_locomotive |
296 | spec.requestedSplinePosition = splinePosition |
297 | self:setLocomotiveState(Locomotive.STATE_REQUESTED_POSITION, true) |
298 | |
299 | local currentPosition = self:getCurrentSplinePosition() |
300 | local requestedPosition = spec.requestedSplinePosition |
301 | if currentPosition > spec.requestedSplinePosition then |
302 | if math.abs(currentPosition - (requestedPosition + 1)) < math.abs(currentPosition - requestedPosition) then |
303 | spec.requestedSplinePosition = requestedPosition + 1 |
304 | end |
305 | end |
306 | |
307 | if self.isServer then |
308 | self:startMotor() |
309 | end |
310 | end |
setTrainSystem
DescriptionDefinitionsetTrainSystem()Code
222 | function Locomotive:setTrainSystem(superFunc, trainSystem) |
223 | superFunc(self, trainSystem) |
224 | |
225 | local spec = self.spec_locomotive |
226 | if spec.powerArm ~= nil then |
227 | local spline = trainSystem:getElectricitySpline() |
228 | if spline ~= nil then |
229 | local electricitySplineLength = trainSystem:getElectricitySplineLength() |
230 | local splineLength = trainSystem:getSplineLength() |
231 | spec.splineDiff = math.abs(electricitySplineLength - splineLength) |
232 | spec.electricitySplineSearchTime = spec.splineDiff * 5 / electricitySplineLength |
233 | spec.electricitySpline = spline |
234 | end |
235 | end |
236 | end |
startAutomatedTrainTravel
DescriptionDefinitionstartAutomatedTrainTravel()Code
353 | function Locomotive:startAutomatedTrainTravel() |
354 | self:setLocomotiveState(Locomotive.STATE_AUTOMATIC_TRAVEL_ACTIVE) |
355 | self:startMotor() |
356 | end |
updateVehiclePhysics
DescriptionDefinitionupdateVehiclePhysics()Code
448 | function Locomotive:updateVehiclePhysics(superFunc, axisForward, axisSide, doHandbrake, dt) |
449 | local spec = self.spec_locomotive |
450 | local specDrivable = self.spec_drivable |
451 | |
452 | axisForward = axisForward * spec.sellingDirection |
453 | |
454 | local acceleration = superFunc(self, axisForward, axisSide, doHandbrake, dt) |
455 | |
456 | local interpDt = g_physicsDt |
457 | if g_server == nil then |
458 | -- on clients, we interpolate the vehicles with g_physicsDtUnclamped, thus we need to use the same for camera interpolation |
459 | interpDt = g_physicsDtUnclamped |
460 | end |
461 | |
462 | -- assuming: |
463 | -- totalMass: mass, |
464 | -- P(v): tractive effort of locomotive |
465 | -- Q(v): drag of train (dismissed) |
466 | -- B: brake force |
467 | -- g: gravity |
468 | -- alpha: inclination angle |
469 | -- |
470 | -- totalMass*a = P(v) - Q(v) - B - totalMass*g*sin(alpha) |
471 | -- a = 1/totalMass * [ (P(v) - Q(v) - B - totalMass*g*sin(alpha) ] |
472 | |
473 | local tractiveEffort = 300000 |
474 | local maxBrakeForce = self.serverMass * 9.81 * 0.18 |
475 | local downhillForce = self:getDownhillForce() |
476 | tractiveEffort = math.min(tractiveEffort, maxBrakeForce) |
477 | |
478 | if self:getIsMotorStarted() and self:getMotorStartTime() <= g_currentMission.time then |
479 | local reverserDirection = specDrivable == nil and 1 or specDrivable.reverserDirection |
480 | if self:getCruiseControlState() ~= Drivable.CRUISECONTROL_STATE_OFF then |
481 | local cruiseControlSpeed = self:getCruiseControlSpeed() / 3.6 |
482 | if spec.speed < reverserDirection * cruiseControlSpeed then |
483 | acceleration = 1 |
484 | elseif spec.speed > reverserDirection * cruiseControlSpeed then |
485 | acceleration = -1 |
486 | end |
487 | end |
488 | else |
489 | tractiveEffort = maxBrakeForce |
490 | end |
491 | |
492 | if math.abs(acceleration) < 0.001 then |
493 | local a = Locomotive.getBrakeAcceleration(self) |
494 | |
495 | if spec.speed > 0 then |
496 | spec.speed = math.max(0, spec.speed + a * dt/1000) |
497 | elseif spec.speed < 0 then |
498 | spec.speed = math.min(0, spec.speed + a * dt/1000) |
499 | else |
500 | -- move train if track is too steep ??? |
501 | if maxBrakeForce < math.abs(downhillForce) then |
502 | spec.speed = spec.speed + a * dt/1000 |
503 | end |
504 | end |
505 | |
506 | if spec.speed == 0 then |
507 | spec.hasStopped = true |
508 | else |
509 | spec.hasStopped = false |
510 | end |
511 | else |
512 | if math.abs(spec.speed) > 0.1 then |
513 | spec.hasStopped = false |
514 | elseif math.abs(spec.speed) == 0 then |
515 | spec.hasStopped = true |
516 | end |
517 | if spec.hasStopped == nil or (spec.hasStopped and math.abs(acceleration) > 0.01) then |
518 | spec.nextMovingDirection = MathUtil.sign(acceleration) |
519 | end |
520 | |
521 | local a = 0 |
522 | local brakeForce |
523 | if spec.nextMovingDirection == nil or (spec.nextMovingDirection * acceleration) > 0 then |
524 | tractiveEffort = acceleration * tractiveEffort |
525 | brakeForce = 0 |
526 | a = (1/self.serverMass) * ( tractiveEffort - brakeForce - downhillForce ) |
527 | else |
528 | tractiveEffort = 0 |
529 | brakeForce = MathUtil.sign(spec.speed) * math.abs(acceleration) * maxBrakeForce |
530 | if math.abs(spec.speed) < 0.1 then |
531 | spec.speed = 0 |
532 | else |
533 | a = (1/self.serverMass) * ( tractiveEffort - brakeForce - downhillForce ) |
534 | end |
535 | end |
536 | |
537 | spec.speed = spec.speed + a * interpDt/1000 |
538 | end |
539 | |
540 | local motor = spec.motor |
541 | if spec.speed > 0 then |
542 | spec.speed = math.min(spec.speed, motor.maxForwardSpeed) |
543 | elseif spec.speed < 0 then |
544 | spec.speed = math.max(spec.speed, -motor.maxBackwardSpeed) |
545 | end |
546 | |
547 | if spec.speed ~= 0 then |
548 | self.trainSystem:updateTrainPositionByLocomotiveSpeed(interpDt, spec.speed) |
549 | end |
550 | |
551 | local minRpm = motor.minRpm |
552 | local maxRpm = motor.maxRpm |
553 | -- fake rpm for indoorHud |
554 | if spec.lastAcceleration * spec.nextMovingDirection > 0 then |
555 | spec.lastVirtualRpm = math.min(maxRpm, spec.lastVirtualRpm + (0.0005 * dt * (maxRpm - minRpm))) |
556 | else |
557 | spec.lastVirtualRpm = math.max(minRpm, spec.lastVirtualRpm - (0.001 * dt * (maxRpm - minRpm))) |
558 | end |
559 | |
560 | motor:setEqualizedMotorRpm(spec.lastVirtualRpm) |
561 | |
562 | spec.lastAcceleration = acceleration |
563 | end |