32 | function Motorized:load(savegame) |
33 | |
34 | self.getIsMotorStarted = Utils.overwrittenFunction(self.getIsMotorStarted, Motorized.getIsMotorStarted); |
35 | self.getDeactivateOnLeave = Utils.overwrittenFunction(self.getDeactivateOnLeave, Motorized.getDeactivateOnLeave); |
36 | self.getDeactivateLights = Utils.overwrittenFunction(self.getDeactivateLights, Motorized.getDeactivateLights); |
37 | self.updateFuelUsage = Utils.overwrittenFunction(self.updateFuelUsage, Motorized.updateFuelUsage); |
38 | self.startMotor = SpecializationUtil.callSpecializationsFunction("startMotor"); |
39 | self.stopMotor = SpecializationUtil.callSpecializationsFunction("stopMotor"); |
40 | self.setIsFuelFilling = SpecializationUtil.callSpecializationsFunction("setIsFuelFilling"); |
41 | self.setFuelFillLevel = SpecializationUtil.callSpecializationsFunction("setFuelFillLevel"); |
42 | self.addFuelFillTrigger = Motorized.addFuelFillTrigger; |
43 | self.removeFuelFillTrigger = Motorized.removeFuelFillTrigger; |
44 | |
45 | self.motorizedNode = nil; |
46 | for _, component in pairs(self.components) do |
47 | if component.motorized then |
48 | self.motorizedNode = component.node; |
49 | break; |
50 | end |
51 | end |
52 | |
53 | Motorized.loadDifferentials(self, self.xmlFile, self.differentialIndex); |
54 | Motorized.loadMotor(self, self.xmlFile, self.configurations["motor"]); |
55 | Motorized.loadSounds(self, self.xmlFile, self.configurations["motor"]); |
56 | |
57 | self.motorizedFillActivatable = MotorizedRefuelActivatable:new(self); |
58 | |
59 | self.fuelFillTriggers = {}; |
60 | self.isFuelFilling = false; |
61 | self.fuelFillLitersPerSecond = 10; |
62 | self.fuelFillLevel = 0; |
63 | self.lastFuelFillLevel = 0; |
64 | self:setFuelFillLevel(self.fuelCapacity); |
65 | self.sentFuelFillLevel = self.fuelFillLevel; |
66 | self.stopMotorOnLeave = true; |
67 | |
68 | if self.isClient then |
69 | self.exhaustParticleSystems = {}; |
70 | local exhaustParticleSystemCount = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.exhaustParticleSystems#count"), 0); |
71 | for i=1, exhaustParticleSystemCount do |
72 | local namei = string.format("vehicle.exhaustParticleSystems.exhaustParticleSystem%d", i); |
73 | local ps = {} |
74 | ParticleUtil.loadParticleSystem(self.xmlFile, ps, namei, self.components, false, nil, self.baseDirectory) |
75 | ps.minScale = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.exhaustParticleSystems#minScale"), 0.5); |
76 | ps.maxScale = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.exhaustParticleSystems#maxScale"), 1); |
77 | table.insert(self.exhaustParticleSystems, ps) |
78 | end |
79 | if #self.exhaustParticleSystems == 0 then |
80 | self.exhaustParticleSystems = nil |
81 | end |
82 | |
83 | local exhaustFlapIndex = getXMLString(self.xmlFile, "vehicle.exhaustFlap#index"); |
84 | if exhaustFlapIndex ~= nil then |
85 | self.exhaustFlap = {}; |
86 | self.exhaustFlap.node = Utils.indexToObject(self.components, exhaustFlapIndex); |
87 | self.exhaustFlap.maxRot = Utils.degToRad(Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.exhaustFlap#maxRot"),0)); |
88 | end |
89 | |
90 | self.exhaustEffects = {}; |
91 | Motorized.loadExhaustEffects(self, self.xmlFile, self.exhaustEffects); |
92 | if table.getn(self.exhaustEffects) == 0 then |
93 | self.exhaustEffects = nil; |
94 | end |
95 | end |
96 | |
97 | self.motorStartDuration = 0; |
98 | if self.sampleMotorStart ~= nil then |
99 | self.motorStartDuration = self.sampleMotorStart.duration; |
100 | end |
101 | self.motorStartDuration = Utils.getNoNil( Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.motorStartDuration"), self.motorStartDuration), 0); |
102 | self.motorStartTime = 0; |
103 | self.lastRoundPerMinute = 0; |
104 | self.actualLoadPercentage = 0; |
105 | self.maxDecelerationDuringBrake = 0; |
106 | self.motorizedDirtyFlag = self:getNextDirtyFlag(); |
107 | |
108 | self.isMotorStarted = false; |
109 | self.motorStopTimerDuration = g_gameSettings:getValue("motorStopTimerDuration") |
110 | self.motorStopTimer = self.motorStopTimerDuration |
111 | |
112 | self.fuelFillLevelHud = VehicleHudUtils.loadHud(self, self.xmlFile, "fuel"); |
113 | self.rpmHud = VehicleHudUtils.loadHud(self, self.xmlFile, "rpm"); |
114 | self.timeHud = VehicleHudUtils.loadHud(self, self.xmlFile, "time"); |
115 | if self.timeHud ~= nil then |
116 | self.minuteChanged = Utils.appendedFunction(self.minuteChanged, Motorized.minuteChanged); |
117 | g_currentMission.environment:addMinuteChangeListener(self); |
118 | self:minuteChanged(); |
119 | end |
120 | self.speedHud = VehicleHudUtils.loadHud(self, self.xmlFile, "speed"); |
121 | self.fuelUsageHud = VehicleHudUtils.loadHud(self, self.xmlFile, "fuelUsage"); |
122 | |
123 | if savegame ~= nil then |
124 | local fuelFillLevel = getXMLFloat(savegame.xmlFile, savegame.key.."#fuelFillLevel"); |
125 | if fuelFillLevel ~= nil then |
126 | if self.fuelCapacity ~= 0 then |
127 | local minFuelFillLevel = 0.1*self.fuelCapacity |
128 | local numToRefill = math.max(minFuelFillLevel - fuelFillLevel, 0); |
129 | if numToRefill > 0 then |
130 | fuelFillLevel = minFuelFillLevel; |
131 | |
132 | local delta = numToRefill * g_currentMission.economyManager:getPricePerLiter(FillUtil.FILLTYPE_FUEL) |
133 | g_currentMission.missionStats:updateStats("expenses", delta); |
134 | g_currentMission:addSharedMoney(-delta, "purchaseFuel"); |
135 | end |
136 | end |
137 | self:setFuelFillLevel(fuelFillLevel); |
138 | end |
139 | end |
140 | |
141 | self.motorTurnedOnRotationNodes = Utils.loadRotationNodes(self.xmlFile, {}, "vehicle.turnedOnRotationNodes.turnedOnRotationNode", "motor", self.components); |
142 | end |
300 | function Motorized:loadMotor(xmlFile, motorId) |
301 | local key, motorId = Vehicle.getXMLConfigurationKey(xmlFile, motorId, "vehicle.motorConfigurations.motorConfiguration", "vehicle", "motor"); |
302 | |
303 | local fallbackConfigKey = "vehicle.motorConfigurations.motorConfiguration(0)"; |
304 | local fallbackOldKey = "vehicle"; |
305 | |
306 | self.motorType = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#type", getXMLString, "vehicle", fallbackConfigKey, fallbackOldKey); |
307 | |
308 | self.fuelCapacity = Vehicle.getConfigurationValue(xmlFile, key, ".fuelCapacity", "", getXMLFloat, 500, fallbackConfigKey, fallbackOldKey); |
309 | |
310 | local wheelKey, _ = Vehicle.getXMLConfigurationKey(xmlFile, self.configurations["wheel"], "vehicle.wheelConfigurations.wheelConfiguration", "vehicle", "wheels"); |
311 | self.fuelCapacity = Utils.getNoNil(Vehicle.getConfigurationValue(xmlFile, wheelKey, ".fuelCapacity", "", getXMLInt, nil, nil, ""), self.fuelCapacity); |
312 | |
313 | local fuelUsage = Vehicle.getConfigurationValue(xmlFile, key, ".fuelUsage", "", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey); |
314 | if fuelUsage == nil then |
315 | fuelUsage = self.fuelCapacity / 5; -- default fuel usage: full->empty: 5h |
316 | end |
317 | self.fuelUsage = fuelUsage / (60*60*1000); -- from l/h to l/ms |
318 | |
319 | ObjectChangeUtil.updateObjectChanges(xmlFile, "vehicle.motorConfigurations.motorConfiguration", motorId, self.components, self); |
320 | |
321 | local motorMinRpm = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#minRpm", getXMLFloat, 1000, fallbackConfigKey, fallbackOldKey); |
322 | local motorMaxRpm = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#maxRpm", getXMLFloat, 1800, fallbackConfigKey, fallbackOldKey); |
323 | local minSpeed = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#minSpeed", getXMLFloat, 1, fallbackConfigKey, fallbackOldKey); |
324 | local maxForwardSpeed = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#maxForwardSpeed", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey); |
325 | local maxBackwardSpeed = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#maxBackwardSpeed", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey); |
326 | if maxForwardSpeed ~= nil then |
327 | maxForwardSpeed = maxForwardSpeed/3.6; |
328 | end |
329 | if maxBackwardSpeed ~= nil then |
330 | maxBackwardSpeed = maxBackwardSpeed/3.6; |
331 | end |
332 | |
333 | local maxWheelSpeed = Vehicle.getConfigurationValue(xmlFile, wheelKey, ".wheels", "#maxForwardSpeed", getXMLFloat, nil, nil, "vehicle.wheels"); |
334 | if maxWheelSpeed ~= nil then |
335 | maxForwardSpeed = maxWheelSpeed/3.6; |
336 | end |
337 | |
338 | local brakeForce = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#brakeForce", getXMLFloat, 10, fallbackConfigKey, fallbackOldKey)*2; |
339 | local lowBrakeForceScale = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#lowBrakeForceScale", getXMLFloat, 0.5, fallbackConfigKey, fallbackOldKey); |
340 | local lowBrakeForceSpeedLimit = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#lowBrakeForceSpeedLimit", getXMLFloat, 20, fallbackConfigKey, fallbackOldKey)/3600; |
341 | local forwardGearRatio = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#forwardGearRatio", getXMLFloat, 2, fallbackConfigKey, fallbackOldKey); |
342 | local backwardGearRatio = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#backwardGearRatio", getXMLFloat, 1.5, fallbackConfigKey, fallbackOldKey); |
343 | local maxForwardGearRatio = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#maxForwardGearRatio", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey); |
344 | local minForwardGearRatio = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#minForwardGearRatio", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey); |
345 | local maxBackwardGearRatio = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#maxBackwardGearRatio", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey); |
346 | local minBackwardGearRatio = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#minBackwardGearRatio", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey); |
347 | local rpmFadeOutRange = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#rpmFadeOutRange", getXMLFloat, 20, fallbackConfigKey, fallbackOldKey); |
348 | local torqueScale = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#torqueScale", getXMLFloat, 1, fallbackConfigKey, fallbackOldKey); |
349 | local ptoMotorRpmRatio = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#ptoMotorRpmRatio", getXMLFloat, 4, fallbackConfigKey, fallbackOldKey); |
350 | |
351 | --local maxTorque = 0; |
352 | local maxMotorPower = 0; |
353 | local torqueCurve = AnimCurve:new(linearInterpolator1); |
354 | local torqueI = 0; |
355 | local torqueBase = fallbackOldKey..".motor.torque"; -- fallback to old motor setup |
356 | if key ~= nil and hasXMLProperty(xmlFile, fallbackConfigKey..".motor.torque(0)") then -- using default motor configuration |
357 | torqueBase = fallbackConfigKey..".motor.torque"; |
358 | end |
359 | if key ~= nil and hasXMLProperty(xmlFile, key..".motor.torque(0)") then -- using selected motor configuration |
360 | torqueBase = key..".motor.torque"; |
361 | end |
362 | |
363 | while true do |
364 | local torqueKey = string.format(torqueBase.."(%d)", torqueI); |
365 | local normRpm = getXMLFloat(xmlFile, torqueKey.."#normRpm"); |
366 | local rpm; |
367 | if normRpm == nil then |
368 | rpm = getXMLFloat(xmlFile, torqueKey.."#rpm"); |
369 | else |
370 | rpm = normRpm * motorMaxRpm; |
371 | end |
372 | local torque = getXMLFloat(xmlFile, torqueKey.."#torque"); |
373 | if torque == nil or rpm == nil then |
374 | break; |
375 | end |
376 | torqueCurve:addKeyframe({v=torque*torqueScale, time = rpm}); |
377 | torqueI = torqueI +1; |
378 | |
379 | local motorPower = 1000 * ( rpm*math.pi/30*(torque*torqueScale) ); |
380 | if motorPower > maxMotorPower then |
381 | maxMotorPower = motorPower; |
382 | end |
383 | end |
384 | |
385 | if self.motorType == "locomotive" then |
386 | self.motor = LocomotiveMotor:new(self, motorMinRpm, motorMaxRpm, maxForwardSpeed, maxBackwardSpeed, torqueCurve, brakeForce, forwardGearRatio, backwardGearRatio, minForwardGearRatio, maxForwardGearRatio, minBackwardGearRatio, maxBackwardGearRatio, ptoMotorRpmRatio, rpmFadeOutRange, 0, maxMotorPower); |
387 | else |
388 | self.motor = VehicleMotor:new(self, motorMinRpm, motorMaxRpm, maxForwardSpeed, maxBackwardSpeed, torqueCurve, brakeForce, forwardGearRatio, backwardGearRatio, minForwardGearRatio, maxForwardGearRatio, minBackwardGearRatio, maxBackwardGearRatio, ptoMotorRpmRatio, rpmFadeOutRange, 0, maxMotorPower, minSpeed); |
389 | end |
390 | |
391 | local rotInertia = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#rotInertia", getXMLFloat, self.motor:getRotInertia(), fallbackConfigKey, fallbackOldKey); |
392 | local dampingRate = Vehicle.getConfigurationValue(xmlFile, key, ".motor", "#dampingRate", getXMLFloat, self.motor:getDampingRate(), fallbackConfigKey, fallbackOldKey); |
393 | self.motor:setRotInertia(rotInertia); |
394 | self.motor:setDampingRate(dampingRate); |
395 | self.motor:setLowBrakeForce(lowBrakeForceScale, lowBrakeForceSpeedLimit); |
396 | end |
401 | function Motorized:loadSounds(xmlFile, motorId) |
402 | if self.isClient then |
403 | self.sampleRefuel = SoundUtil.loadSample(xmlFile, {}, "vehicle.refuelSound", "$data/maps/sounds/refuel.wav", self.baseDirectory, self.components[1].node); |
404 | self.sampleMotorStart = SoundUtil.loadSample(xmlFile, {}, "vehicle.motorStartSound", nil, self.baseDirectory); |
405 | self.sampleMotorStop = SoundUtil.loadSample(xmlFile, {}, "vehicle.motorStopSound", nil, self.baseDirectory); |
406 | self.sampleMotor = SoundUtil.loadSample(xmlFile, {}, "vehicle.motorSound", nil, self.baseDirectory, self.components[1].node); |
407 | self.sampleMotorRun = SoundUtil.loadSample(xmlFile, {}, "vehicle.motorSoundRun", nil, self.baseDirectory, self.components[1].node); |
408 | self.sampleMotorLoad = SoundUtil.loadSample(xmlFile, {}, "vehicle.motorSoundLoad", nil, self.baseDirectory, self.components[1].node); |
409 | self.sampleGearbox = SoundUtil.loadSample(xmlFile, {}, "vehicle.gearboxSound", nil, self.baseDirectory, self.components[1].node); |
410 | self.sampleRetarder = SoundUtil.loadSample(xmlFile, {}, "vehicle.retarderSound", nil, self.baseDirectory, self.components[1].node); |
411 | |
412 | self.sampleBrakeCompressorStart = SoundUtil.loadSample(xmlFile, {}, "vehicle.brakeCompressorStartSound", nil, self.baseDirectory); |
413 | self.sampleBrakeCompressorRun = SoundUtil.loadSample(xmlFile, {}, "vehicle.brakeCompressorRunSound", nil, self.baseDirectory); |
414 | self.sampleBrakeCompressorStop = SoundUtil.loadSample(xmlFile, {}, "vehicle.brakeCompressorStopSound", nil, self.baseDirectory); |
415 | |
416 | self.sampleReverseDrive = SoundUtil.loadSample(xmlFile, {}, "vehicle.reverseDriveSound", nil, self.baseDirectory, self.components[1].node); |
417 | |
418 | self.sampleCompressedAir = SoundUtil.loadSample(xmlFile, {}, "vehicle.compressedAirSound", nil, self.baseDirectory); |
419 | self.sampleAirReleaseValve = SoundUtil.loadSample(xmlFile, {}, "vehicle.airReleaseValveSound", nil, self.baseDirectory); |
420 | |
421 | local maxRpmDelta = (self.motor:getMaxRpm() - self.motor:getMinRpm()); |
422 | local maxRpsDelta = maxRpmDelta / 60; |
423 | |
424 | if self.sampleMotor.sample ~= nil then |
425 | self.motorSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSound#pitchMax"), 2.0); |
426 | self.motorSoundPitchScale = getXMLFloat(xmlFile, "vehicle.motorSound#pitchScale"); |
427 | if self.motorSoundPitchScale == nil then |
428 | self.motorSoundPitchScale = (self.motorSoundPitchMax - self.sampleMotor.pitchOffset) / maxRpsDelta; |
429 | end |
430 | self.motorSoundVolumeMin = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSound#volumeMin"), self.sampleMotor.volume); |
431 | self.motorSoundVolumeMinSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSound#volumeMinSpeed"), math.huge); |
432 | end |
433 | |
434 | if self.sampleMotorRun.sample ~= nil then |
435 | self.motorSoundRunPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSoundRun#pitchMax"), 2.0); |
436 | self.motorSoundRunPitchScale = getXMLFloat(xmlFile, "vehicle.motorSoundRun#pitchScale"); |
437 | if self.motorSoundRunPitchScale == nil then |
438 | self.motorSoundRunPitchScale = (self.motorSoundRunPitchMax - self.sampleMotorRun.pitchOffset) / maxRpsDelta; |
439 | end |
440 | self.motorSoundRunMinimalVolumeFactor = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSoundRun#minimalVolumeFactor"), 0.0); |
441 | end |
442 | |
443 | if self.sampleMotorLoad.sample ~= nil then |
444 | self.motorSoundLoadPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSoundLoad#pitchMax"), 2.0); |
445 | self.motorSoundLoadPitchScale = getXMLFloat(xmlFile, "vehicle.motorSoundLoad#pitchScale"); |
446 | if self.motorSoundLoadPitchScale == nil then |
447 | self.motorSoundLoadPitchScale = (self.motorSoundLoadPitchMax - self.sampleMotorLoad.pitchOffset) / maxRpsDelta; |
448 | end |
449 | self.motorSoundLoadMinimalVolumeFactor = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSoundLoad#minimalVolumeFactor"), 0.0); |
450 | self.motorSoundLoadFactor = 0; |
451 | end |
452 | |
453 | if self.sampleGearbox.sample ~= nil then |
454 | self.gearboxSoundVolumeMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.gearboxSound#volumeMax"), 2.0); |
455 | self.gearboxSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.gearboxSound#pitchMax"), 2.0); |
456 | self.gearboxSoundReverseVolumeMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.gearboxSound#reverseVolumeMax"), self.gearboxSoundVolumeMax); |
457 | self.gearboxSoundReversePitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.gearboxSound#reversePitchMax"), self.gearboxSoundPitchMax); |
458 | self.gearboxSoundPitchExponent = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.gearboxSound#pitchExponent"), 1.0); |
459 | end |
460 | |
461 | if self.sampleRetarder.sample ~= nil then |
462 | self.retarderSoundVolumeMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.retarderSound#volumeMax"), 2.0); |
463 | self.retarderSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.retarderSound#pitchMax"), 2.0); |
464 | self.retarderSoundMinSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.retarderSound#minSpeed"), 5.0); |
465 | self.retarderSoundActualVolume = 0.0; |
466 | end |
467 | |
468 | self.pitchInfluenceFromWheels = 0; |
469 | self.volumeInfluenceFromWheels = 0; |
470 | self.wheelInfluenceOnGearboxSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.gearboxSound#wheelInfluenceOnVolume"), 0.1); |
471 | self.wheelInfluenceOnGearboxSoundPitch = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.gearboxSound#wheelInfluenceOnPitch"), 0.2); |
472 | self.wheelInfluenceOnRetarderSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.retarderSound#wheelInfluenceOnVolume"), 0.1); |
473 | self.wheelInfluenceOnRetarderSoundPitch = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.retarderSound#wheelInfluenceOnPitch"), 0.2); |
474 | |
475 | if self.sampleBrakeCompressorRun.sample ~= nil then |
476 | self.brakeCompressorRunSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.brakeCompressorRunSound#pitchMax"), 2.0); |
477 | self.brakeCompressorRunSoundPitchScale = getXMLFloat(xmlFile, "vehicle.brakeCompressorRunSound#pitchScale"); |
478 | if self.brakeCompressorRunSoundPitchScale == nil then |
479 | self.brakeCompressorRunSoundPitchScale = (self.brakeCompressorRunSoundPitchMax - self.sampleBrakeCompressorRun.pitchOffset) / maxRpsDelta; |
480 | end |
481 | end |
482 | |
483 | self.brakeCompressor = {}; |
484 | self.brakeCompressor.capacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.brakeCompressor#capacity"), 6); |
485 | self.brakeCompressor.refillFilllevel = math.min(self.brakeCompressor.capacity, Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.brakeCompressor#refillFillLevel"), self.brakeCompressor.capacity/2)); |
486 | self.brakeCompressor.fillSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.brakeCompressor#fillSpeed"), 0.6) / 1000; |
487 | self.brakeCompressor.fillLevel = 0; |
488 | self.brakeCompressor.doFill = true; |
489 | |
490 | self.soundsAdjustedToIndoorCamera = false; |
491 | |
492 | self.compressedAirSoundEnabled = false; |
493 | self.compressionSoundTime = 0; |
494 | end |
495 | end |
630 | function Motorized:update(dt) |
631 | |
632 | if self.isClient then |
633 | if self.isEntered and self:getIsActiveForInput(false) and not g_currentMission.missionInfo.automaticMotorStartEnabled then |
634 | if InputBinding.hasEvent(InputBinding.TOGGLE_MOTOR_STATE) then |
635 | if not self:getIsHired() then |
636 | if self.isMotorStarted then |
637 | self:stopMotor() |
638 | else |
639 | self:startMotor() |
640 | end |
641 | end |
642 | end |
643 | end |
644 | end |
645 | |
646 | Utils.updateRotationNodes(self, self.motorTurnedOnRotationNodes, dt, self:getIsMotorStarted()); |
647 | |
648 | if self:getIsMotorStarted() then |
649 | local accInput = 0; |
650 | if self.axisForward ~= nil then |
651 | accInput = -self.axisForward; |
652 | end |
653 | if self.cruiseControl ~= nil and self.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF then |
654 | accInput = 1; |
655 | end |
656 | if self.isClient then |
657 | if self:getIsActiveForSound() then |
658 | if not SoundUtil.isSamplePlaying(self.sampleMotorStart, 1.5*dt) then |
659 | SoundUtil.playSample(self.sampleMotor, 0, 0, nil); |
660 | SoundUtil.playSample(self.sampleMotorRun, 0, 0, 0); |
661 | SoundUtil.playSample(self.sampleMotorLoad, 0, 0, 0); |
662 | SoundUtil.playSample(self.sampleGearbox, 0, 0, 0); |
663 | SoundUtil.playSample(self.sampleRetarder, 0, 0, 0); |
664 | |
665 | if self.brakeLightsVisibility then |
666 | self.brakeLightsVisibilityWasActive = true; |
667 | self.maxDecelerationDuringBrake = math.max(self.maxDecelerationDuringBrake, math.abs(accInput)); |
668 | end |
669 | if self.brakeLightsVisibilityWasActive and not self.brakeLightsVisibility then |
670 | self.brakeLightsVisibilityWasActive = false; |
671 | |
672 | local factor = self.maxDecelerationDuringBrake; |
673 | self.maxDecelerationDuringBrake = 0; |
674 | |
675 | local airConsumption = self:getMaximalAirConsumptionPerFullStop(); |
676 | -- print( string.format(" -----> factor = %.2f // %.2f ", factor, airConsumption) ); |
677 | airConsumption = factor * airConsumption; |
678 | self.brakeCompressor.fillLevel = math.max(0, self.brakeCompressor.fillLevel - airConsumption); --implementCount * self.brakeCompressor.capacity * 0.05); |
679 | end |
680 | |
681 | if self.brakeCompressor.fillLevel < self.brakeCompressor.refillFilllevel then |
682 | self.brakeCompressor.doFill = true; |
683 | end |
684 | if self.brakeCompressor.doFill and self.brakeCompressor.fillLevel == self.brakeCompressor.capacity then |
685 | self.brakeCompressor.doFill = false; |
686 | end |
687 | if self.brakeCompressor.doFill then |
688 | self.brakeCompressor.fillLevel = math.min(self.brakeCompressor.capacity, self.brakeCompressor.fillLevel + self.brakeCompressor.fillSpeed * dt); |
689 | end |
690 | |
691 | if Vehicle.debugRendering then |
692 | renderText(0.3, 0.16, getCorrectTextSize(0.02), string.format("brakeCompressor.fillLevel = %.1f", 100*(self.brakeCompressor.fillLevel / self.brakeCompressor.capacity) )); |
693 | end |
694 | |
695 | if not self.brakeCompressor.doFill then |
696 | if self.brakeCompressor.runSoundActive then |
697 | SoundUtil.stopSample(self.sampleBrakeCompressorRun, true); |
698 | SoundUtil.playSample(self.sampleBrakeCompressorStop, 1, 0, nil); |
699 | self.brakeCompressor.startSoundPlayed = false; |
700 | self.brakeCompressor.runSoundActive = false; |
701 | end |
702 | elseif not SoundUtil.isSamplePlaying(self.sampleBrakeCompressorStop, 1.5*dt) then |
703 | if not self.brakeCompressor.startSoundPlayed then |
704 | self.brakeCompressor.startSoundPlayed = true; |
705 | SoundUtil.playSample(self.sampleBrakeCompressorStart, 1, 0, nil); |
706 | else |
707 | if not SoundUtil.isSamplePlaying(self.sampleBrakeCompressorStart, 1.5*dt) and not self.brakeCompressor.runSoundActive then |
708 | self.brakeCompressor.runSoundActive = true; |
709 | SoundUtil.playSample(self.sampleBrakeCompressorRun, 0, 0, nil); |
710 | end |
711 | end |
712 | end |
713 | end |
714 | |
715 | if self.compressionSoundTime <= g_currentMission.time then |
716 | SoundUtil.playSample(self.sampleAirReleaseValve, 1, 0, nil); |
717 | self.compressionSoundTime = g_currentMission.time + math.random(10000, 40000); |
718 | end |
719 | |
720 | if self.sampleCompressedAir.sample ~= nil then |
721 | if self.movingDirection > 0 and self.lastSpeed > self.motor:getMaximumForwardSpeed()*0.0002 then -- faster than 20% of max speed |
722 | if accInput < -0.05 then |
723 | -- play the compressor sound if we drive fast enough and brake |
724 | if not self.compressedAirSoundEnabled then |
725 | SoundUtil.playSample(self.sampleCompressedAir, 1, 0, nil); |
726 | self.compressedAirSoundEnabled = true; |
727 | end |
728 | else |
729 | self.compressedAirSoundEnabled = false; |
730 | end |
731 | end |
732 | end |
733 | |
734 | SoundUtil.stop3DSample(self.sampleMotor); |
735 | SoundUtil.stop3DSample(self.sampleMotorRun); |
736 | SoundUtil.stop3DSample(self.sampleGearbox); |
737 | SoundUtil.stop3DSample(self.sampleRetarder); |
738 | else |
739 | SoundUtil.play3DSample(self.sampleMotor); |
740 | SoundUtil.play3DSample(self.sampleMotorRun); |
741 | end |
742 | |
743 | -- adjust pitch and volume of samples |
744 | if (self.wheels ~= nil and table.getn(self.wheels) > 0) or (self.dummyWheels ~= nil and table.getn(self.dummyWheels) > 0) then |
745 | |
746 | if self.sampleReverseDrive.sample ~= nil then |
747 | if (accInput < 0 or accInput == 0) and (self:getLastSpeed() > 3 and self.movingDirection ~= self.reverserDirection) then |
748 | if self:getIsActiveForSound() then |
749 | SoundUtil.playSample(self.sampleReverseDrive, 0, 0, nil); |
750 | SoundUtil.stop3DSample(self.sampleReverseDrive); |
751 | else |
752 | SoundUtil.play3DSample(self.sampleReverseDrive); |
753 | SoundUtil.stopSample(self.sampleReverseDrive); |
754 | end |
755 | else |
756 | SoundUtil.stopSample(self.sampleReverseDrive); |
757 | SoundUtil.stop3DSample(self.sampleReverseDrive); |
758 | end |
759 | end |
760 | |
761 | local minRpm = self.motor:getMinRpm(); |
762 | local maxRpm = self.motor:getMaxRpm(); |
763 | |
764 | local maxSpeed; |
765 | if self.movingDirection >= 0 then |
766 | maxSpeed = self.motor:getMaximumForwardSpeed()*0.001; |
767 | else |
768 | maxSpeed = self.motor:getMaximumBackwardSpeed()*0.001; |
769 | end |
770 | |
771 | local motorRpm = self.motor:getEqualizedMotorRpm(); |
772 | -- Increase the motor rpm to the max rpm if faster than 75% of the full speed |
773 | if self.movingDirection > 0 and self.lastSpeed > 0.75*maxSpeed and motorRpm < maxRpm then |
774 | motorRpm = motorRpm + (maxRpm - motorRpm) * math.min((self.lastSpeed-0.75*maxSpeed) / (0.25*maxSpeed), 1); |
775 | end |
776 | -- The actual rpm offset is 50% from the motor and 50% from the speed |
777 | local targetRpmOffset = (motorRpm - minRpm)*0.5 + math.min(self.lastSpeed/maxSpeed, 1)*(maxRpm-minRpm)*0.5; |
778 | |
779 | if Vehicle.debugRendering then |
780 | renderText(0.3, 0.14, getCorrectTextSize(0.02), string.format("getLastMotorRpm() = %.2f", self.motor:getLastMotorRpm() )); |
781 | renderText(0.3, 0.12, getCorrectTextSize(0.02), string.format("getEqualziedMotorRpm() = %.2f", self.motor:getEqualizedMotorRpm() )); |
782 | renderText(0.3, 0.10, getCorrectTextSize(0.02), string.format("targetRpmOffset = %.2f", targetRpmOffset )); |
783 | end |
784 | |
785 | |
786 | local alpha = math.pow(0.01, dt*0.001); |
787 | local roundPerMinute = targetRpmOffset + alpha*(self.lastRoundPerMinute-targetRpmOffset); |
788 | |
789 | self.lastRoundPerMinute = roundPerMinute; |
790 | |
791 | local roundPerSecondSmoothed = roundPerMinute / 60; |
792 | |
793 | if self.sampleMotor.sample ~= nil then |
794 | local motorSoundPitch = math.min(self.sampleMotor.pitchOffset + self.motorSoundPitchScale*math.abs(roundPerSecondSmoothed), self.motorSoundPitchMax); |
795 | SoundUtil.setSamplePitch(self.sampleMotor, motorSoundPitch); |
796 | |
797 | local deltaVolume = (self.sampleMotor.volume - self.motorSoundVolumeMin) * math.max(0.0, math.min(1.0, self:getLastSpeed()/self.motorSoundVolumeMinSpeed)) |
798 | SoundUtil.setSampleVolume(self.sampleMotor, math.max(self.motorSoundVolumeMin, self.sampleMotor.volume - deltaVolume)); |
799 | end; |
800 | |
801 | if self.sampleMotorRun.sample ~= nil then |
802 | local motorSoundRunPitch = math.min(self.sampleMotorRun.pitchOffset + self.motorSoundRunPitchScale*math.abs(roundPerSecondSmoothed), self.motorSoundRunPitchMax); |
803 | SoundUtil.setSamplePitch(self.sampleMotorRun, motorSoundRunPitch); |
804 | |
805 | local runVolume = roundPerMinute/(maxRpm - minRpm); |
806 | if math.abs(accInput) < 0.01 or Utils.sign(accInput) ~= self.movingDirection or ptoVolume == 0 then |
807 | runVolume = runVolume * 0.9; |
808 | end; |
809 | runVolume = Utils.clamp(runVolume, 0.0, 1.0); |
810 | |
811 | if Vehicle.debugRendering then |
812 | renderText(0.3, 0.08, getCorrectTextSize(0.02), string.format("runVolume = %.2f", runVolume) ); |
813 | end |
814 | |
815 | if self.sampleMotorLoad.sample == nil then |
816 | SoundUtil.setSampleVolume(self.sampleMotorRun, runVolume * self.sampleMotorRun.volume); |
817 | else |
818 | local motorSoundLoadPitch = math.min(self.sampleMotorLoad.pitchOffset + self.motorSoundLoadPitchScale*math.abs(roundPerSecondSmoothed), self.motorSoundLoadPitchMax); |
819 | SoundUtil.setSamplePitch(self.sampleMotorLoad, motorSoundLoadPitch); |
820 | |
821 | if self.motorSoundLoadFactor < self.actualLoadPercentage then |
822 | self.motorSoundLoadFactor = math.min(self.actualLoadPercentage, self.motorSoundLoadFactor + dt/500); |
823 | elseif self.motorSoundLoadFactor > self.actualLoadPercentage then |
824 | self.motorSoundLoadFactor = math.max(self.actualLoadPercentage, self.motorSoundLoadFactor - dt/750); |
825 | end |
826 | if Vehicle.debugRendering then |
827 | renderText(0.3, 0.06, getCorrectTextSize(0.02), string.format("motorSoundLoadFactor = %.2f", self.motorSoundLoadFactor) ); |
828 | end |
829 | |
830 | SoundUtil.setSampleVolume(self.sampleMotorRun, math.max(self.motorSoundRunMinimalVolumeFactor, (1.0 - self.motorSoundLoadFactor) * runVolume * self.sampleMotorRun.volume) ); |
831 | SoundUtil.setSampleVolume(self.sampleMotorLoad, math.max(self.motorSoundLoadMinimalVolumeFactor, self.motorSoundLoadFactor * runVolume * self.sampleMotorLoad.volume) ); |
832 | end |
833 | end |
834 | |
835 | if self.sampleGearbox.sample ~= nil then |
836 | local speedFactor = Utils.clamp( (self:getLastSpeed() - 1) / math.ceil(self.motor:getMaximumForwardSpeed()*3.6), 0, 1); |
837 | local pitchGearbox = Utils.lerp(self.sampleGearbox.pitchOffset, self.gearboxSoundPitchMax, speedFactor^self.gearboxSoundPitchExponent); |
838 | local volumeGearbox = Utils.lerp(self.sampleGearbox.volume, self.gearboxSoundVolumeMax, speedFactor); |
839 | |
840 | if self.reverserDirection ~= self.movingDirection then |
841 | speedFactor = Utils.clamp( (self:getLastSpeed() - 1) / math.ceil(self.motor:getMaximumBackwardSpeed()*3.6), 0, 1); |
842 | pitchGearbox = Utils.lerp(self.sampleGearbox.pitchOffset, self.gearboxSoundReversePitchMax, speedFactor^self.gearboxSoundPitchExponent); |
843 | volumeGearbox = Utils.lerp(self.sampleGearbox.volume, self.gearboxSoundReverseVolumeMax, speedFactor); |
844 | end |
845 | |
846 | SoundUtil.setSamplePitch(self.sampleGearbox, pitchGearbox); |
847 | SoundUtil.setSampleVolume(self.sampleGearbox, volumeGearbox); |
848 | end |
849 | |
850 | if self.sampleRetarder.sample ~= nil then |
851 | local speedFactor = Utils.clamp( (self:getLastSpeed() - self.retarderSoundMinSpeed) / math.ceil(self.motor:getMaximumForwardSpeed()*3.6), 0, 1); |
852 | local pitchGearbox = Utils.lerp(self.sampleRetarder.pitchOffset, self.retarderSoundPitchMax, speedFactor); |
853 | SoundUtil.setSamplePitch(self.sampleRetarder, pitchGearbox); |
854 | |
855 | local volumeRetarder = Utils.lerp(self.sampleRetarder.volume, self.retarderSoundVolumeMax, speedFactor); |
856 | local targetVolume = 0.0; |
857 | if accInput <= 0.0 and self:getLastSpeed() > self.retarderSoundMinSpeed and self.reverserDirection == self.movingDirection then |
858 | if accInput > -0.9 then |
859 | targetVolume = volumeRetarder; |
860 | else |
861 | targetVolume = self.sampleRetarder.volume; |
862 | end |
863 | end |
864 | |
865 | if self.retarderSoundActualVolume < targetVolume then |
866 | self.retarderSoundActualVolume = math.min(targetVolume, self.retarderSoundActualVolume + dt/self.axisSmoothTime); |
867 | elseif self.retarderSoundActualVolume > targetVolume then |
868 | self.retarderSoundActualVolume = math.max(targetVolume, self.retarderSoundActualVolume - dt/self.axisSmoothTime); |
869 | end |
870 | SoundUtil.setSampleVolume(self.sampleRetarder, self.retarderSoundActualVolume); |
871 | |
872 | if Vehicle.debugRendering then |
873 | renderText(0.8, 0.44, getCorrectTextSize(0.02), string.format("retarderSoundActualVolume = %.2f", self.retarderSoundActualVolume )); |
874 | renderText(0.8, 0.42, getCorrectTextSize(0.02), string.format("getLastSpeed() = %.2f", self:getLastSpeed() )); |
875 | end |
876 | end |
877 | |
878 | if self.sampleBrakeCompressorRun.sample ~= nil then |
879 | local pitchCompressor = math.min(self.sampleBrakeCompressorRun.pitchOffset + self.brakeCompressorRunSoundPitchScale*math.abs(roundPerSecondSmoothed), self.brakeCompressorRunSoundPitchMax); |
880 | SoundUtil.setSamplePitch(self.sampleBrakeCompressorRun, pitchCompressor); |
881 | end |
882 | |
883 | end |
884 | end |
885 | |
886 | if self.isServer then |
887 | if not self:getIsHired() then |
888 | if self.lastMovedDistance > 0 then |
889 | g_currentMission.missionStats:updateStats("traveledDistance", self.lastMovedDistance*0.001); |
890 | end |
891 | end |
892 | |
893 | self:updateFuelUsage(dt) |
894 | end |
895 | end |
896 | end |
901 | function Motorized:updateTick(dt) |
902 | if self.isServer then |
903 | -- compare power |
904 | --local torque,_ = self.motor:getTorque(1, false)*1000; |
905 | --local motorPower = self.motor:getNonClampedMotorRpm()*math.pi/30*torque -- [kW] |
906 | -- |
907 | --self.actualLoadPercentage = motorPower / self.motor.maxMotorPower; |
908 | |
909 | -- compare torque I |
910 | self.actualLoadPercentage = self.motor:getMotorLoad() / self.motor.maxMotorTorque; |
911 | |
912 | -- compare torque II |
913 | --local torque,_ = self.motor:getTorque(1, false)*1000; |
914 | --self.actualLoadPercentage = (self.motor:getMotorLoad() * 1000 / torque); |
915 | |
916 | if self:getIsActive() then |
917 | --print(" --------------------- "); |
918 | --print( string.format(" %.2f <= %.2f / %.2f", self.actualLoadPercentage, motorPower, self.motor.maxMotorPower) ); |
919 | --print( string.format(" %.2f <= %.2f / %.2f", self.actualLoadPercentage, self.motor:getMotorLoad(), self.motor.maxMotorTorque) ); |
920 | --print( string.format(" %.2f <= %.2f / %.2f", self.actualLoadPercentage, self.motor:getMotorLoad() * 1000, torque) ); |
921 | end |
922 | |
923 | local neededPtoTorque = PowerConsumer.getTotalConsumedPtoTorque(self); |
924 | if neededPtoTorque > 0 then |
925 | local ptoLoad = (neededPtoTorque / self.motor.ptoMotorRpmRatio) / self.motor.maxMotorTorque; |
926 | self.actualLoadPercentage = math.min(1.0, self.actualLoadPercentage + ptoLoad); |
927 | end |
928 | |
929 | if math.abs(self.fuelFillLevel-self.sentFuelFillLevel) > 0.001 then |
930 | self:raiseDirtyFlags(self.motorizedDirtyFlag); |
931 | self.sentFuelFillLevel = self.fuelFillLevel; |
932 | end |
933 | |
934 | if self.isMotorStarted and not self.isControlled and not self.isEntered and not self:getIsHired() and not g_currentMission.missionInfo.automaticMotorStartEnabled then |
935 | local isPlayerInRange = false |
936 | local vx, vy, vz = getWorldTranslation(self.rootNode); |
937 | |
938 | for _, player in pairs(g_currentMission.players) do |
939 | if player.isControlled then |
940 | local px, py, pz = getWorldTranslation(player.rootNode); |
941 | local distance = Utils.vector3Length(px-vx, py-vy, pz-vz); |
942 | if distance < 250 then |
943 | isPlayerInRange = true |
944 | break |
945 | end |
946 | end; |
947 | end; |
948 | if not isPlayerInRange then |
949 | for _, steerable in pairs(g_currentMission.steerables) do |
950 | if steerable.isControlled then |
951 | local px, py, pz = getWorldTranslation(steerable.rootNode); |
952 | local distance = Utils.vector3Length(px-vx, py-vy, pz-vz); |
953 | if distance < 250 then |
954 | isPlayerInRange = true |
955 | break |
956 | end |
957 | end; |
958 | end; |
959 | end |
960 | |
961 | if isPlayerInRange then |
962 | self.motorStopTimer = self.motorStopTimerDuration |
963 | else |
964 | self.motorStopTimer = self.motorStopTimer - dt |
965 | if self.motorStopTimer <= 0 then |
966 | self:stopMotor() |
967 | end |
968 | end |
969 | end |
970 | end |
971 | |
972 | if self.isClient then |
973 | if self:getIsMotorStarted() then |
974 | if self.rpmHud ~= nil then |
975 | VehicleHudUtils.setHudValue(self, self.rpmHud, self.motor:getLastMotorRpm(), self.motor:getMaxRpm()); |
976 | end |
977 | if self.speedHud ~= nil then |
978 | local maxSpeed = 30; |
979 | if self.cruiseControl ~= nil then |
980 | maxSpeed = self.cruiseControl.maxSpeed; |
981 | end |
982 | VehicleHudUtils.setHudValue(self, self.speedHud, g_i18n:getSpeed(self:getLastSpeed() * self.speedDisplayScale), g_i18n:getSpeed(maxSpeed)); |
983 | end |
984 | |
985 | if self.exhaustParticleSystems ~= nil then |
986 | for _, ps in pairs(self.exhaustParticleSystems) do |
987 | local scale = Utils.lerp(self.exhaustParticleSystems.minScale, self.exhaustParticleSystems.maxScale, self.motor:getEqualizedMotorRpm() / self.motor:getMaxRpm()); |
988 | ParticleUtil.setEmitCountScale(self.exhaustParticleSystems, scale); |
989 | ParticleUtil.setParticleLifespan(ps, ps.originalLifespan * scale) |
990 | end |
991 | end |
992 | |
993 | if self.exhaustFlap ~= nil then |
994 | local minRandom = -0.1; |
995 | local maxRandom = 0.1; |
996 | local angle = Utils.lerp(minRandom, maxRandom, math.random()) + self.exhaustFlap.maxRot * (self.motor:getEqualizedMotorRpm() / self.motor:getMaxRpm()); |
997 | angle = Utils.clamp(angle, 0, self.exhaustFlap.maxRot); |
998 | setRotation(self.exhaustFlap.node, angle, 0, 0); |
999 | end |
1000 | |
1001 | if self.exhaustEffects ~= nil then |
1002 | local lastSpeed = self:getLastSpeed(); |
1003 | |
1004 | self.currentDirection = {localDirectionToWorld(self.rootNode, 0, 0, 1)}; |
1005 | if self.lastDirection == nil then |
1006 | self.lastDirection = self.currentDirection; |
1007 | end |
1008 | |
1009 | local x,_,z = worldDirectionToLocal(self.rootNode, self.lastDirection[1], self.lastDirection[2], self.lastDirection[3]); |
1010 | local dot = z; |
1011 | dot = dot / Utils.vector2Length(x,z); |
1012 | local angle = math.acos(dot); |
1013 | if x < 0 then |
1014 | angle = -angle; |
1015 | end |
1016 | local steeringPercent = math.abs((angle / dt) / self.exhaustEffectMaxSteeringSpeed); |
1017 | self.lastDirection = self.currentDirection; |
1018 | |
1019 | for _, effect in pairs(self.exhaustEffects) do |
1020 | local rpmScale = self.motor:getEqualizedMotorRpm() / self.motor:getMaxRpm(); |
1021 | local scale = Utils.lerp(effect.minRpmScale, effect.maxRpmScale, rpmScale); |
1022 | local forwardXRot = 0; |
1023 | local forwardZRot = 0; |
1024 | local steerXRot = 0; |
1025 | local steerZRot = 0; |
1026 | |
1027 | local r = Utils.lerp(effect.minRpmColor[1], effect.maxRpmColor[1], rpmScale); |
1028 | local g = Utils.lerp(effect.minRpmColor[2], effect.maxRpmColor[2], rpmScale); |
1029 | local b = Utils.lerp(effect.minRpmColor[3], effect.maxRpmColor[3], rpmScale); |
1030 | local a = Utils.lerp(effect.minRpmColor[4], effect.maxRpmColor[4], rpmScale); |
1031 | setShaderParameter(effect.effectNode, "exhaustColor", r, g, b, a, false); |
1032 | |
1033 | -- speed rotation |
1034 | if self.movingDirection == 1 then |
1035 | local percent = Utils.clamp(lastSpeed/effect.maxForwardSpeed, 0, 1); |
1036 | forwardXRot = effect.xzRotationsForward[1] * percent; |
1037 | forwardZRot = effect.xzRotationsForward[2] * percent; |
1038 | elseif self.movingDirection == -1 then |
1039 | local percent = Utils.clamp(lastSpeed/effect.maxBackwardSpeed, 0, 1); |
1040 | forwardXRot = effect.xzRotationsBackward[1] * percent; |
1041 | forwardZRot = effect.xzRotationsBackward[2] * percent; |
1042 | end |
1043 | |
1044 | -- steering rotation |
1045 | if angle > 0 then |
1046 | steerXRot = effect.xzRotationsRight[1] * steeringPercent; |
1047 | steerZRot = effect.xzRotationsRight[2] * steeringPercent; |
1048 | elseif angle < 0 then |
1049 | steerXRot = effect.xzRotationsLeft[1] * steeringPercent; |
1050 | steerZRot = effect.xzRotationsLeft[2] * steeringPercent; |
1051 | end |
1052 | -- target rotations |
1053 | local targetXRot = effect.xzRotationsOffset[1] + forwardXRot + steerXRot; |
1054 | local targetZRot = effect.xzRotationsOffset[2] + forwardZRot + steerZRot; |
1055 | |
1056 | -- damping |
1057 | if targetXRot > effect.xRot then |
1058 | effect.xRot = math.min(effect.xRot + 0.003*dt, targetXRot); |
1059 | else |
1060 | effect.xRot = math.max(effect.xRot - 0.003*dt, targetXRot); |
1061 | end |
1062 | if targetZRot > effect.xRot then |
1063 | effect.zRot = math.min(effect.zRot + 0.003*dt, targetZRot); |
1064 | else |
1065 | effect.zRot = math.max(effect.zRot - 0.003*dt, targetZRot); |
1066 | end |
1067 | setShaderParameter(effect.effectNode, "param", effect.xRot, effect.zRot, 0, scale, false); |
1068 | end |
1069 | end |
1070 | end |
1071 | end |
1072 | |
1073 | if self.isFuelFilling then |
1074 | if self.isServer then |
1075 | local delta = 0; |
1076 | if self.fuelFillTrigger ~= nil then |
1077 | delta = self.fuelFillLitersPerSecond*dt*0.001; |
1078 | delta = self.fuelFillTrigger:fillFuel(self, delta); |
1079 | end |
1080 | if delta <= 0.001 then |
1081 | self:setIsFuelFilling(false); |
1082 | end |
1083 | end |
1084 | end |
1085 | end |