1058 | function Motorized:loadConsumerConfiguration(xmlFile, consumerIndex) |
1059 | local key = string.format("vehicle.motorized.consumerConfigurations.consumerConfiguration(%d)", consumerIndex-1) |
1060 | |
1061 | local spec = self.spec_motorized |
1062 | |
1063 | local fallbackConfigKey = "vehicle.motorized.consumers" |
1064 | local fallbackOldKey = nil |
1065 | |
1066 | spec.consumers = {} |
1067 | spec.consumersByFillTypeName = {} |
1068 | spec.consumersByFillType = {} |
1069 | |
1070 | if not hasXMLProperty(xmlFile, key) then |
1071 | return |
1072 | end |
1073 | |
1074 | local i = 0 |
1075 | while true do |
1076 | local consumerKey = string.format(".consumer(%d)", i) |
1077 | if not hasXMLProperty(xmlFile, key..consumerKey) then |
1078 | break |
1079 | end |
1080 | local consumer = {} |
1081 | consumer.fillUnitIndex = ConfigurationUtil.getConfigurationValue(xmlFile, key, consumerKey, "#fillUnitIndex", getXMLInt, 1, fallbackConfigKey, fallbackOldKey) |
1082 | |
1083 | local fillTypeName = ConfigurationUtil.getConfigurationValue(xmlFile, key, consumerKey, "#fillType", getXMLString, "consumer", fallbackConfigKey, fallbackOldKey) |
1084 | consumer.fillType = g_fillTypeManager:getFillTypeIndexByName(fillTypeName) |
1085 | |
1086 | local fillUnit = self:getFillUnitByIndex(consumer.fillUnitIndex) |
1087 | if fillUnit ~= nil then |
1088 | --fill fillUnit on start |
1089 | fillUnit.startFillLevel = fillUnit.capacity |
1090 | fillUnit.startFillTypeIndex = consumer.fillType |
1091 | else |
1092 | g_logManager:xmlWarning(self.configFileName, "Unknown fillUnit '%d' for consumer '%s'", consumer.fillUnitIndex, key..consumerKey) |
1093 | break |
1094 | end |
1095 | |
1096 | local usage = ConfigurationUtil.getConfigurationValue(xmlFile, key, consumerKey, "#usage", getXMLFloat, 1.0, fallbackConfigKey, fallbackOldKey) |
1097 | consumer.permanentConsumption = ConfigurationUtil.getConfigurationValue(xmlFile, key, consumerKey, "#permanentConsumption", getXMLBool, true, fallbackConfigKey, fallbackOldKey) |
1098 | if consumer.permanentConsumption then |
1099 | consumer.usage = usage / (60*60*1000) -- from l/h to l/ms |
1100 | else |
1101 | consumer.usage = usage |
1102 | end |
1103 | consumer.refillLitersPerSecond = ConfigurationUtil.getConfigurationValue(xmlFile, key, consumerKey, "#refillLitersPerSecond", getXMLFloat, 0, fallbackConfigKey, fallbackOldKey) |
1104 | consumer.refillCapacityPercentage = ConfigurationUtil.getConfigurationValue(xmlFile, key, consumerKey, "#refillCapacityPercentage", getXMLFloat, 0, fallbackConfigKey, fallbackOldKey) |
1105 | |
1106 | table.insert(spec.consumers, consumer) |
1107 | spec.consumersByFillTypeName[fillTypeName] = consumer |
1108 | spec.consumersByFillType[consumer.fillType] = consumer |
1109 | i = i + 1 |
1110 | end |
1111 | end |
923 | function Motorized:loadExhaustEffects(xmlFile) |
924 | local spec = self.spec_motorized |
925 | |
926 | spec.exhaustParticleSystems = {} |
927 | local exhaustParticleSystemCount = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.motorized.exhaustParticleSystems#count"), 0) |
928 | for i=1, exhaustParticleSystemCount do |
929 | local namei = string.format("vehicle.motorized.exhaustParticleSystems.exhaustParticleSystem%d", i) |
930 | local ps = {} |
931 | ParticleUtil.loadParticleSystem(xmlFile, ps, namei, self.components, false, nil, self.baseDirectory) |
932 | ps.minScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorized.exhaustParticleSystems#minScale"), 0.5) |
933 | ps.maxScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorized.exhaustParticleSystems#maxScale"), 1) |
934 | table.insert(spec.exhaustParticleSystems, ps) |
935 | end |
936 | if #spec.exhaustParticleSystems == 0 then |
937 | spec.exhaustParticleSystems = nil |
938 | end |
939 | |
940 | XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, "vehicle.motorized.exhaustFlap#index", "vehicle.motorized.exhaustFlap#node") --FS17 to FS19 |
941 | |
942 | local exhaustFlapIndex = getXMLString(xmlFile, "vehicle.motorized.exhaustFlap#node") |
943 | if exhaustFlapIndex ~= nil then |
944 | spec.exhaustFlap = {} |
945 | spec.exhaustFlap.node = I3DUtil.indexToObject(self.components, exhaustFlapIndex, self.i3dMappings) |
946 | spec.exhaustFlap.maxRot = MathUtil.degToRad(Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorized.exhaustFlap#maxRot"),0)) |
947 | end |
948 | |
949 | spec.exhaustEffects = {} |
950 | local i = 0 |
951 | while true do |
952 | local key = string.format("vehicle.motorized.exhaustEffects.exhaustEffect(%d)", i) |
953 | if not hasXMLProperty(xmlFile, key) then |
954 | break |
955 | end |
956 | |
957 | XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key.."#index", key.."#node") --FS17 to FS19 |
958 | |
959 | local linkNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#node"), self.i3dMappings) |
960 | local filename = getXMLString(xmlFile, key .. "#filename") |
961 | if filename ~= nil and linkNode ~= nil then |
962 | local i3dNode = g_i3DManager:loadSharedI3DFile(filename, self.baseDirectory, false, false, false) |
963 | if i3dNode ~= 0 then |
964 | local node = getChildAt(i3dNode, 0) |
965 | if getHasShaderParameter(node, "param") then |
966 | local effect = {} |
967 | effect.effectNode = node |
968 | effect.node = linkNode |
969 | effect.filename = filename |
970 | link(effect.node, effect.effectNode) |
971 | setVisibility(effect.effectNode, false) |
972 | delete(i3dNode) |
973 | |
974 | effect.minRpmColor = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#minRpmColor"), "0 0 0 1"), 4) |
975 | effect.maxRpmColor = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#maxRpmColor"), "0.0384 0.0359 0.0627 2.0"), 4) |
976 | effect.minRpmScale = Utils.getNoNil(getXMLFloat(xmlFile, key.."#minRpmScale"), 0.25) |
977 | effect.maxRpmScale = Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxRpmScale"), 0.95) |
978 | effect.maxForwardSpeed = Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxForwardSpeed"), math.ceil(spec.motor:getMaximumForwardSpeed()*3.6)) |
979 | effect.maxBackwardSpeed = Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxBackwardSpeed"), math.ceil(spec.motor:getMaximumBackwardSpeed()*3.6)) |
980 | |
981 | effect.xzRotationsOffset = StringUtil.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#xzRotationsOffset"), "0 0"), 2) |
982 | effect.xzRotationsForward = StringUtil.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#xzRotationsForward"), "0 0"), 2) |
983 | effect.xzRotationsBackward = StringUtil.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#xzRotationsBackward"), "0 0"), 2) |
984 | effect.xzRotationsLeft = StringUtil.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#xzRotationsLeft"), "0 0"), 2) |
985 | effect.xzRotationsRight = StringUtil.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#xzRotationsRight"), "0 0"), 2) |
986 | |
987 | effect.xRot = 0 |
988 | effect.zRot = 0 |
989 | |
990 | table.insert(spec.exhaustEffects, effect) |
991 | end |
992 | end |
993 | end |
994 | i = i + 1 |
995 | end |
996 | spec.exhaustEffectMaxSteeringSpeed = 0.001 |
997 | end |
749 | function Motorized:loadMotor(xmlFile, motorId) |
750 | local key, motorId = ConfigurationUtil.getXMLConfigurationKey(xmlFile, motorId, "vehicle.motorized.motorConfigurations.motorConfiguration", "vehicle.motorized", "motor") |
751 | |
752 | local spec = self.spec_motorized |
753 | |
754 | local fallbackConfigKey = "vehicle.motorized.motorConfigurations.motorConfiguration(0)" |
755 | local fallbackOldKey = "vehicle" |
756 | |
757 | spec.motorType = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#type", getXMLString, "vehicle", fallbackConfigKey, fallbackOldKey) |
758 | spec.motorStartAnimation = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#startAnimationName", getXMLString, "vehicle", fallbackConfigKey, fallbackOldKey) |
759 | |
760 | spec.fuelCapacity = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".fuelCapacity", "", getXMLFloat, 500, fallbackConfigKey, fallbackOldKey) |
761 | spec.consumerConfigurationIndex = ConfigurationUtil.getConfigurationValue(xmlFile, key, "#consumerConfigurationIndex", "", getXMLInt, 1, fallbackConfigKey, fallbackOldKey) |
762 | |
763 | local wheelKey, _ = ConfigurationUtil.getXMLConfigurationKey(xmlFile, self.configurations["wheel"], "vehicle.wheels.wheelConfigurations.wheelConfiguration", "vehicle.wheels", "wheels") |
764 | |
765 | ObjectChangeUtil.updateObjectChanges(xmlFile, "vehicle.motorized.motorConfigurations.motorConfiguration", motorId, self.components, self) |
766 | |
767 | local motorMinRpm = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#minRpm", getXMLFloat, 1000, fallbackConfigKey, fallbackOldKey) |
768 | local motorMaxRpm = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#maxRpm", getXMLFloat, 1800, fallbackConfigKey, fallbackOldKey) |
769 | local minSpeed = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#minSpeed", getXMLFloat, 1, fallbackConfigKey, fallbackOldKey) |
770 | local maxForwardSpeed = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#maxForwardSpeed", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey) |
771 | local maxBackwardSpeed = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#maxBackwardSpeed", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey) |
772 | if maxForwardSpeed ~= nil then |
773 | maxForwardSpeed = maxForwardSpeed/3.6 |
774 | end |
775 | if maxBackwardSpeed ~= nil then |
776 | maxBackwardSpeed = maxBackwardSpeed/3.6 |
777 | end |
778 | |
779 | local maxWheelSpeed = ConfigurationUtil.getConfigurationValue(xmlFile, wheelKey, ".wheels", "#maxForwardSpeed", getXMLFloat, nil, nil, "vehicle.wheels") |
780 | if maxWheelSpeed ~= nil then |
781 | maxForwardSpeed = maxWheelSpeed/3.6 |
782 | end |
783 | local accelerationLimit = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#accelerationLimit", getXMLFloat, 2.0, fallbackConfigKey, fallbackOldKey) -- m/s^2 |
784 | |
785 | local brakeForce = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#brakeForce", getXMLFloat, 10, fallbackConfigKey, fallbackOldKey)*2 |
786 | local lowBrakeForceScale = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#lowBrakeForceScale", getXMLFloat, 0.5, fallbackConfigKey, fallbackOldKey) |
787 | local lowBrakeForceSpeedLimit = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#lowBrakeForceSpeedLimit", getXMLFloat, 1, fallbackConfigKey, fallbackOldKey)/3600 |
788 | local torqueScale = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#torqueScale", getXMLFloat, 1, fallbackConfigKey, fallbackOldKey) |
789 | local ptoMotorRpmRatio = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#ptoMotorRpmRatio", getXMLFloat, 4, fallbackConfigKey, fallbackOldKey) |
790 | |
791 | local minForwardGearRatio = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".transmission", "#minForwardGearRatio", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey) |
792 | local maxForwardGearRatio = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".transmission", "#maxForwardGearRatio", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey) |
793 | local minBackwardGearRatio = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".transmission", "#minBackwardGearRatio", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey) |
794 | local maxBackwardGearRatio = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".transmission", "#maxBackwardGearRatio", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey) |
795 | local gearChangeTime = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".transmission", "#gearChangeTime", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey) |
796 | local autoGearChangeTime = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".transmission", "#autoGearChangeTime", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey) |
797 | local axleRatio = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".transmission", "#axleRatio", getXMLFloat, 1.0, fallbackConfigKey, fallbackOldKey) |
798 | |
799 | |
800 | if maxForwardGearRatio == nil or minForwardGearRatio == nil then |
801 | minForwardGearRatio = nil |
802 | maxForwardGearRatio = nil |
803 | else |
804 | minForwardGearRatio = minForwardGearRatio * axleRatio |
805 | maxForwardGearRatio = maxForwardGearRatio * axleRatio |
806 | end |
807 | if minBackwardGearRatio == nil or maxBackwardGearRatio == nil then |
808 | minBackwardGearRatio = nil |
809 | maxBackwardGearRatio = nil |
810 | else |
811 | minBackwardGearRatio = minBackwardGearRatio * axleRatio |
812 | maxBackwardGearRatio = maxBackwardGearRatio * axleRatio |
813 | end |
814 | |
815 | -- Read forward gear ratios |
816 | local forwardGearRatios |
817 | if minForwardGearRatio == nil then |
818 | forwardGearRatios = self:loadGears(xmlFile, "forwardGear", key, motorId, fallbackConfigKey, fallbackOldKey, motorMaxRpm, axleRatio) |
819 | if forwardGearRatios == nil then |
820 | print("Warning: Missing forward gear ratios for motor in '"..self.configFileName.."'!") |
821 | forwardGearRatios = {1} |
822 | end |
823 | end |
824 | -- Read backward gear ratios |
825 | local backwardGearRatios |
826 | if minBackwardGearRatio == nil then |
827 | backwardGearRatios = self:loadGears(xmlFile, "backwardGear", key, motorId, fallbackConfigKey, fallbackOldKey, motorMaxRpm, axleRatio) |
828 | if backwardGearRatios == nil then |
829 | print("Warning: Missing backward gear ratios for motor in '"..self.configFileName.."'!") |
830 | backwardGearRatios = {1} |
831 | end |
832 | end |
833 | |
834 | --local maxTorque = 0 |
835 | local torqueCurve = AnimCurve:new(linearInterpolator1) |
836 | local torqueI = 0 |
837 | local torqueBase = fallbackOldKey..".motor.torque" -- fallback to old motor setup |
838 | if key ~= nil and hasXMLProperty(xmlFile, fallbackConfigKey..".motor.torque(0)") then -- using default motor configuration |
839 | torqueBase = fallbackConfigKey..".motor.torque" |
840 | end |
841 | if key ~= nil and hasXMLProperty(xmlFile, key..".motor.torque(0)") then -- using selected motor configuration |
842 | torqueBase = key..".motor.torque" |
843 | end |
844 | |
845 | while true do |
846 | local torqueKey = string.format(torqueBase.."(%d)", torqueI) |
847 | local normRpm = getXMLFloat(xmlFile, torqueKey.."#normRpm") |
848 | local rpm |
849 | if normRpm == nil then |
850 | rpm = getXMLFloat(xmlFile, torqueKey.."#rpm") |
851 | else |
852 | rpm = normRpm * motorMaxRpm |
853 | end |
854 | local torque = getXMLFloat(xmlFile, torqueKey.."#torque") |
855 | if torque == nil or rpm == nil then |
856 | break |
857 | end |
858 | torqueCurve:addKeyframe({torque*torqueScale, time = rpm}) |
859 | torqueI = torqueI +1 |
860 | end |
861 | |
862 | spec.motor = VehicleMotor:new(self, motorMinRpm, motorMaxRpm, maxForwardSpeed, maxBackwardSpeed, torqueCurve, brakeForce, forwardGearRatios, backwardGearRatios, minForwardGearRatio, maxForwardGearRatio, minBackwardGearRatio, maxBackwardGearRatio, ptoMotorRpmRatio, minSpeed) |
863 | |
864 | local rotInertia = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#rotInertia", getXMLFloat, spec.motor:getRotInertia(), fallbackConfigKey, fallbackOldKey) |
865 | local dampingRateFullThrottle = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#dampingRateFullThrottle", getXMLFloat, spec.motor:getDampingRateFullThrottle(), fallbackConfigKey, fallbackOldKey) |
866 | local dampingRateZeroThrottleClutchEngaged = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#dampingRateZeroThrottleClutchEngaged", getXMLFloat, spec.motor:getDampingRateZeroThrottleClutchEngaged(), fallbackConfigKey, fallbackOldKey) |
867 | local dampingRateZeroThrottleClutchDisengaged = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#dampingRateZeroThrottleClutchDisengaged", getXMLFloat, spec.motor:getDampingRateZeroThrottleClutchDisengaged(), fallbackConfigKey, fallbackOldKey) |
868 | spec.motor:setRotInertia(rotInertia) |
869 | spec.motor:setDampingRateFullThrottle(dampingRateFullThrottle) |
870 | spec.motor:setDampingRateZeroThrottleClutchEngaged(dampingRateZeroThrottleClutchEngaged) |
871 | spec.motor:setDampingRateZeroThrottleClutchDisengaged(dampingRateZeroThrottleClutchDisengaged) |
872 | spec.motor:setLowBrakeForce(lowBrakeForceScale, lowBrakeForceSpeedLimit) |
873 | spec.motor:setAccelerationLimit(accelerationLimit) |
874 | |
875 | local motorRotationAccelerationLimit = ConfigurationUtil.getConfigurationValue(xmlFile, key, ".motor", "#rpmSpeedLimit", getXMLFloat, nil, fallbackConfigKey, fallbackOldKey) -- xml: rpm/s -> converted to rad/s^2 |
876 | if motorRotationAccelerationLimit ~= nil then |
877 | motorRotationAccelerationLimit = motorRotationAccelerationLimit * math.pi/30 |
878 | spec.motor:setMotorRotationAccelerationLimit(motorRotationAccelerationLimit) |
879 | end |
880 | |
881 | if gearChangeTime ~= nil then |
882 | spec.motor:setGearChangeTime(gearChangeTime*1000) |
883 | end |
884 | if autoGearChangeTime ~= nil then |
885 | spec.motor:setAutoGearChangeTime(autoGearChangeTime*1000) |
886 | end |
887 | end |
1003 | function Motorized:loadSounds(xmlFile, motorId) |
1004 | if self.isClient then |
1005 | local spec = self.spec_motorized |
1006 | |
1007 | local baseString = "vehicle.motorized.sounds" |
1008 | spec.samples = {} |
1009 | |
1010 | spec.samples.motorStart = g_soundManager:loadSampleFromXML(xmlFile, baseString, "motorStart", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
1011 | spec.samples.motorStop = g_soundManager:loadSampleFromXML(xmlFile, baseString, "motorStop", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
1012 | spec.samples.gearbox = g_soundManager:loadSampleFromXML(xmlFile, baseString, "gearbox", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
1013 | spec.samples.retarder = g_soundManager:loadSampleFromXML(xmlFile, baseString, "retarder", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
1014 | |
1015 | spec.motorSamples = {} |
1016 | local i = 0 |
1017 | while true do |
1018 | local sample = g_soundManager:loadSampleFromXML(xmlFile, baseString, string.format("motor(%d)", i), self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
1019 | if sample == nil then |
1020 | break |
1021 | end |
1022 | |
1023 | table.insert(spec.motorSamples, sample) |
1024 | i = i + 1 |
1025 | end |
1026 | |
1027 | spec.samples.airCompressorStart = g_soundManager:loadSampleFromXML(xmlFile, baseString, "airCompressorStart", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
1028 | spec.samples.airCompressorStop = g_soundManager:loadSampleFromXML(xmlFile, baseString, "airCompressorStop", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
1029 | spec.samples.airCompressorRun = g_soundManager:loadSampleFromXML(xmlFile, baseString, "airCompressorRun", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
1030 | |
1031 | spec.samples.compressedAir = g_soundManager:loadSampleFromXML(xmlFile, baseString, "compressedAir", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
1032 | if spec.samples.compressedAir ~= nil then |
1033 | spec.samples.compressedAir.brakeTime = 0 |
1034 | spec.samples.compressedAir.lastBrakeTime = 0 |
1035 | end |
1036 | |
1037 | spec.samples.airRelease = g_soundManager:loadSampleFromXML(xmlFile, baseString, "airRelease", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
1038 | |
1039 | spec.samples.reverseDrive = g_soundManager:loadSampleFromXML(xmlFile, baseString, "reverseDrive", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
1040 | spec.reverseDriveThreshold = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorized.reverseDriveSound#threshold"), 4) |
1041 | |
1042 | spec.brakeCompressor = {} |
1043 | spec.brakeCompressor.capacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorized.brakeCompressor#capacity"), 6) |
1044 | spec.brakeCompressor.refillFilllevel = math.min(spec.brakeCompressor.capacity, Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorized.brakeCompressor#refillFillLevel"), spec.brakeCompressor.capacity/2)) |
1045 | spec.brakeCompressor.fillSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorized.brakeCompressor#fillSpeed"), 0.6) / 1000 |
1046 | spec.brakeCompressor.fillLevel = 0 |
1047 | spec.brakeCompressor.doFill = true |
1048 | |
1049 | spec.isBrakeSamplePlaying = false |
1050 | spec.samples.brake = g_soundManager:loadSampleFromXML(xmlFile, baseString, "brake", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
1051 | |
1052 | spec.compressionSoundTime = 0 |
1053 | end |
1054 | end |
111 | function Motorized:onLoad(savegame) |
112 | local spec = self.spec_motorized |
113 | |
114 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.turnedOnRotationNodes.turnedOnRotationNode#type", "vehicle.motor.animationNodes.animationNode", "motor") --FS17 to FS19 |
115 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.differentialConfigurations", "vehicle.motorized.differentialConfigurations") --FS17 to FS19 |
116 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.motorConfigurations", "vehicle.motorized.motorConfigurations") --FS17 to FS19 |
117 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.fuelCapacity", "vehicle.fillUnit") --FS17 to FS19 |
118 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.maximalAirConsumptionPerFullStop", "vehicle.motorized.consumerConfigurations.consumerConfiguration.consumer(with fill type 'air')#usage (is now in usage per second at full brake power)") --FS17 to FS19 |
119 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.indoorHud.rpm", "vehicle.motorized.dashboards.dashboard with valueType 'rpm'") --FS17 to FS19 |
120 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.indoorHud.speed", "vehicle.motorized.dashboards.dashboard with valueType 'speed'") --FS17 to FS19 |
121 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.indoorHud.fuelUsage", "vehicle.motorized.dashboards.dashboard with valueType 'fuelUsage'") --FS17 to FS19 |
122 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.indoorHud.fuel", "fillUnit.dashboard with valueType 'fillLevel'") --FS17 to FS19 |
123 | |
124 | spec.motorizedNode = nil |
125 | for _, component in pairs(self.components) do |
126 | if component.motorized then |
127 | spec.motorizedNode = component.node |
128 | break |
129 | end |
130 | end |
131 | |
132 | self:loadDifferentials(self.xmlFile, self.differentialIndex) |
133 | self:loadMotor(self.xmlFile, self.configurations["motor"]) |
134 | self:loadSounds(self.xmlFile, self.configurations["motor"]) |
135 | |
136 | self:loadConsumerConfiguration(self.xmlFile, spec.consumerConfigurationIndex) |
137 | |
138 | if self.isClient then |
139 | self:loadExhaustEffects(self.xmlFile) |
140 | end |
141 | |
142 | spec.stopMotorOnLeave = true |
143 | |
144 | spec.motorStartDuration = 0 |
145 | if spec.samples ~= nil and spec.samples.motorStart ~= nil then |
146 | spec.motorStartDuration = spec.samples.motorStart.duration |
147 | end |
148 | spec.motorStartDuration = Utils.getNoNil(Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.motorized.motorStartDuration"), spec.motorStartDuration), 0) |
149 | spec.consumersEmptyWarning = g_i18n:getText(Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.motorized#consumersEmptyWarning"), "warning_motorFuelEmpty"), self.customEnvironment) |
150 | |
151 | spec.turnOnText = g_i18n:getText(Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.motorized#turnOnText"), "action_startMotor"), self.customEnvironment) |
152 | spec.turnOffText = g_i18n:getText(Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.motorized#turnOffText"), "action_stopMotor"), self.customEnvironment) |
153 | |
154 | spec.speedDisplayScale = 1 |
155 | spec.motorStartTime = 0 |
156 | spec.lastRoundPerMinute = 0 |
157 | spec.actualLoadPercentage = 0 |
158 | spec.smoothedLoadPercentage = 0 |
159 | spec.maxDecelerationDuringBrake = 0 |
160 | spec.showTurnOnMotorWarning = 0 |
161 | |
162 | spec.isMotorStarted = false |
163 | spec.motorStopTimerDuration = g_gameSettings:getValue("motorStopTimerDuration") |
164 | spec.motorStopTimer = spec.motorStopTimerDuration |
165 | |
166 | spec.motorTemperature = {} |
167 | spec.motorTemperature.value = 20 |
168 | spec.motorTemperature.valueSend = 20 |
169 | spec.motorTemperature.valueMax = 120 |
170 | spec.motorTemperature.valueMin = 20 |
171 | spec.motorTemperature.heatingPerMS = 1.5 / 1000 -- delta °C per ms, at full load |
172 | spec.motorTemperature.coolingByWindPerMS = 1.00 / 1000 |
173 | |
174 | spec.motorFan = {} |
175 | spec.motorFan.enabled = false |
176 | spec.motorFan.enableTemperature = 95 |
177 | spec.motorFan.disableTemperature = 85 |
178 | spec.motorFan.coolingPerMS = 3.0 / 1000 |
179 | |
180 | spec.lastFuelUsage = 0 |
181 | spec.lastFuelUsageDisplay = 0 |
182 | spec.lastFuelUsageDisplayTime = 0 |
183 | spec.fuelUsageBuffer = ValueBuffer:new(250) |
184 | spec.lastDefUsage = 0 |
185 | spec.lastAirUsage = 0 |
186 | spec.lastVehicleDamage = 0 |
187 | |
188 | if self.loadDashboardsFromXML ~= nil then |
189 | self:loadDashboardsFromXML(self.xmlFile, "vehicle.motorized.dashboards", {valueTypeToLoad = "rpm", |
190 | valueObject = spec.motor, |
191 | valueFunc = "getEqualizedMotorRpm", |
192 | minFunc = 0, |
193 | maxFunc = "getMaxRpm"}) |
194 | |
195 | self:loadDashboardsFromXML(self.xmlFile, "vehicle.motorized.dashboards", {valueTypeToLoad = "speed", |
196 | valueObject = self, |
197 | valueFunc = "getLastSpeed", |
198 | minFunc = 0, |
199 | maxFunc = self:getMotor():getMaximumForwardSpeed()*3.6}) |
200 | |
201 | self:loadDashboardsFromXML(self.xmlFile, "vehicle.motorized.dashboards", {valueTypeToLoad = "speedDir", |
202 | valueObject = self, |
203 | valueFunc = Motorized.getDashboardSpeedDir, |
204 | minFunc = -self:getMotor():getMaximumBackwardSpeed()*3.6, |
205 | maxFunc = self:getMotor():getMaximumForwardSpeed()*3.6, |
206 | centerFunc = 0}) |
207 | |
208 | self:loadDashboardsFromXML(self.xmlFile, "vehicle.motorized.dashboards", {valueTypeToLoad = "fuelUsage", |
209 | valueObject = spec, |
210 | valueFunc = "lastFuelUsageDisplay"}) |
211 | |
212 | self:loadDashboardsFromXML(self.xmlFile, "vehicle.motorized.dashboards", {valueTypeToLoad = "motorTemperature", |
213 | valueObject = spec.motorTemperature, |
214 | valueFunc = "value", |
215 | minFunc = "valueMin", |
216 | maxFunc = "valueMax"}) |
217 | |
218 | self:loadDashboardsFromXML(self.xmlFile, "vehicle.motorized.dashboards", {valueTypeToLoad = "motorTemperatureWarning", |
219 | valueObject = spec.motorTemperature, |
220 | valueFunc = "value", |
221 | additionalAttributesFunc = Dashboard.warningAttributes, |
222 | stateFunc = Dashboard.warningState}) |
223 | end |
224 | |
225 | spec.animationNodes = g_animationManager:loadAnimations(self.xmlFile, "vehicle.motorized.animationNodes", self.components, self, self.i3dMappings) |
226 | end |
333 | function Motorized:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
334 | local spec = self.spec_motorized |
335 | |
336 | local accInput = 0 |
337 | if self.getAxisForward ~= nil then |
338 | accInput = self:getAxisForward() |
339 | end |
340 | |
341 | if self:getIsMotorStarted() then |
342 | spec.motor:update(dt) |
343 | if self.getCruiseControlState ~= nil then |
344 | if self:getCruiseControlState() ~= Drivable.CRUISECONTROL_STATE_OFF then |
345 | accInput = 1 |
346 | end |
347 | end |
348 | |
349 | if self.isServer then |
350 | self:updateConsumers(dt, accInput) |
351 | |
352 | -- update motor properties on damage change to update the torque reduction |
353 | local damage = self:getVehicleDamage() |
354 | if math.abs(damage - spec.lastVehicleDamage) > 0.05 then |
355 | self:updateMotorProperties() |
356 | spec.lastVehicleDamage = self:getVehicleDamage() |
357 | end |
358 | end |
359 | |
360 | if self.isClient then |
361 | -- update sounds |
362 | local samples = spec.samples |
363 | if g_soundManager:getIsSamplePlaying(spec.motorSamples[1], 1.5*dt) then |
364 | -- air compressor fill sound |
365 | if samples.airCompressorStart ~= nil and samples.airCompressorStop ~= nil and samples.airCompressorRun ~= nil then |
366 | if spec.consumersByFillTypeName ~= nil and spec.consumersByFillTypeName.air ~= nil then |
367 | local consumer = spec.consumersByFillTypeName.air |
368 | |
369 | if not consumer.doRefill then |
370 | if g_soundManager:getIsSamplePlaying(samples.airCompressorRun) then |
371 | g_soundManager:stopSample(samples.airCompressorRun) |
372 | g_soundManager:playSample(samples.airCompressorStop) |
373 | end |
374 | else |
375 | if not g_soundManager:getIsSamplePlaying(samples.airCompressorRun) then |
376 | if not g_soundManager:getIsSamplePlaying(samples.airCompressorStart, 1.5*dt) and spec.brakeCompressor.playSampleRunTime == nil then |
377 | g_soundManager:playSample(samples.airCompressorStart) |
378 | spec.playSampleRunTime = g_currentMission.time + samples.airCompressorStart.duration |
379 | end |
380 | if not g_soundManager:getIsSamplePlaying(samples.airCompressorStart) then |
381 | spec.brakeCompressor.playSampleRunTime = nil |
382 | g_soundManager:stopSample(samples.airCompressorStart) |
383 | g_soundManager:playSample(samples.airCompressorRun) |
384 | end |
385 | end |
386 | end |
387 | end |
388 | end |
389 | |
390 | -- random zsch sound |
391 | if spec.compressionSoundTime <= g_currentMission.time then |
392 | g_soundManager:playSample(samples.airRelease) |
393 | spec.compressionSoundTime = g_currentMission.time + math.random(10000, 40000) |
394 | end |
395 | |
396 | local isBraking = self:getDecelerationAxis() > 0 and self:getLastSpeed() > 1 |
397 | |
398 | -- brake zsch sound |
399 | if samples.compressedAir ~= nil then |
400 | if isBraking then |
401 | samples.compressedAir.brakeTime = samples.compressedAir.brakeTime + dt |
402 | else |
403 | if samples.compressedAir.brakeTime > 0 then |
404 | samples.compressedAir.lastBrakeTime = samples.compressedAir.brakeTime |
405 | samples.compressedAir.brakeTime = 0 |
406 | |
407 | g_soundManager:playSample(samples.compressedAir) |
408 | end |
409 | end |
410 | end |
411 | |
412 | --brake sound |
413 | if samples.brake ~= nil then |
414 | if isBraking then |
415 | if not spec.isBrakeSamplePlaying then |
416 | g_soundManager:playSample(samples.brake) |
417 | spec.isBrakeSamplePlaying = true |
418 | end |
419 | else |
420 | if spec.isBrakeSamplePlaying then |
421 | g_soundManager:stopSample(samples.brake) |
422 | spec.isBrakeSamplePlaying = false |
423 | end |
424 | end |
425 | end |
426 | |
427 | -- reverse driving beep |
428 | if samples.reverseDrive ~= nil then |
429 | if (self.getIsControlled ~= nil and self:getIsControlled()) or self:getIsAIActive() then |
430 | local reverserDirection = self.getReverserDirection == nil and 1 or self:getReverserDirection() |
431 | local isReverseDriving = self:getLastSpeed() > spec.reverseDriveThreshold and self.movingDirection ~= reverserDirection |
432 | if not g_soundManager:getIsSamplePlaying(samples.reverseDrive) and isReverseDriving then |
433 | g_soundManager:playSample(samples.reverseDrive) |
434 | elseif not isReverseDriving then |
435 | g_soundManager:stopSample(samples.reverseDrive) |
436 | end |
437 | else |
438 | g_soundManager:stopSample(samples.reverseDrive) |
439 | end |
440 | end |
441 | end |
442 | end |
443 | |
444 | if self.isServer then |
445 | if not self:getIsAIActive() and self:getTravedDistanceStatsActive() then |
446 | if self.lastMovedDistance > 0.001 then |
447 | g_currentMission:farmStats(self:getOwnerFarmId()):updateStats("traveledDistance", self.lastMovedDistance*0.001) |
448 | end |
449 | end |
450 | end |
451 | |
452 | spec.showTurnOnMotorWarning = false |
453 | else |
454 | if self:getCanMotorRun() then |
455 | spec.showTurnOnMotorWarning = accInput ~= 0 |
456 | end |
457 | end |
458 | end |
465 | function Motorized:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
466 | local spec = self.spec_motorized |
467 | |
468 | if self.isServer then |
469 | -- motor load |
470 | local loadPercentage = spec.motor:getMotorAppliedTorque() / math.max(spec.motor:getMotorAvailableTorque(), 0.0001) |
471 | |
472 | spec.actualLoadPercentage = loadPercentage |
473 | if spec.actualLoadPercentage > spec.smoothedLoadPercentage then |
474 | spec.smoothedLoadPercentage = 0.95*spec.smoothedLoadPercentage + 0.05*loadPercentage |
475 | else |
476 | spec.smoothedLoadPercentage = 0.98*spec.smoothedLoadPercentage + 0.02*loadPercentage |
477 | end |
478 | |
479 | -- force stop of motor if player is far away from vehicle for a certain amount of time |
480 | if not g_currentMission.missionInfo.automaticMotorStartEnabled then |
481 | if spec.isMotorStarted and not self:getIsAIActive() then |
482 | |
483 | local isEntered = self.getIsEntered ~= nil and self:getIsEntered() |
484 | local isControlled = self.getIsControlled ~= nil and self:getIsControlled() |
485 | |
486 | if not isEntered and not isControlled then |
487 | |
488 | local isPlayerInRange = false |
489 | |
490 | for _, player in pairs(g_currentMission.players) do |
491 | if player.isControlled then |
492 | local distance = calcDistanceFrom(self.rootNode, player.rootNode) |
493 | if distance < 250 then |
494 | isPlayerInRange = true |
495 | break |
496 | end |
497 | end |
498 | end |
499 | if not isPlayerInRange then |
500 | for _, enterable in pairs(g_currentMission.enterables) do |
501 | if enterable.spec_enterable ~= nil and enterable.spec_enterable.isControlled then |
502 | local distance = calcDistanceFrom(self.rootNode, enterable.rootNode) |
503 | if distance < 250 then |
504 | isPlayerInRange = true |
505 | break |
506 | end |
507 | end |
508 | end |
509 | end |
510 | |
511 | if isPlayerInRange then |
512 | spec.motorStopTimer = spec.motorStopTimerDuration |
513 | else |
514 | spec.motorStopTimer = spec.motorStopTimer - dt |
515 | if spec.motorStopTimer <= 0 then |
516 | self:stopMotor() |
517 | end |
518 | end |
519 | |
520 | end |
521 | end |
522 | end |
523 | |
524 | if spec.isMotorStarted then |
525 | self:updateMotorTemperature(dt) |
526 | else |
527 | --start motor if fuel was empty and got filled |
528 | --also starts the motor again when we toggle the automatic motoro start option |
529 | if g_currentMission.missionInfo.automaticMotorStartEnabled then |
530 | if self.getIsControlled ~= nil and self:getIsControlled() then |
531 | if self:getCanMotorRun() then |
532 | self:startMotor(true) |
533 | end |
534 | end |
535 | end |
536 | end |
537 | end |
538 | |
539 | if self.isClient then |
540 | if self:getIsMotorStarted() then |
541 | if spec.exhaustParticleSystems ~= nil then |
542 | for _, ps in pairs(spec.exhaustParticleSystems) do |
543 | local scale = MathUtil.lerp(spec.exhaustParticleSystems.minScale, spec.exhaustParticleSystems.maxScale, spec.motor:getEqualizedMotorRpm() / spec.motor:getMaxRpm()) |
544 | ParticleUtil.setEmitCountScale(spec.exhaustParticleSystems, scale) |
545 | ParticleUtil.setParticleLifespan(ps, ps.originalLifespan * scale) |
546 | end |
547 | end |
548 | |
549 | if spec.exhaustFlap ~= nil then |
550 | local minRandom = -0.1 |
551 | local maxRandom = 0.1 |
552 | local angle = MathUtil.lerp(minRandom, maxRandom, math.random()) + spec.exhaustFlap.maxRot * (spec.motor:getEqualizedMotorRpm() / spec.motor:getMaxRpm()) |
553 | angle = MathUtil.clamp(angle, 0, spec.exhaustFlap.maxRot) |
554 | setRotation(spec.exhaustFlap.node, angle, 0, 0) |
555 | end |
556 | |
557 | if spec.exhaustEffects ~= nil then |
558 | local lastSpeed = self:getLastSpeed() |
559 | |
560 | spec.currentDirection = {localDirectionToWorld(self.rootNode, 0, 0, 1)} |
561 | if spec.lastDirection == nil then |
562 | spec.lastDirection = spec.currentDirection |
563 | end |
564 | |
565 | local x,_,z = worldDirectionToLocal(self.rootNode, spec.lastDirection[1], spec.lastDirection[2], spec.lastDirection[3]) |
566 | local dot = z |
567 | dot = dot / MathUtil.vector2Length(x,z) |
568 | local angle = math.acos(dot) |
569 | if x < 0 then |
570 | angle = -angle |
571 | end |
572 | local steeringPercent = math.abs((angle / dt) / spec.exhaustEffectMaxSteeringSpeed) |
573 | spec.lastDirection = spec.currentDirection |
574 | |
575 | for _, effect in pairs(spec.exhaustEffects) do |
576 | local rpmScale = spec.motor:getEqualizedMotorRpm() / spec.motor:getMaxRpm() |
577 | local scale = MathUtil.lerp(effect.minRpmScale, effect.maxRpmScale, rpmScale) |
578 | local forwardXRot = 0 |
579 | local forwardZRot = 0 |
580 | local steerXRot = 0 |
581 | local steerZRot = 0 |
582 | |
583 | local r = MathUtil.lerp(effect.minRpmColor[1], effect.maxRpmColor[1], rpmScale) |
584 | local g = MathUtil.lerp(effect.minRpmColor[2], effect.maxRpmColor[2], rpmScale) |
585 | local b = MathUtil.lerp(effect.minRpmColor[3], effect.maxRpmColor[3], rpmScale) |
586 | local a = MathUtil.lerp(effect.minRpmColor[4], effect.maxRpmColor[4], rpmScale) |
587 | setShaderParameter(effect.effectNode, "exhaustColor", r, g, b, a, false) |
588 | |
589 | -- speed rotation |
590 | if self.movingDirection == 1 then |
591 | local percent = MathUtil.clamp(lastSpeed/effect.maxForwardSpeed, 0, 1) |
592 | forwardXRot = effect.xzRotationsForward[1] * percent |
593 | forwardZRot = effect.xzRotationsForward[2] * percent |
594 | elseif self.movingDirection == -1 then |
595 | local percent = MathUtil.clamp(lastSpeed/effect.maxBackwardSpeed, 0, 1) |
596 | forwardXRot = effect.xzRotationsBackward[1] * percent |
597 | forwardZRot = effect.xzRotationsBackward[2] * percent |
598 | end |
599 | |
600 | -- steering rotation |
601 | if angle > 0 then |
602 | steerXRot = effect.xzRotationsRight[1] * steeringPercent |
603 | steerZRot = effect.xzRotationsRight[2] * steeringPercent |
604 | elseif angle < 0 then |
605 | steerXRot = effect.xzRotationsLeft[1] * steeringPercent |
606 | steerZRot = effect.xzRotationsLeft[2] * steeringPercent |
607 | end |
608 | -- target rotations |
609 | local targetXRot = effect.xzRotationsOffset[1] + forwardXRot + steerXRot |
610 | local targetZRot = effect.xzRotationsOffset[2] + forwardZRot + steerZRot |
611 | |
612 | -- damping |
613 | if targetXRot > effect.xRot then |
614 | effect.xRot = math.min(effect.xRot + 0.003*dt, targetXRot) |
615 | else |
616 | effect.xRot = math.max(effect.xRot - 0.003*dt, targetXRot) |
617 | end |
618 | if targetZRot > effect.xRot then |
619 | effect.zRot = math.min(effect.zRot + 0.003*dt, targetZRot) |
620 | else |
621 | effect.zRot = math.max(effect.zRot - 0.003*dt, targetZRot) |
622 | end |
623 | setShaderParameter(effect.effectNode, "param", effect.xRot, effect.zRot, 0, scale, false) |
624 | end |
625 | end |
626 | |
627 | spec.lastFuelUsageDisplayTime = spec.lastFuelUsageDisplayTime + dt |
628 | if spec.lastFuelUsageDisplayTime > 250 then |
629 | spec.lastFuelUsageDisplayTime = 0 |
630 | spec.lastFuelUsageDisplay = spec.fuelUsageBuffer:getAverage() |
631 | end |
632 | |
633 | spec.fuelUsageBuffer:add(spec.lastFuelUsage) |
634 | end |
635 | |
636 | -- display permanent fuel empty warning if automatic motor start is enabled |
637 | if isActiveForInputIgnoreSelection then |
638 | if g_currentMission.missionInfo.automaticMotorStartEnabled then |
639 | if not self:getCanMotorRun() then |
640 | local warning = self:getMotorNotAllowedWarning() |
641 | if warning ~= nil then |
642 | g_currentMission:showBlinkingWarning(warning, 2000) |
643 | end |
644 | end |
645 | end |
646 | |
647 | -- hide / show start motor input action if setting is changed on the fly |
648 | local actionEvent = spec.actionEvents[InputAction.TOGGLE_MOTOR_STATE] |
649 | if actionEvent ~= nil then |
650 | if not g_currentMission.missionInfo.automaticMotorStartEnabled then |
651 | local text |
652 | |
653 | g_inputBinding:setActionEventActive(actionEvent.actionEventId, true) |
654 | if self:getIsMotorStarted() then |
655 | g_inputBinding:setActionEventTextPriority(actionEvent.actionEventId, GS_PRIO_VERY_LOW) |
656 | text = spec.turnOffText |
657 | else |
658 | g_inputBinding:setActionEventTextPriority(actionEvent.actionEventId, GS_PRIO_VERY_HIGH) |
659 | text = spec.turnOnText |
660 | end |
661 | |
662 | g_inputBinding:setActionEventText(actionEvent.actionEventId, text) |
663 | else |
664 | g_inputBinding:setActionEventActive(actionEvent.actionEventId, false) |
665 | end |
666 | end |
667 | end |
668 | end |
669 | end |
1166 | function Motorized:startMotor(noEventSend) |
1167 | if noEventSend == nil or noEventSend == false then |
1168 | if g_server ~= nil then |
1169 | g_server:broadcastEvent(SetMotorTurnedOnEvent:new(self, true), nil, nil, self) |
1170 | else |
1171 | g_client:getServerConnection():sendEvent(SetMotorTurnedOnEvent:new(self, true)) |
1172 | end |
1173 | end |
1174 | local spec = self.spec_motorized |
1175 | if not spec.isMotorStarted then |
1176 | spec.isMotorStarted = true |
1177 | |
1178 | if self.isClient then |
1179 | if spec.exhaustParticleSystems ~= nil then |
1180 | for _, ps in pairs(spec.exhaustParticleSystems) do |
1181 | ParticleUtil.setEmittingState(ps, true) |
1182 | end |
1183 | end |
1184 | if spec.exhaustEffects ~= nil then |
1185 | for _, effect in pairs(spec.exhaustEffects) do |
1186 | setVisibility(effect.effectNode, true) |
1187 | effect.xRot = effect.xzRotationsOffset[1] |
1188 | effect.zRot = effect.xzRotationsOffset[2] |
1189 | setShaderParameter(effect.effectNode, "param", effect.xRot, effect.zRot, 0, 0, false) |
1190 | |
1191 | local color = effect.minRpmColor |
1192 | setShaderParameter(effect.effectNode, "exhaustColor", color[1], color[2], color[3], color[4], false) |
1193 | end |
1194 | end |
1195 | |
1196 | g_soundManager:stopSample(spec.samples.motorStop) |
1197 | |
1198 | g_soundManager:playSample(spec.samples.motorStart) |
1199 | g_soundManager:playSamples(spec.motorSamples, 0, spec.samples.motorStart) |
1200 | g_soundManager:playSample(spec.samples.gearbox, 0, spec.samples.motorStart) |
1201 | g_soundManager:playSample(spec.samples.retarder, 0, spec.samples.motorStart) |
1202 | |
1203 | g_animationManager:startAnimations(spec.animationNodes) |
1204 | |
1205 | if spec.motorStartAnimation ~= nil then |
1206 | self:playAnimation(spec.motorStartAnimation, 1, nil, true) |
1207 | end |
1208 | end |
1209 | |
1210 | spec.motorStartTime = g_currentMission.time + spec.motorStartDuration |
1211 | spec.compressionSoundTime = g_currentMission.time + math.random(5000, 20000) |
1212 | spec.lastRoundPerMinute = 0 |
1213 | |
1214 | SpecializationUtil.raiseEvent(self, "onStartMotor") |
1215 | self:getRootVehicle():raiseStateChange(Vehicle.STATE_CHANGE_MOTOR_TURN_ON) |
1216 | end |
1217 | |
1218 | if self.setDashboardsDirty ~= nil then |
1219 | self:setDashboardsDirty() |
1220 | end |
1221 | |
1222 | end |
1227 | function Motorized:stopMotor(noEventSend) |
1228 | if noEventSend == nil or noEventSend == false then |
1229 | if g_server ~= nil then |
1230 | g_server:broadcastEvent(SetMotorTurnedOnEvent:new(self, false), nil, nil, self) |
1231 | else |
1232 | g_client:getServerConnection():sendEvent(SetMotorTurnedOnEvent:new(self, false)) |
1233 | end |
1234 | end |
1235 | |
1236 | local spec = self.spec_motorized |
1237 | if spec.isMotorStarted then |
1238 | spec.isMotorStarted = false |
1239 | |
1240 | if self.isClient then |
1241 | if spec.exhaustParticleSystems ~= nil then |
1242 | for _, ps in pairs(spec.exhaustParticleSystems) do |
1243 | ParticleUtil.setEmittingState(ps, false) |
1244 | end |
1245 | end |
1246 | |
1247 | if spec.exhaustEffects ~= nil then |
1248 | for _, effect in pairs(spec.exhaustEffects) do |
1249 | setVisibility(effect.effectNode, false) |
1250 | end |
1251 | end |
1252 | if spec.exhaustFlap ~= nil then |
1253 | setRotation(spec.exhaustFlap.node, 0, 0, 0) |
1254 | end |
1255 | |
1256 | g_soundManager:stopSample(spec.samples.motorStart) |
1257 | g_soundManager:playSample(spec.samples.motorStop) |
1258 | g_soundManager:stopSamples(spec.motorSamples) |
1259 | g_soundManager:stopSample(spec.samples.gearbox) |
1260 | g_soundManager:stopSample(spec.samples.retarder) |
1261 | |
1262 | g_soundManager:stopSample(spec.samples.airCompressorStart) |
1263 | g_soundManager:stopSample(spec.samples.airCompressorStop) |
1264 | g_soundManager:stopSample(spec.samples.airCompressorRun) |
1265 | |
1266 | g_soundManager:stopSample(spec.samples.compressedAir) |
1267 | g_soundManager:stopSample(spec.samples.airRelease) |
1268 | |
1269 | g_soundManager:stopSample(spec.samples.reverseDrive) |
1270 | g_soundManager:stopSample(spec.samples.brake) |
1271 | spec.isBrakeSamplePlaying = false |
1272 | |
1273 | g_animationManager:stopAnimations(spec.animationNodes) |
1274 | |
1275 | if spec.motorStartAnimation ~= nil then |
1276 | self:playAnimation(spec.motorStartAnimation, -1, nil, true) |
1277 | end |
1278 | end |
1279 | |
1280 | SpecializationUtil.raiseEvent(self, "onStopMotor") |
1281 | self:getRootVehicle():raiseStateChange(Vehicle.STATE_CHANGE_MOTOR_TURN_OFF) |
1282 | end |
1283 | |
1284 | if self.setDashboardsDirty ~= nil then |
1285 | self:setDashboardsDirty() |
1286 | end |
1287 | end |
1291 | function Motorized:updateConsumers(dt, accInput) |
1292 | local spec = self.spec_motorized |
1293 | |
1294 | local idleFactor = 0.5 |
1295 | local rpmPercentage = (spec.motor:getLastMotorRpm() - spec.motor:getMinRpm()) / (spec.motor:getMaxRpm() - spec.motor:getMinRpm()) |
1296 | local rpmFactor = idleFactor + rpmPercentage * (1-idleFactor) |
1297 | local loadFactor = spec.smoothedLoadPercentage * rpmPercentage |
1298 | local motorFactor = 0.5 * ( (0.2*rpmFactor) + (1.8*loadFactor) ) |
1299 | |
1300 | local usageFactor = 1.0 |
1301 | if g_currentMission.missionInfo.fuelUsageLow then |
1302 | usageFactor = 0.7 |
1303 | end |
1304 | |
1305 | local damage = self:getVehicleDamage() |
1306 | if damage > 0 then |
1307 | usageFactor = usageFactor * (1 + damage * Motorized.DAMAGED_USAGE_INCREASE) |
1308 | end |
1309 | |
1310 | -- update permanent consumers |
1311 | for _,consumer in pairs(spec.consumers) do |
1312 | if consumer.permanentConsumption and consumer.usage > 0 then |
1313 | local used = usageFactor * motorFactor * consumer.usage * dt |
1314 | if used ~= 0 then |
1315 | local fillType = self:getFillUnitLastValidFillType(consumer.fillUnitIndex) |
1316 | |
1317 | local stats = g_currentMission:farmStats(self:getOwnerFarmId()) |
1318 | stats:updateStats("fuelUsage", used) |
1319 | |
1320 | if self:getIsAIActive() then |
1321 | if fillType == FillType.DIESEL or fillType == FillType.DEF then |
1322 | if g_currentMission.missionInfo.helperBuyFuel then |
1323 | if fillType == FillType.DIESEL then |
1324 | local price = used * g_currentMission.economyManager:getPricePerLiter(fillType) * 1.5 |
1325 | stats:updateStats("expenses", price) |
1326 | |
1327 | g_currentMission:addMoney(-price, self:getOwnerFarmId(), MoneyType.PURCHASE_FUEL, true) |
1328 | end |
1329 | |
1330 | used = 0 |
1331 | end |
1332 | end |
1333 | end |
1334 | |
1335 | if fillType == consumer.fillType then |
1336 | self:addFillUnitFillLevel(self:getOwnerFarmId(), consumer.fillUnitIndex, -used, fillType, ToolType.UNDEFINED) |
1337 | end |
1338 | |
1339 | if fillType == FillType.DIESEL then |
1340 | spec.lastFuelUsage = used / dt * 1000 * 60 * 60 -- per hour |
1341 | elseif fillType == FillType.DEF then |
1342 | spec.lastDefUsage = used / dt * 1000 * 60 * 60 -- per hour |
1343 | end |
1344 | end |
1345 | end |
1346 | end |
1347 | |
1348 | -- update air consuming |
1349 | if spec.consumersByFillTypeName.air ~= nil then |
1350 | local consumer = spec.consumersByFillTypeName.air |
1351 | local fillType = self:getFillUnitLastValidFillType(consumer.fillUnitIndex) |
1352 | if fillType == consumer.fillType then |
1353 | local usage = 0 |
1354 | |
1355 | -- consume air on brake |
1356 | local forwardBrake = self.movingDirection > 0 and accInput < 0 |
1357 | local backwardBrake = self.movingDirection < 0 and accInput > 0 |
1358 | local brakeIsPressed = self:getLastSpeed() > 1.0 and (forwardBrake or backwardBrake) |
1359 | if brakeIsPressed then |
1360 | local delta = math.abs(accInput) * dt * self:getAirConsumerUsage() / 1000 |
1361 | self:addFillUnitFillLevel(self:getOwnerFarmId(), consumer.fillUnitIndex, -delta, consumer.fillType, ToolType.UNDEFINED) |
1362 | |
1363 | usage = delta / dt * 1000 -- per sec |
1364 | end |
1365 | |
1366 | --refill air fill unit if it is below given level |
1367 | local fillLevelPercentage = self:getFillUnitFillLevelPercentage(consumer.fillUnitIndex) |
1368 | if fillLevelPercentage < consumer.refillCapacityPercentage then |
1369 | consumer.doRefill = true |
1370 | elseif fillLevelPercentage == 1 then |
1371 | consumer.doRefill = false |
1372 | end |
1373 | |
1374 | if consumer.doRefill then |
1375 | local delta = consumer.refillLitersPerSecond / 1000 * dt |
1376 | self:addFillUnitFillLevel(self:getOwnerFarmId(), consumer.fillUnitIndex, delta, consumer.fillType, ToolType.UNDEFINED) |
1377 | |
1378 | usage = -delta / dt * 1000 -- per sec |
1379 | end |
1380 | |
1381 | spec.lastAirUsage = usage |
1382 | end |
1383 | end |
1384 | end |