Sprache Deutsch Language English

Script Dokumentation LS 2015 - Motorized (Patch 1.3)

Script Dokumentation Übersicht

scripts/vehicles/specializations/Motorized.lua

Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1--
2-- Motorized
3-- Desc
4--
5-- @author Stefan Geiger
6-- @date 30/11/08
7--
8-- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.
9
10Motorized = {};
11source("dataS/scripts/vehicles/specializations/SteerableToggleRefuelEvent.lua");
12source("dataS/scripts/vehicles/specializations/SetMotorTurnedOnEvent.lua");
13
14
15function Motorized.prerequisitesPresent(specializations)
16 return true;
17end;
18
19function Motorized:load(xmlFile)
20
21 self.startMotor = SpecializationUtil.callSpecializationsFunction("startMotor");
22 self.stopMotor = SpecializationUtil.callSpecializationsFunction("stopMotor");
23 self.setIsFuelFilling = SpecializationUtil.callSpecializationsFunction("setIsFuelFilling");
24 self.setFuelFillLevel = SpecializationUtil.callSpecializationsFunction("setFuelFillLevel");
25 self.addFuelFillTrigger = Motorized.addFuelFillTrigger;
26 self.removeFuelFillTrigger = Motorized.removeFuelFillTrigger;
27
28 self.fuelCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fuelCapacity"), 500);
29 local fuelUsage = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fuelUsage"), 1);
30 if fuelUsage < 1 then
31 print("Warning: New unit for fuel usage is l/h at max. rpm");
32 end;
33 self.fuelUsage = fuelUsage / (60*60*1000); -- from l/h to l/ms
34
35 self.motorizedFillActivatable = MotorizedRefuelActivatable:new(self);
36
37 self.fuelFillTriggers = {};
38 self.isFuelFilling = false;
39 self.fuelFillLitersPerSecond = 10;
40 self:setFuelFillLevel(self.fuelCapacity);
41 self.sentFuelFillLevel = self.fuelFillLevel;
42
43 self.stopMotorOnLeave = true;
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, xmlFile);
54 Motorized.loadMotor(self, xmlFile);
55
56 if self.isClient then
57 self.sampleRefuel = Utils.loadSample(xmlFile, {}, "vehicle.refuelSound", "$data/maps/sounds/refuel.wav", self.baseDirectory, self.components[1].node);
58 self.sampleMotorStart = Utils.loadSample(xmlFile, {}, "vehicle.motorStartSound", nil, self.baseDirectory);
59 self.sampleMotorStop = Utils.loadSample(xmlFile, {}, "vehicle.motorStopSound", nil, self.baseDirectory);
60 self.sampleMotor = Utils.loadSample(xmlFile, {}, "vehicle.motorSound", nil, self.baseDirectory, self.components[1].node);
61 self.sampleMotorRun = Utils.loadSample(xmlFile, {}, "vehicle.motorSoundRun", nil, self.baseDirectory, self.components[1].node);
62 self.sampleMotorRun2 = Utils.loadSample(xmlFile, {}, "vehicle.motorSoundRun2", nil, self.baseDirectory, self.components[1].node);
63 self.sampleReverseDrive = Utils.loadSample(xmlFile, {}, "vehicle.reverseDriveSound", nil, self.baseDirectory);
64 self.sampleCompressedAir = Utils.loadSample(xmlFile, {}, "vehicle.compressedAirSound", nil, self.baseDirectory);
65 self.sampleCompression = Utils.loadSample(xmlFile, {}, "vehicle.compressionSound", nil, self.baseDirectory);
66
67 self.motorRun2PitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSoundRun2#pitchMax"), 2.0);
68 self.motorRun2VolumeMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSoundRun2#volumeMax"), 2.0);
69
70 self.motorSoundPitchScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSound#pitchScale"), 0.05);
71 self.motorSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSound#pitchMax"), 2.0);
72 self.motorSoundRunPitchScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSoundRun#pitchScale"), 0.05);
73 self.motorSoundRunPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorSoundRun#pitchMax"), 2.0);
74 self.compressedAirSoundEnabled = false;
75 self.compressionSoundTime = 0;
76
77 self.exhaustParticleSystems = {};
78 local exhaustParticleSystemCount = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.exhaustParticleSystems#count"), 0);
79 for i=1, exhaustParticleSystemCount do
80 local namei = string.format("vehicle.exhaustParticleSystems.exhaustParticleSystem%d", i);
81
82 Utils.loadParticleSystem(xmlFile, self.exhaustParticleSystems, namei, self.components, false, nil, self.baseDirectory)
83 self.exhaustParticleSystems.minScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.exhaustParticleSystems#minScale"), 0.5);
84 self.exhaustParticleSystems.maxScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.exhaustParticleSystems#maxScale"), 1);
85 for _, v in ipairs(self.exhaustParticleSystems) do
86 v.originalLifespan = getParticleSystemLifespan(v.geometry);
87 end;
88 end;
89
90 local exhaustFlapIndex = getXMLString(xmlFile, "vehicle.exhaustFlap#index");
91 if exhaustFlapIndex ~= nil then
92 self.exhaustFlap = {};
93 self.exhaustFlap.node = Utils.indexToObject(self.components, exhaustFlapIndex);
94 self.exhaustFlap.maxRot = Utils.degToRad(Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.exhaustFlap#maxRot"),0));
95 end;
96
97 self.exhaustEffects = {};
98 Motorized.loadExhaustEffects(self, xmlFile, self.exhaustEffects);
99 if table.getn(self.exhaustEffects) == 0 then
100 self.exhaustEffects = nil;
101 end;
102 end;
103
104 self.motorStartDuration = 0;
105 if self.sampleMotorStart ~= nil then
106 self.motorStartDuration = self.sampleMotorStart.duration;
107 end;
108 self.motorStartDuration = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motorStartDuration"), self.motorStartDuration);
109 self.motorStartTime = 0;
110 self.isMotorStarted = false;
111 self.lastRoundPerMinute = 0;
112 self.motorizedDirtyFlag = self:getNextDirtyFlag();
113end;
114
115function Motorized:loadExhaustEffects(xmlFile, exhaustEffects)
116 local i = 0;
117 while true do
118 local key = string.format("vehicle.exhaustEffects.exhaustEffect(%d)", i);
119 if not hasXMLProperty(xmlFile, key) then
120 break;
121 end
122 local filename = getXMLString(xmlFile, key .. "#filename");
123 if filename ~= nil then
124 local effect = {};
125 if exhaustEffects ~= nil and exhaustEffects[i+1] ~= nil then
126 effect = exhaustEffects[i+1];
127 else
128 effect.node = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
129 effect.filename = filename;
130 local i3dNode = Utils.loadSharedI3DFile(filename, self.baseDirectory, false, false, false);
131 if i3dNode ~= 0 then
132 effect.effectNode = getChildAt(i3dNode, 0);
133 link(effect.node, effect.effectNode);
134 setVisibility(effect.effectNode, false);
135 delete(i3dNode);
136 table.insert(exhaustEffects, effect);
137 end;
138 end;
139
140 effect.minRpmColor = Utils.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#minRpmColor"), "0 0 0 1"), 4);
141 effect.maxRpmColor = Utils.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#maxRpmColor"), "0.0384 0.0359 0.0627 2.0"), 4);
142 effect.minRpmScale = Utils.getNoNil(getXMLFloat(xmlFile, key.."#minRpmScale"), 0.25);
143 effect.maxRpmScale = Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxRpmScale"), 0.95);
144 effect.maxForwardSpeed = Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxForwardSpeed"), math.ceil(self.motor:getMaximumForwardSpeed()*3.6));
145 effect.maxBackwardSpeed = Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxBackwardSpeed"), math.ceil(self.motor:getMaximumBackwardSpeed()*3.6));
146 effect.forwardXRotations = Utils.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#forwardXRotations"), "-250 0 250"), 3);
147 effect.forwardZRotations = Utils.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#forwardZRotations"), "0 0 0"), 3);
148 effect.steerXRotations = Utils.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#steerXRotations"), "0 0 0"), 3);
149 effect.steerZRotations = Utils.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#steerZRotations"), "120 0 -120"), 3);
150 effect.xRot = 0;
151 effect.zRot = 0;
152 end;
153 i = i + 1;
154 end;
155 self.exhaustEffectMaxSteeringSpeed = 0.001;
156end;
157
158function Motorized:loadDifferentials(xmlFile)
159 self.differentials = {};
160 if self.isServer and self.motorizedNode ~= nil then
161 local i = 0;
162 while true do
163 local key = string.format("vehicle.differentials.differential(%d)", i);
164 if not hasXMLProperty(xmlFile, key) then
165 break;
166 end;
167 local torqueRatio = Utils.getNoNil(getXMLFloat(xmlFile, key.."#torqueRatio"), 0.5);
168 local maxSpeedRatio = Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxSpeedRatio"), 1.3);
169
170 local diffIndex1, diffIndex1IsWheel;
171 local diffIndex2, diffIndex2IsWheel;
172
173 local wheelIndex1 = getXMLInt(xmlFile, key.."#wheelIndex1");
174 if wheelIndex1 ~= nil then
175 local wheel = self.wheels[wheelIndex1+1];
176 if wheel ~= nil then
177 diffIndex1IsWheel = true;
178 diffIndex1 = wheel.wheelShape;
179 end
180 else
181 diffIndex1IsWheel = false;
182 diffIndex1 = getXMLInt(xmlFile, key.."#differentialIndex1");
183 end
184 local wheelIndex2 = getXMLInt(xmlFile, key.."#wheelIndex2");
185 if wheelIndex2 ~= nil then
186 local wheel = self.wheels[wheelIndex2+1];
187 if wheel ~= nil then
188 diffIndex2IsWheel = true;
189 diffIndex2 = wheel.wheelShape;
190 end
191 else
192 diffIndex2IsWheel = false;
193 diffIndex2 = getXMLInt(xmlFile, key.."#differentialIndex2");
194 end
195 if diffIndex1 ~= nil and diffIndex2 ~= nil then
196 table.insert(self.differentials, {torqueRatio=torqueRatio, maxSpeedRatio=maxSpeedRatio, diffIndex1=diffIndex1, diffIndex1IsWheel=diffIndex1IsWheel, diffIndex2=diffIndex2, diffIndex2IsWheel=diffIndex2IsWheel});
197 addDifferential(self.motorizedNode, diffIndex1, diffIndex1IsWheel, diffIndex2, diffIndex2IsWheel, torqueRatio, maxSpeedRatio);
198 else
199 print("Error: Invalid differential indices in '"..self.configFileName.."'");
200 end
201
202 i = i + 1;
203 end
204 end
205end
206
207function Motorized:loadMotor(xmlFile)
208
209 local motorMinRpm = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motor#minRpm"), 1000);
210 local motorMaxRpm = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motor#maxRpm"), 1800);
211 local maxForwardSpeed = getXMLFloat(xmlFile, "vehicle.motor#maxForwardSpeed");
212 local maxBackwardSpeed = getXMLFloat(xmlFile, "vehicle.motor#maxBackwardSpeed");
213 if maxForwardSpeed ~= nil then
214 maxForwardSpeed = maxForwardSpeed/3.6;
215 end
216 if maxBackwardSpeed ~= nil then
217 maxBackwardSpeed = maxBackwardSpeed/3.6;
218 end
219 local brakeForce = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motor#brakeForce"), 10)*2;
220 local lowBrakeForceScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motor#lowBrakeForceScale"), 0.5);
221 local lowBrakeForceSpeedLimit = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motor#lowBrakeForceSpeedLimit"), 20)/3600;
222 local forwardGearRatio = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motor#forwardGearRatio"), 2);
223 local backwardGearRatio = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motor#backwardGearRatio"), 1.5);
224 local maxForwardGearRatio = getXMLFloat(xmlFile, "vehicle.motor#maxForwardGearRatio");
225 local minForwardGearRatio = getXMLFloat(xmlFile, "vehicle.motor#minForwardGearRatio");
226 local maxBackwardGearRatio = getXMLFloat(xmlFile, "vehicle.motor#maxBackwardGearRatio");
227 local minBackwardGearRatio = getXMLFloat(xmlFile, "vehicle.motor#minBackwardGearRatio");
228 local rpmFadeOutRange = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motor#rpmFadeOutRange"), 20);
229 local torqueScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motor#torqueScale"), 1);
230 local ptoMotorRpmRatio = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.motor#ptoMotorRpmRatio"), 4);
231 local maxTorque = 0;
232 local torqueCurve = AnimCurve:new(linearInterpolator1);
233 local torqueI = 0;
234 while true do
235 local key = string.format("vehicle.motor.torque(%d)", torqueI);
236 local normRpm = getXMLFloat(xmlFile, key.."#normRpm");
237 local rpm;
238 if normRpm == nil then
239 rpm = getXMLFloat(xmlFile, key.."#rpm");
240 else
241 rpm = normRpm * motorMaxRpm;
242 end
243 local torque = getXMLFloat(xmlFile, key.."#torque");
244 if torque == nil or rpm == nil then
245 break;
246 end;
247 torqueCurve:addKeyframe({v=torque*torqueScale, time = rpm});
248 torqueI = torqueI +1;
249 if torque*torqueScale > maxTorque then
250 maxTorque = torque*torqueScale;
251 end;
252 end;
253 self.motor = VehicleMotor:new(self, motorMinRpm, motorMaxRpm, maxForwardSpeed, maxBackwardSpeed, torqueCurve, brakeForce, forwardGearRatio, backwardGearRatio, minForwardGearRatio, maxForwardGearRatio, minBackwardGearRatio, maxBackwardGearRatio, ptoMotorRpmRatio, rpmFadeOutRange, maxTorque);
254 self.motor:setLowBrakeForce(lowBrakeForceScale, lowBrakeForceSpeedLimit);
255end;
256
257function Motorized:delete()
258
259 for _, trigger in pairs(self.fuelFillTriggers) do
260 trigger:onVehicleDeleted(self);
261 end
262
263 g_currentMission:removeActivatableObject(self.motorizedFillActivatable);
264
265 if self.isClient then
266 if self.exhaustEffects ~= nil then
267 for _, effect in pairs(self.exhaustEffects) do
268 Utils.releaseSharedI3DFile(effect.filename, self.baseDirectory, true);
269 end;
270 end;
271
272 Utils.deleteParticleSystem(self.exhaustParticleSystems);
273 Utils.deleteSample(self.sampleRefuel);
274 Utils.deleteSample(self.sampleCompressedAir);
275 Utils.deleteSample(self.sampleCompression);
276 Utils.deleteSample(self.sampleMotor);
277 Utils.deleteSample(self.sampleMotorRun);
278 Utils.deleteSample(self.sampleMotorRun2);
279 Utils.deleteSample(self.sampleMotorStart);
280 Utils.deleteSample(self.sampleMotorStop);
281 Utils.deleteSample(self.sampleReverseDrive);
282 end;
283end;
284
285function Motorized:readStream(streamId, connection)
286 local isMotorStarted = streamReadBool(streamId);
287 if isMotorStarted then
288 self:startMotor(true);
289 else
290 self:stopMotor(true);
291 end;
292 local isFuelFilling = streamReadBool(streamId);
293 self:setIsFuelFilling(isFuelFilling, true);
294
295 local newFuelFillLevel=streamReadFloat32(streamId);
296 self:setFuelFillLevel(newFuelFillLevel);
297end;
298
299function Motorized:writeStream(streamId, connection)
300 streamWriteBool(streamId, self.isMotorStarted);
301 streamWriteBool(streamId, self.isFuelFilling);
302 streamWriteFloat32(streamId, self.fuelFillLevel);
303end;
304
305function Motorized:readUpdateStream(streamId, timestamp, connection)
306 if connection.isServer then
307 --self.motor.lastMotorRpm = streamReadFloat32(streamId);
308 local rpm = streamReadUIntN(streamId, 11);
309 rpm = rpm / 2047;
310 local rpmRange = self.motor.maxRpm - self.motor.minRpm;
311 self.motor.lastMotorRpm = (rpm * rpmRange) + self.motor.minRpm ;
312
313 if streamReadBool(streamId) then
314 local fuelFillLevel = streamReadUIntN(streamId, 15)/32767*self.fuelCapacity;
315 self:setFuelFillLevel(fuelFillLevel);
316 end;
317 end;
318end;
319
320function Motorized:writeUpdateStream(streamId, connection, dirtyMask)
321 if not connection.isServer then
322 --streamWriteFloat32(streamId, self.motor.lastMotorRpm);
323 local rpmRange = self.motor.maxRpm - self.motor.minRpm;
324 local rpm = (self.motor.lastMotorRpm - self.motor.minRpm) / rpmRange;
325 rpm = math.floor(rpm * 2047);
326 streamWriteUIntN(streamId, rpm, 11);
327
328 if streamWriteBool(streamId, bitAND(dirtyMask, self.motorizedDirtyFlag) ~= 0) then
329 local percent = 0;
330 if self.fuelCapacity ~= 0 then
331 percent = Utils.clamp(self.fuelFillLevel / self.fuelCapacity, 0, 1);
332 end;
333 streamWriteUIntN(streamId, math.floor(percent*32767), 15);
334 end;
335 end;
336end;
337
338function Motorized:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
339 local fuelFillLevel = getXMLFloat(xmlFile, key.."#fuelFillLevel");
340 if fuelFillLevel ~= nil then
341 if self.fuelCapacity ~= 0 then
342 local minFuelFillLevel = 0.1*self.fuelCapacity
343 local numToRefill = math.max(minFuelFillLevel - fuelFillLevel, 0);
344 if numToRefill > 0 then
345 fuelFillLevel = minFuelFillLevel;
346 local delta = numToRefill * g_fuelPricePerLiter;
347 g_currentMission.missionStats:updateStats("expenses", delta);
348 g_currentMission:addSharedMoney(-delta, "vehicleRunningCost");
349 end;
350 end;
351 self:setFuelFillLevel(fuelFillLevel);
352 end;
353 return BaseMission.VEHICLE_LOAD_OK;
354end;
355
356function Motorized:getSaveAttributesAndNodes(nodeIdent)
357 local attributes = 'fuelFillLevel="'..self.fuelFillLevel..'"';
358 return attributes, nil;
359end;
360
361function Motorized:mouseEvent(posX, posY, isDown, isUp, button)
362end;
363
364function Motorized:keyEvent(unicode, sym, modifier, isDown)
365end;
366
367function Motorized:update(dt)
368
369 if self.isMotorStarted then
370 local accInput = 0;
371 if self.axisForward ~= nil then
372 accInput = -self.axisForward;
373 end;
374 if self.cruiseControl ~= nil and self.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF then
375 accInput = 1;
376 end
377 if self.isClient then
378 if self:getIsActiveForSound() then
379 if not Utils.isSamplePlaying(self.sampleMotorStart, 1.5*dt) then
380 Utils.playSample(self.sampleMotor, 0, 0, nil);
381 Utils.playSample(self.sampleMotorRun, 0, 0, 0);
382 Utils.playSample(self.sampleMotorRun2, 0, 0, 0);
383 end;
384
385 if self.compressionSoundTime <= g_currentMission.time then
386 Utils.playSample(self.sampleCompression, 1, 0, nil);
387 self.compressionSoundTime = g_currentMission.time + 180000;
388 end
389
390 Utils.stop3DSample(self.sampleMotor);
391 Utils.stop3DSample(self.sampleMotorRun);
392 Utils.stop3DSample(self.sampleMotorRun2);
393 else
394 Utils.play3DSample(self.sampleMotor);
395 Utils.play3DSample(self.sampleMotorRun);
396 Utils.play3DSample(self.sampleMotorRun2);
397 end;
398
399 if self.sampleReverseDrive.sample ~= nil then
400 if (accInput < 0 or accInput == 0) and (self:getLastSpeed() > 3 and self.movingDirection ~= self.reverserDirection) then
401 if self:getIsActiveForSound() then
402 Utils.playSample(self.sampleReverseDrive, 0, 0, nil);
403 end;
404 else
405 Utils.stopSample(self.sampleReverseDrive);
406 end;
407 end;
408 end;
409
410 if table.getn(self.wheels) > 0 then
411 local minRpm = self.motor.minRpm;
412 local maxRpm = self.motor.maxRpm;
413
414 local maxSpeed;
415 if self.movingDirection >= 0 then
416 maxSpeed = self.motor.maxForwardSpeed*0.001;
417 else
418 maxSpeed = self.motor.maxBackwardSpeed*0.001;
419 end
420
421 -- The actual rpm offset is 80% from the motor and 20% from the speed
422 local targetRpmOffset = (self.motor.lastMotorRpm-minRpm)*0.8 + math.min(self.lastSpeed/maxSpeed, 1)*(maxRpm-minRpm)*0.2;
423
424 local alpha = math.pow(0.01, dt*0.001);
425 local roundPerMinute = targetRpmOffset + alpha*(self.lastRoundPerMinute-targetRpmOffset);
426 self.lastRoundPerMinute = roundPerMinute;
427 local roundPerSecond = roundPerMinute / 60;
428 if self.isClient then
429 local motorSoundPitch = math.min(self.sampleMotor.pitchOffset + self.motorSoundPitchScale*math.abs(roundPerSecond), self.motorSoundPitchMax)
430 Utils.setSamplePitch(self.sampleMotor, motorSoundPitch);
431 local motorSoundRunPitch = math.min(self.sampleMotorRun.pitchOffset + self.motorSoundRunPitchScale*math.abs(roundPerSecond), self.motorSoundRunPitchMax)
432 Utils.setSamplePitch(self.sampleMotorRun, motorSoundRunPitch);
433
434 local rpmRunVolume = math.abs(roundPerMinute)/(maxRpm - minRpm);
435 if math.abs(accInput) < 0.01 then
436 rpmRunVolume = rpmRunVolume * 0.666;
437 end;
438 rpmRunVolume = Utils.clamp(rpmRunVolume, 0.0, 1.0);
439 Utils.setSampleVolume(self.sampleMotorRun, rpmRunVolume*self.sampleMotorRun.volume);
440
441 local speedFactor = Utils.clamp(self:getLastSpeed() / math.ceil(self.motor:getMaximumForwardSpeed()*3.6), 0, 1);
442 local pitchRun2 = Utils.lerp(self.sampleMotorRun2.pitchOffset, self.motorRun2PitchMax, speedFactor);
443 local volumeRun2 = Utils.lerp(self.sampleMotorRun2.volume, self.motorRun2VolumeMax, speedFactor);
444 Utils.setSamplePitch(self.sampleMotorRun2, pitchRun2)
445 Utils.setSampleVolume(self.sampleMotorRun2, volumeRun2);
446
447 if self.sampleCompressedAir.sample ~= nil then
448
449 if self.movingDirection > 0 and self.lastSpeed > self.motor.maxForwardSpeed*0.0005 then -- faster than 50% of max speed
450 if accInput > 0.5 then
451 -- if we drive fast enough, we need to run the compressor the next time we brake
452 self.compressedAirSoundEnabled = false;
453 elseif accInput < -0.7 then
454 -- play the compressor sound if we drive fast enough and brake
455 if not self.compressedAirSoundEnabled then
456 if self:getIsActiveForSound() then
457 Utils.playSample(self.sampleCompressedAir, 1, 0, nil);
458 end;
459 self.compressedAirSoundEnabled = true;
460 end
461 end;
462 end
463 end
464 end;
465 end;
466
467 if self.isServer then
468 if not self:getIsHired() then
469 if self.lastMovedDistance > 0 then
470 g_currentMission.missionStats:updateStats("traveledDistance", self.lastMovedDistance*0.001);
471 end;
472
473 local rpmFactor = math.max(0.02, (self.motor.lastMotorRpm-self.motor.minRpm)/(self.motor.maxRpm-self.motor.minRpm));
474 local fuelUsed = rpmFactor * (self.fuelUsage * dt);
475 self:setFuelFillLevel(self.fuelFillLevel-fuelUsed);
476 if fuelUsed > 0 then
477 g_currentMission.missionStats:updateStats("fuelUsage", fuelUsed);
478 end;
479 end;
480 end;
481 end;
482end;
483
484function Motorized:updateTick(dt)
485 if self.isServer then
486 if math.abs(self.fuelFillLevel-self.sentFuelFillLevel) > 0.001 then
487 self:raiseDirtyFlags(self.motorizedDirtyFlag);
488 self.sentFuelFillLevel = self.fuelFillLevel;
489 end;
490 end;
491
492 if self.isClient then
493 if self.isMotorStarted then
494 if self.exhaustParticleSystems.minScale ~= nil and self.exhaustParticleSystems.maxScale ~= nil then
495 local scale = Utils.lerp(self.exhaustParticleSystems.minScale, self.exhaustParticleSystems.maxScale, self.motor.lastMotorRpm / self.motor.maxRpm);
496 Utils.setEmitCountScale(self.exhaustParticleSystems, scale);
497 for _, ps in ipairs(self.exhaustParticleSystems) do
498 setParticleSystemLifespan(ps.geometry, ps.originalLifespan * scale, true);
499 end;
500 end;
501
502 if self.exhaustFlap ~= nil then
503 local minRandom = -0.1;
504 local maxRandom = 0.1;
505 local angle = Utils.lerp(minRandom, maxRandom, math.random()) + self.exhaustFlap.maxRot * (self.motor.lastMotorRpm / self.motor.maxRpm);
506 angle = Utils.clamp(angle, 0, self.exhaustFlap.maxRot);
507 setRotation(self.exhaustFlap.node, angle, 0, 0);
508 end;
509
510 if self.exhaustEffects ~= nil then
511 local lastSpeed = self:getLastSpeed();
512
513 self.currentDirection = {localDirectionToWorld(self.rootNode, 0, 0, 1)};
514 if self.lastDirection == nil then
515 self.lastDirection = self.currentDirection;
516 end;
517
518 local x,y,z = worldDirectionToLocal(self.rootNode, self.lastDirection[1], self.lastDirection[2], self.lastDirection[3]);
519 local dot = z;
520 dot = dot / Utils.vector2Length(x,z);
521 local angle = math.acos(dot);
522 if x < 0 then
523 angle = -angle;
524 end;
525 local steeringPercent = math.abs((angle / dt) / self.exhaustEffectMaxSteeringSpeed);
526 self.lastDirection = self.currentDirection;
527
528 for _, effect in pairs(self.exhaustEffects) do
529 local rpmScale = self.motor.lastMotorRpm / self.motor.maxRpm;
530 local scale = Utils.lerp(effect.minRpmScale, effect.maxRpmScale, rpmScale);
531 local forwardXRot = effect.forwardXRotations[2];
532 local forwardZRot = effect.forwardZRotations[2];
533 local steerXRot = effect.steerXRotations[2];
534 local steerZRot = effect.steerZRotations[2];
535
536 local r = Utils.lerp(effect.minRpmColor[1], effect.maxRpmColor[1], rpmScale);
537 local g = Utils.lerp(effect.minRpmColor[2], effect.maxRpmColor[2], rpmScale);
538 local b = Utils.lerp(effect.minRpmColor[3], effect.maxRpmColor[3], rpmScale);
539 local a = Utils.lerp(effect.minRpmColor[4], effect.maxRpmColor[4], rpmScale);
540 setShaderParameter(effect.effectNode, "exhaustColor", r, g, b, a, false);
541
542 -- speed rotation
543 if self.movingDirection == 1 then
544 local percent = Utils.clamp(lastSpeed/effect.maxForwardSpeed, 0, 1);
545 forwardXRot = effect.forwardXRotations[1] * percent;
546 forwardZRot = effect.forwardZRotations[1] * percent;
547 elseif self.movingDirection == -1 then
548 local percent = Utils.clamp(lastSpeed/effect.maxBackwardSpeed, 0, 1);
549 forwardXRot = effect.forwardXRotations[3] * percent;
550 forwardZRot = effect.forwardZRotations[3] * percent;
551 end;
552
553 -- steering rotation
554 if angle > 0 then
555 steerXRot = effect.steerXRotations[1] * steeringPercent;
556 steerZRot = effect.steerZRotations[1] * steeringPercent;
557 elseif angle < 0 then
558 steerXRot = effect.steerXRotations[3] * steeringPercent;
559 steerZRot = effect.steerZRotations[3] * steeringPercent;
560 end;
561 -- target rotations
562 local targetXRot = forwardXRot + steerXRot;
563 local targetZRot = forwardZRot + steerZRot;
564
565 -- damping
566 if targetXRot > effect.xRot then
567 effect.xRot = math.min(effect.xRot + 0.003*dt, targetXRot);
568 else
569 effect.xRot = math.max(effect.xRot - 0.003*dt, targetXRot);
570 end;
571 if targetZRot > effect.xRot then
572 effect.zRot = math.min(effect.zRot + 0.003*dt, targetZRot);
573 else
574 effect.zRot = math.max(effect.zRot - 0.003*dt, targetZRot);
575 end;
576 setShaderParameter(effect.effectNode, "param", effect.xRot, effect.zRot, 0, scale, false);
577 end;
578 end;
579 end;
580 end;
581
582 if self.isFuelFilling then
583 if self.isServer then
584 local delta = 0;
585 if self.fuelFillTrigger ~= nil then
586 delta = self.fuelFillLitersPerSecond*dt*0.001;
587 delta = self.fuelFillTrigger:fillFuel(self, delta);
588 end
589 if delta <= 0.001 then
590 self:setIsFuelFilling(false);
591 end
592 end
593 end;
594end;
595
596function Motorized:draw()
597end;
598
599function Motorized:startMotor(noEventSend)
600 if noEventSend == nil or noEventSend == false then
601 if g_server ~= nil then
602 g_server:broadcastEvent(SetMotorTurnedOnEvent:new(self, true), nil, nil, self);
603 else
604 g_client:getServerConnection():sendEvent(SetMotorTurnedOnEvent:new(self, true));
605 end;
606 end;
607 if not self.isMotorStarted then
608 self.isMotorStarted = true;
609
610 if self.isClient then
611 Utils.setEmittingState(self.exhaustParticleSystems, true)
612 if self:getIsActiveForSound() then
613 Utils.playSample(self.sampleMotorStart, 1, 0, nil);
614 end;
615 if self.exhaustEffects ~= nil then
616 for _, effect in pairs(self.exhaustEffects) do
617 setVisibility(effect.effectNode, true);
618 effect.xRot = effect.forwardXRotations[2] + effect.steerXRotations[2];
619 effect.zRot = effect.forwardZRotations[2] + effect.steerZRotations[2];
620 setShaderParameter(effect.effectNode, "param", effect.xRot, effect.zRot, 0, 1, false);
621 end;
622 end;
623 end;
624
625 self.motorStartTime = g_currentMission.time + self.motorStartDuration;
626 self.compressionSoundTime = g_currentMission.time + 180000;
627 self.lastRoundPerMinute=0;
628 end;
629end;
630
631function Motorized:stopMotor(noEventSend)
632 if noEventSend == nil or noEventSend == false then
633 if g_server ~= nil then
634 g_server:broadcastEvent(SetMotorTurnedOnEvent:new(self, false), nil, nil, self);
635 else
636 g_client:getServerConnection():sendEvent(SetMotorTurnedOnEvent:new(self, false));
637 end;
638 end;
639
640 self.isMotorStarted = false;
641
642 Motorized.stopSounds(self);
643
644 if self.isClient then
645 Utils.setEmittingState(self.exhaustParticleSystems, false);
646 if self:getIsActiveForSound() then
647 Utils.playSample(self.sampleMotorStop, 1, 0, nil);
648 end;
649 if self.exhaustEffects ~= nil then
650 for _, effect in pairs(self.exhaustEffects) do
651 setVisibility(effect.effectNode, false);
652 end;
653 end;
654 if self.exhaustFlap ~= nil then
655 setRotation(self.exhaustFlap.node, 0, 0, 0);
656 end;
657 end;
658end;
659
660function Motorized:stopSounds()
661 if self.isClient then
662 Utils.stopSample(self.sampleMotor, true);
663 Utils.stopSample(self.sampleMotorRun, true);
664 Utils.stopSample(self.sampleMotorRun2, true);
665 Utils.stopSample(self.sampleMotorStart, true);
666 Utils.stopSample(self.sampleCompression, true);
667 Utils.stopSample(self.sampleReverseDrive, true);
668 Utils.stop3DSample(self.sampleMotor, true);
669 Utils.stop3DSample(self.sampleMotorRun, true);
670 Utils.stop3DSample(self.sampleMotorRun2, true);
671 --Utils.stopSample(self.sampleRefuel, true);
672 end;
673end;
674
675function Motorized:onLeave()
676 if self.stopMotorOnLeave then
677 self:stopMotor(true);
678 end;
679 Motorized.stopSounds(self);
680end
681
682function Motorized:setFuelFillLevel(newFillLevel)
683 self.fuelFillLevel = math.max(math.min(newFillLevel, self.fuelCapacity), 0);
684end;
685
686function Motorized:setIsFuelFilling(isFilling, noEventSend)
687 if isFilling ~= self.isFuelFilling then
688 if noEventSend == nil or noEventSend == false then
689 if g_server ~= nil then
690 g_server:broadcastEvent(SteerableToggleRefuelEvent:new(self, isFilling), nil, nil, self);
691 else
692 g_client:getServerConnection():sendEvent(SteerableToggleRefuelEvent:new(self, isFilling));
693 end;
694 end;
695 self.isFuelFilling = isFilling;
696 if isFilling then
697 -- find the first trigger which is activable
698 self.fuelFillTrigger = nil;
699 for i=1, table.getn(self.fuelFillTriggers) do
700 local trigger = self.fuelFillTriggers[i];
701 if trigger:getIsActivatable(self) then
702 self.fuelFillTrigger = trigger;
703 break;
704 end;
705 end;
706 end
707 if self.isClient and self.sampleRefuel ~= nil then
708 if isFilling then
709 Utils.play3DSample(self.sampleRefuel);
710 else
711 Utils.stop3DSample(self.sampleRefuel);
712 end;
713 end;
714 end
715end
716
717function Motorized:addFuelFillTrigger(trigger)
718 if table.getn(self.fuelFillTriggers) == 0 then
719 g_currentMission:addActivatableObject(self.motorizedFillActivatable);
720 end;
721 table.insert(self.fuelFillTriggers, trigger);
722end;
723
724function Motorized:removeFuelFillTrigger(trigger)
725 for i=1, table.getn(self.fuelFillTriggers) do
726 if self.fuelFillTriggers[i] == trigger then
727 table.remove(self.fuelFillTriggers, i);
728 break;
729 end;
730 end;
731 if table.getn(self.fuelFillTriggers) == 0 or trigger == self.fuelFillTrigger then
732 if self.isServer then
733 self:setIsFuelFilling(false);
734 end;
735 if table.getn(self.fuelFillTriggers) == 0 then
736 g_currentMission:removeActivatableObject(self.motorizedFillActivatable);
737 end
738 end;
739end;
740
741function Motorized:developmentReloadFromXML(xmlFile)
742 if self.motorizedNode ~= nil then
743 removeAllDifferentials(self.motorizedNode);
744 end
745 Motorized.loadDifferentials(self, xmlFile);
746 Motorized.loadMotor(self, xmlFile);
747
748 if self.isClient then
749 Motorized.loadExhaustEffects(self, xmlFile, self.exhaustEffects);
750 end;
751end;
752
753MotorizedRefuelActivatable = {}
754local MotorizedRefuelActivatable_mt = Class(MotorizedRefuelActivatable);
755
756function MotorizedRefuelActivatable:new(motorized)
757 local self = {};
758 setmetatable(self, MotorizedRefuelActivatable_mt);
759
760 self.motorized = motorized;
761 self.activateText = "unknown";
762
763 return self;
764end;
765
766
767function MotorizedRefuelActivatable:getIsActivatable()
768 if self.motorized:getIsActiveForInput(false) and self.motorized.fuelFillLevel-1 < self.motorized.fuelCapacity then
769 -- find the first trigger which is activable
770 for i=1, table.getn(self.motorized.fuelFillTriggers) do
771 local trigger = self.motorized.fuelFillTriggers[i];
772 if trigger:getIsActivatable(self.motorized) then
773 self:updateActivateText();
774 return true;
775 end
776 end
777 end
778 return false;
779end;
780
781function MotorizedRefuelActivatable:onActivateObject()
782 self.motorized:setIsFuelFilling(not self.motorized.isFuelFilling);
783 self:updateActivateText();
784 g_currentMission:addActivatableObject(self);
785end;
786
787function MotorizedRefuelActivatable:drawActivate()
788 g_currentMission:enableHudIcon("refuel", 5);
789end;
790
791function MotorizedRefuelActivatable:updateActivateText()
792 if self.motorized.isFuelFilling then
793 self.activateText = string.format(g_i18n:getText("stop_Refuel"), self.motorized.typeDesc);
794 else
795 self.activateText = string.format(g_i18n:getText("Refuel"), self.motorized.typeDesc);
796 end;
797end;
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
Script Dokumentation Übersicht