Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- |
2 | -- Drivable |
3 | -- Specialization class for Drivables |
4 | -- |
5 | -- @author Stefan Geiger |
6 | -- @date 30/11/08 |
7 | -- |
8 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
9 | |
10 | source("dataS/scripts/vehicles/specializations/SetCruiseControlStateEvent.lua"); |
11 | source("dataS/scripts/vehicles/specializations/SetCruiseControlSpeedEvent.lua"); |
12 | source("dataS/scripts/vehicles/specializations/DrivableToggleLowerAllEvent.lua"); |
13 | |
14 | Drivable = {}; |
15 | Drivable.CRUISECONTROL_STATE_OFF = 0; |
16 | Drivable.CRUISECONTROL_STATE_ACTIVE = 1; |
17 | Drivable.CRUISECONTROL_STATE_FULL = 2; |
18 | |
19 | |
20 | function Drivable.prerequisitesPresent(specializations) |
21 | return SpecializationUtil.hasSpecialization(Motorized, specializations) and SpecializationUtil.hasSpecialization(Steerable, specializations); |
22 | end; |
23 | |
24 | function Drivable:load(xmlFile) |
25 | |
26 | self.getRpmLimit = Utils.overwrittenFunction(self.getRpmLimit, Drivable.getRpmLimit); |
27 | self.setCruiseControlState = Drivable.setCruiseControlState; |
28 | self.setCruiseControlMaxSpeed = Drivable.setCruiseControlMaxSpeed; |
29 | self.setMirrorVisible = Drivable.setMirrorVisible; |
30 | self.lowerImplementByJointIndex = Drivable.lowerImplementByJointIndex; |
31 | self.toggleLowerAllImplements = Drivable.toggleLowerAllImplements; |
32 | self.addOperatingTime = Drivable.addOperatingTime; |
33 | self.getDirtMultiplier = Utils.overwrittenFunction(self.getDirtMultiplier, Drivable.getDirtMultiplier); |
34 | |
35 | self.steering = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.steering#index")); |
36 | Drivable.loadSettingsFromXML(self, xmlFile); |
37 | |
38 | self.steeringEnabled = true; |
39 | |
40 | self.axisSmoothTime = 500; |
41 | self.axisForward = 0; |
42 | self.lastDigitalForward = 0; |
43 | self.axisForwardIsAnalog = false; |
44 | self.axisSide = 0; |
45 | self.axisSideIsAnalog = false; |
46 | self.reverserDirection = 1; |
47 | |
48 | -- mirrors |
49 | self.mirrors = {}; |
50 | local i = 0; |
51 | while true do |
52 | local key = string.format("vehicle.mirrors.mirror(%d)", i); |
53 | if not hasXMLProperty(xmlFile, key) then |
54 | break; |
55 | end |
56 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index")); |
57 | table.insert(self.mirrors, node); |
58 | i = i + 1; |
59 | end; |
60 | self:setMirrorVisible(false); |
61 | self.mirrorAvailable = g_settingsRearMirrors and g_rearMirrorsAvailable; -- show mirrors only on high profile |
62 | if self.mirrorAvailable and self.cameras[self.camIndex].useMirror then |
63 | self:setMirrorVisible(true); |
64 | else |
65 | self:setMirrorVisible(false); |
66 | end; |
67 | |
68 | |
69 | self.attacherJointLowerCombo = {}; |
70 | self.attacherJointLowerCombo.duration = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.attacherJointLowerCombo#duration"), 2) * 1000; |
71 | self.attacherJointLowerCombo.currentTime = 0; |
72 | self.attacherJointLowerCombo.direction = -1; |
73 | self.attacherJointLowerCombo.isRunning = false; |
74 | self.attacherJointLowerCombo.joints = {}; |
75 | local i=0; |
76 | while true do |
77 | local baseName = string.format("vehicle.attacherJointLowerCombo.attacherJoint(%d)", i); |
78 | if not hasXMLProperty(xmlFile, baseName) then |
79 | break; |
80 | end; |
81 | |
82 | local attacherJointIndex = getXMLInt(xmlFile, baseName .. "#index"); |
83 | if attacherJointIndex ~= nil and self.attacherJoints[attacherJointIndex] ~= nil then |
84 | local t = Utils.clamp(Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#time"), 0), 0, 1); |
85 | table.insert(self.attacherJointLowerCombo.joints, {jointIndex=attacherJointIndex, t=t*self.attacherJointLowerCombo.duration}); |
86 | end; |
87 | i = i + 1; |
88 | end; |
89 | |
90 | if table.getn(self.attacherJointLowerCombo.joints) == 0 then |
91 | self.attacherJointLowerCombo = nil; |
92 | end; |
93 | |
94 | |
95 | self.cruiseControl = {}; |
96 | self.cruiseControl.maxSpeed = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.cruiseControl#maxSpeed"), math.ceil(self.motor:getMaximumForwardSpeed()*3.6)); |
97 | self.cruiseControl.minSpeed = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.cruiseControl#minSpeed"), math.min(1, self.cruiseControl.maxSpeed)); |
98 | self.cruiseControl.speed = self.cruiseControl.maxSpeed; |
99 | --self.cruiseControl.tempSpeed = self.cruiseControl.speed; |
100 | self.cruiseControl.isActive = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.cruiseControl#enabled"), true); |
101 | self.cruiseControl.state = Drivable.CRUISECONTROL_STATE_OFF; |
102 | self.cruiseControl.topSpeedTime = 2000; |
103 | self.cruiseControl.changeDelay = 250; |
104 | self.cruiseControl.changeCurrentDelay = 0; |
105 | self.cruiseControl.changeMultiplier = 1; |
106 | self.cruiseControl.speedSent = self.cruiseControl.speed; |
107 | |
108 | self.operatingTime = 0; |
109 | self.maxOperatingTime = 99999.9 * 1000 * 60 * 60; |
110 | |
111 | if self.isClient then |
112 | local defaultSample = nil; |
113 | if #self.turnSignals.left > 0 or #self.turnSignals.right > 0 then |
114 | defaultSample = "$data/vehicles/shared/blinker.wav"; |
115 | end; |
116 | self.sampleTurnSignal = Utils.loadSample(xmlFile, {}, "vehicle.turnSignalSound", defaultSample, self.baseDirectory); |
117 | self.turnSignalRepetitionCount = 0; |
118 | end; |
119 | |
120 | self.drivableGroundFlag = self:getNextDirtyFlag(); |
121 | end; |
122 | |
123 | function Drivable:loadSettingsFromXML(xmlFile) |
124 | self.speedRotScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.speedRotScale#scale"), 80); |
125 | self.speedRotScaleOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.speedRotScale#offset"), 0.7); |
126 | self.maxRotatedTimeSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.maxRotatedTimeSpeed#value"), 1)*0.001; |
127 | end; |
128 | |
129 | function Drivable:postLoad(xmlFile) |
130 | end; |
131 | |
132 | function Drivable:delete() |
133 | end; |
134 | |
135 | function Drivable:readStream(streamId, connection) |
136 | if self.cruiseControl.isActive then |
137 | self:setCruiseControlState(streamReadUIntN(streamId, 2), true); |
138 | local speed = streamReadUInt8(streamId); |
139 | self:setCruiseControlMaxSpeed(speed); |
140 | self.cruiseControl.speedSent = speed; |
141 | end; |
142 | |
143 | if self.attacherJointLowerCombo ~= nil then |
144 | self.attacherJointLowerCombo.direction = -1; |
145 | local dir = streamReadBool(streamId); |
146 | if dir then |
147 | self.attacherJointLowerCombo.direction = 1; |
148 | self.attacherJointLowerCombo.currentTime = self.attacherJointLowerCombo.duration; |
149 | end; |
150 | end; |
151 | |
152 | self:addOperatingTime(streamReadFloat32(streamId), true); |
153 | end; |
154 | |
155 | function Drivable:writeStream(streamId, connection) |
156 | if self.cruiseControl.isActive then |
157 | streamWriteUIntN(streamId, self.cruiseControl.state, 2); |
158 | streamWriteUInt8(streamId, self.cruiseControl.speed); |
159 | end; |
160 | |
161 | if self.attacherJointLowerCombo ~= nil then |
162 | streamWriteBool(streamId, self.attacherJointLowerCombo.direction == 1); |
163 | end; |
164 | |
165 | streamWriteFloat32(streamId, self.operatingTime); |
166 | end; |
167 | |
168 | function Drivable:readUpdateStream(streamId, timestamp, connection) |
169 | --if not self.isServer and not self.isEntered or self.isServer and self.isEntered then |
170 | if connection:getIsServer() then |
171 | local brakeLightsVisibility = streamReadBool(streamId); |
172 | self:setBrakeLightsVisibility(brakeLightsVisibility); |
173 | local reverseLightsVisibility = streamReadBool(streamId); |
174 | self:setReverseLightsVisibility(reverseLightsVisibility); |
175 | else |
176 | local hasUpdate = streamReadBool(streamId); |
177 | if hasUpdate then |
178 | local axisForwardIsAnalog = streamReadBool(streamId); |
179 | local axisSideIsAnalog = streamReadBool(streamId); |
180 | local axisForward = streamReadFloat32(streamId); |
181 | local axisSide = streamReadFloat32(streamId); |
182 | local dt = streamReadFloat32(streamId); |
183 | if self.steeringEnabled and self.isControlled then |
184 | Drivable.updateVehiclePhysics(self, axisForward, axisForwardIsAnalog, axisSide, axisSideIsAnalog, dt); |
185 | end; |
186 | end; |
187 | end; |
188 | end; |
189 | |
190 | function Drivable:writeUpdateStream(streamId, connection, dirtyMask) |
191 | if not connection:getIsServer() then |
192 | streamWriteBool(streamId, self.brakeLightsVisibility); |
193 | streamWriteBool(streamId, self.reverseLightsVisibility); |
194 | else |
195 | if streamWriteBool(streamId, bitAND(dirtyMask, self.drivableGroundFlag) ~= 0) then |
196 | streamWriteBool(streamId, self.axisForwardIsAnalog); |
197 | streamWriteBool(streamId, self.axisSideIsAnalog); |
198 | streamWriteFloat32(streamId, self.axisForward); |
199 | streamWriteFloat32(streamId, self.axisSide); |
200 | streamWriteFloat32(streamId, self.tickDt); |
201 | end; |
202 | end; |
203 | end; |
204 | |
205 | function Drivable:mouseEvent(posX, posY, isDown, isUp, button) |
206 | end; |
207 | |
208 | function Drivable:keyEvent(unicode, sym, modifier, isDown) |
209 | end; |
210 | |
211 | function Drivable:update(dt) |
212 | if (self:getIsActive() and self.isMotorStarted) or self.isHired then |
213 | self:addOperatingTime(dt); |
214 | end; |
215 | |
216 | if self.isEntered and self.isClient and not self.isHired then |
217 | -- do all the input handling |
218 | if self:getIsActiveForInput(false) then |
219 | if self.cruiseControl.isActive then |
220 | -- Cruise Control |
221 | if InputBinding.hasEvent(InputBinding.TOGGLE_CRUISE_CONTROL) then |
222 | if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF then |
223 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE); |
224 | else |
225 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF); |
226 | end; |
227 | self.cruiseControl.topSpeedTime = 1000; |
228 | elseif InputBinding.isPressed(InputBinding.TOGGLE_CRUISE_CONTROL) then |
229 | self.cruiseControl.topSpeedTime = self.cruiseControl.topSpeedTime - dt; |
230 | if self.cruiseControl.topSpeedTime < 0 then |
231 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_FULL); |
232 | end; |
233 | else |
234 | self.cruiseControl.topSpeedTime = 1000; |
235 | end; |
236 | |
237 | local inputW = InputBinding.getDigitalInputAxis(InputBinding.AXIS_CRUISE_CONTROL); |
238 | if InputBinding.isAxisZero(inputW) then |
239 | inputW = InputBinding.getAnalogInputAxis(InputBinding.AXIS_CRUISE_CONTROL) |
240 | end; |
241 | |
242 | if inputW ~= 0 then |
243 | self.cruiseControl.changeCurrentDelay = self.cruiseControl.changeCurrentDelay - dt*self.cruiseControl.changeMultiplier; |
244 | self.cruiseControl.changeMultiplier = math.min(self.cruiseControl.changeMultiplier + dt*0.0005, 5); |
245 | if self.cruiseControl.changeCurrentDelay < 0 then |
246 | local dir = 0; |
247 | if inputW > g_analogStickVTolerance then |
248 | dir = 1; |
249 | elseif inputW < -g_analogStickVTolerance then |
250 | dir = -1; |
251 | end; |
252 | |
253 | if dir ~= 0 then |
254 | self.cruiseControl.wasSpeedChanged = true; |
255 | self:setCruiseControlMaxSpeed(self.cruiseControl.speed + dir); |
256 | self.cruiseControl.changeCurrentDelay = self.cruiseControl.changeDelay; |
257 | end; |
258 | end; |
259 | else |
260 | self.cruiseControl.wasSpeedChanged = false; |
261 | self.cruiseControl.changeMultiplier = 1; |
262 | self.cruiseControl.changeCurrentDelay = 0; |
263 | end; |
264 | |
265 | if not self.cruiseControl.wasSpeedChanged and self.cruiseControl.speed ~= self.cruiseControl.speedSent then |
266 | if g_server ~= nil then |
267 | g_server:broadcastEvent(SetCruiseControlSpeedEvent:new(self, self.cruiseControl.speed), nil, nil, self); |
268 | else |
269 | g_client:getServerConnection():sendEvent(SetCruiseControlSpeedEvent:new(self, self.cruiseControl.speed)); |
270 | end; |
271 | self.cruiseControl.speedSent = self.cruiseControl.speed; |
272 | end; |
273 | end; |
274 | |
275 | if InputBinding.hasEvent(InputBinding.ATTACH) and not g_currentMission.inGameMessage:getIsVisible() then |
276 | self:handleAttachEvent(); |
277 | end; |
278 | |
279 | if InputBinding.hasEvent(InputBinding.LOWER_IMPLEMENT) then |
280 | self:handleLowerImplementEvent(); |
281 | end; |
282 | |
283 | if InputBinding.hasEvent(InputBinding.LOWER_ALL_IMPLEMENTS) then |
284 | self:toggleLowerAllImplements(); |
285 | end; |
286 | |
287 | if InputBinding.hasEvent(InputBinding.SWITCH_IMPLEMENT) then |
288 | self:selectNextSelectableImplement(); |
289 | end; |
290 | |
291 | if InputBinding.hasEvent(InputBinding.TOGGLE_TURNSIGNAL_HAZARD) then |
292 | local state = Vehicle.TURNSIGNAL_OFF; |
293 | if self.turnSignalState ~= Vehicle.TURNSIGNAL_HAZARD then |
294 | state = Vehicle.TURNSIGNAL_HAZARD; |
295 | end; |
296 | self:setTurnSignalState(state); |
297 | end; |
298 | if InputBinding.hasEvent(InputBinding.TOGGLE_TURNSIGNAL_LEFT) then |
299 | local state = Vehicle.TURNSIGNAL_OFF; |
300 | if self.turnSignalState ~= Vehicle.TURNSIGNAL_LEFT then |
301 | state = Vehicle.TURNSIGNAL_LEFT; |
302 | end; |
303 | self:setTurnSignalState(state); |
304 | end; |
305 | if InputBinding.hasEvent(InputBinding.TOGGLE_TURNSIGNAL_RIGHT) then |
306 | local state = Vehicle.TURNSIGNAL_OFF; |
307 | if self.turnSignalState ~= Vehicle.TURNSIGNAL_RIGHT then |
308 | state = Vehicle.TURNSIGNAL_RIGHT; |
309 | end; |
310 | self:setTurnSignalState(state); |
311 | end; |
312 | |
313 | if InputBinding.hasEvent(InputBinding.TOGGLE_BEACON_LIGHTS) then |
314 | self:setBeaconLightsVisibility(not self.beaconLightsActive); |
315 | end; |
316 | |
317 | end; |
318 | |
319 | local turnSignalRepetitionCount = math.floor((getShaderTimeSec()*7 + math.acos(-0.2)) / (math.pi*2)); |
320 | if self.turnSignalRepetitionCount ~= nil and turnSignalRepetitionCount ~= self.turnSignalRepetitionCount then |
321 | if self.turnSignalState > Vehicle.TURNSIGNAL_OFF then |
322 | Utils.playSample(self.sampleTurnSignal, 1, 0, nil); |
323 | end; |
324 | end |
325 | self.turnSignalRepetitionCount = turnSignalRepetitionCount; |
326 | |
327 | end; |
328 | end; |
329 | |
330 | function Drivable:updateTick(dt) |
331 | if self:getIsActive() then |
332 | local xt,yt,zt = getTranslation(self.components[1].node); |
333 | local deltaWater = yt-g_currentMission.waterY+2.5; |
334 | if deltaWater < 0 then |
335 | self.isBroken = true; |
336 | g_currentMission:onSunkVehicle(self); |
337 | |
338 | if self.isEntered then |
339 | g_currentMission:onLeaveVehicle(); |
340 | |
341 | if self:getIsActiveForSound() then |
342 | local volume = math.min(1, self:getLastSpeed()/30); |
343 | if self.waterSplashSample == nil then |
344 | self.waterSplashSample = createSample("waterSplashSample"); |
345 | loadSample(self.waterSplashSample, "data/maps/sounds/waterSplash.wav", false); |
346 | end; |
347 | playSample(self.waterSplashSample, 1, volume, 0); |
348 | end; |
349 | end; |
350 | end; |
351 | self.showWaterWarning = deltaWater < 2; |
352 | |
353 | if self.attacherJointLowerCombo ~= nil and self.attacherJointLowerCombo.isRunning then |
354 | for k, joint in pairs(self.attacherJointLowerCombo.joints) do |
355 | if self.attacherJointLowerCombo.direction == 1 and self.attacherJointLowerCombo.currentTime >= joint.t then |
356 | self:lowerImplementByJointIndex(joint.jointIndex, true); |
357 | elseif self.attacherJointLowerCombo.direction == -1 and self.attacherJointLowerCombo.currentTime <= self.attacherJointLowerCombo.duration-joint.t then |
358 | self:lowerImplementByJointIndex(joint.jointIndex, false); |
359 | end; |
360 | end; |
361 | |
362 | if (self.attacherJointLowerCombo.direction == -1 and self.attacherJointLowerCombo.currentTime == 0) or |
363 | (self.attacherJointLowerCombo.direction == 1 and self.attacherJointLowerCombo.currentTime == self.attacherJointLowerCombo.duration) then |
364 | self.attacherJointLowerCombo.isRunning = false; |
365 | end; |
366 | |
367 | self.attacherJointLowerCombo.currentTime = Utils.clamp(self.attacherJointLowerCombo.currentTime + dt*self.attacherJointLowerCombo.direction, 0, self.attacherJointLowerCombo.duration); |
368 | end; |
369 | |
370 | -- lock max speed to working tool |
371 | local speed,_ = self:getSpeedLimit(true); |
372 | if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_ACTIVE then |
373 | speed = math.min(speed, self.cruiseControl.speed); |
374 | end; |
375 | self.motor:setSpeedLimit(speed); --math.min(self.cruiseControl.tempSpeed, speed)); |
376 | end; |
377 | |
378 | if self.isEntered and self.isClient then |
379 | local axisForward = InputBinding.getDigitalInputAxis(InputBinding.AXIS_MOVE_FORWARD_VEHICLE); |
380 | if InputBinding.isAxisZero(axisForward) then |
381 | self.axisForward = InputBinding.getAnalogInputAxis(InputBinding.AXIS_MOVE_FORWARD_VEHICLE) |
382 | if not InputBinding.isAxisZero(self.axisForward) then |
383 | self.axisForwardIsAnalog = true; |
384 | end |
385 | self.lastDigitalForward = 0; |
386 | if math.abs(self.axisForward) > 0.1 and g_gui.currentGuiName ~= "ChatDialog" then |
387 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF); |
388 | end; |
389 | else |
390 | self.axisForward = Utils.clamp(self.lastDigitalForward + dt/self.axisSmoothTime*axisForward, -1, 1); |
391 | self.axisForwardIsAnalog = false; |
392 | self.lastDigitalForward = self.axisForward; |
393 | if g_gui.currentGuiName ~= "ChatDialog" then |
394 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF); |
395 | end; |
396 | end; |
397 | |
398 | self.axisSide = InputBinding.getDigitalInputAxis(InputBinding.AXIS_MOVE_SIDE_VEHICLE); |
399 | if InputBinding.isAxisZero(self.axisSide) then |
400 | self.axisSide = InputBinding.getAnalogInputAxis(InputBinding.AXIS_MOVE_SIDE_VEHICLE) |
401 | if not InputBinding.isAxisZero(self.axisSide) then |
402 | self.axisSideIsAnalog = true; |
403 | end |
404 | else |
405 | self.axisSideIsAnalog = false; |
406 | end; |
407 | |
408 | if not self:getIsActiveForInput(false) then |
409 | if not self.axisSideIsAnalog then |
410 | self.axisSide = 0; |
411 | end |
412 | if not self.axisForwardIsAnalog then |
413 | self.axisForward = 0; |
414 | end |
415 | |
416 | if self.steeringEnabled then |
417 | if g_gui.currentGui ~= nil and g_gui.currentGuiName ~= "ChatDialog" then |
418 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF); |
419 | end; |
420 | end; |
421 | end |
422 | |
423 | if g_isServerStreamingVersion then self.axisSide = self.axisSide * 0.5; end; -- This is the factor to slow down the steering |
424 | |
425 | if self.isServer then |
426 | if self.steeringEnabled then |
427 | Drivable.updateVehiclePhysics(self, self.axisForward, self.axisForwardIsAnalog, self.axisSide, self.axisSideIsAnalog, dt); |
428 | end; |
429 | else |
430 | self:raiseDirtyFlags(self.drivableGroundFlag); |
431 | end; |
432 | end; |
433 | end; |
434 | |
435 | function Drivable:draw() |
436 | |
437 | if not self.isEntered then |
438 | return; |
439 | end; |
440 | |
441 | g_currentMission:drawVehicleHud(self); |
442 | |
443 | setTextAlignment(RenderText.ALIGN_LEFT); |
444 | setTextBold(false); |
445 | |
446 | if self.showWaterWarning then |
447 | g_currentMission:showBlinkingWarning(g_i18n:getText("Dont_drive_to_depth_into_the_water"), 2000); |
448 | end; |
449 | |
450 | if GS_IS_CONSOLE_VERSION then |
451 | if #self.beaconLights > 0 then |
452 | g_currentMission:addHelpButtonText(g_i18n:getText("TOGGLE_BEACON_LIGHTS"), InputBinding.TOGGLE_BEACON_LIGHTS); |
453 | end; |
454 | g_currentMission:addHelpButtonText(g_i18n:getText("TOGGLE_CRUISE_CONTROL"), InputBinding.TOGGLE_CRUISE_CONTROL); |
455 | end; |
456 | |
457 | if self == g_currentMission.controlledVehicle then |
458 | if g_currentMission.trailerInTipRange ~= nil then |
459 | g_currentMission:enableHudIcon("tip", 4); |
460 | g_currentMission:addHelpButtonText(g_currentMission.trailerInTipRange.tipText, InputBinding.ATTACH); |
461 | end; |
462 | |
463 | -- enable attachment icon if needed |
464 | if (g_currentMission.attachableInMountRange ~= nil and g_currentMission.attachableInMountRangeVehicle.attacherJoints[g_currentMission.attachableInMountRangeIndex].jointIndex == 0) then |
465 | g_currentMission:enableHudIcon("attach", 3); |
466 | g_currentMission:addHelpButtonText(g_i18n:getText("Attach"), InputBinding.ATTACH); |
467 | else |
468 | -- if something is detachable is attached, show button help |
469 | if g_currentMission.trailerInTipRange == nil and self:detachingIsPossible() then |
470 | g_currentMission:addHelpButtonText(g_i18n:getText("Detach"), InputBinding.ATTACH); |
471 | end; |
472 | end; |
473 | |
474 | if self.selectedImplement ~= nil and self.selectedImplement.object ~= nil and self.selectedImplement.object.attacherVehicle ~= nil and not self.isHired then |
475 | local object = self.selectedImplement.object; |
476 | local jointDesc = object.attacherVehicle.attacherJoints[self.selectedImplement.jointDescIndex]; |
477 | |
478 | if object.attacherJoint.allowsLowering and jointDesc.allowsLowering then |
479 | if jointDesc.moveDown then |
480 | g_currentMission:addHelpButtonText(string.format(g_i18n:getText("lift_OBJECT"), object.typeDesc), InputBinding.LOWER_IMPLEMENT); |
481 | else -- if object.needsLowering then |
482 | g_currentMission:addHelpButtonText(string.format(g_i18n:getText("lower_OBJECT"), object.typeDesc), InputBinding.LOWER_IMPLEMENT); |
483 | end; |
484 | end; |
485 | end; |
486 | end; |
487 | |
488 | |
489 | -- new in console version: show common commands (toggle lights, honk, etc) in button help |
490 | if not self.isHired then |
491 | local numDirectImplements = table.getn(self.attachedImplements); |
492 | local hasMoreThan1Selectable = (numDirectImplements > 1) or (self:getIsSelectable() and numDirectImplements > 0); |
493 | if not hasMoreThan1Selectable then |
494 | for _, implement in pairs(self.attachedImplements) do |
495 | if implement.object ~= nil and table.getn(implement.object.attachedImplements) > 0 then |
496 | hasMoreThan1Selectable = true; |
497 | break; |
498 | end |
499 | end |
500 | end |
501 | |
502 | if hasMoreThan1Selectable then |
503 | g_currentMission:addHelpButtonText(g_i18n:getText("Change_tools"), InputBinding.SWITCH_IMPLEMENT); |
504 | end; |
505 | end; |
506 | end; |
507 | |
508 | function Drivable:onEnter(isControlling) |
509 | self:startMotor(true); |
510 | self:onActivateAttachments(); |
511 | |
512 | if self.mirrorAvailable and self.cameras[self.camIndex].useMirror then |
513 | self:setMirrorVisible(true); |
514 | end; |
515 | end; |
516 | |
517 | function Drivable:onLeave() |
518 | self:setBrakeLightsVisibility(false); |
519 | self:setTurnSignalState(Vehicle.TURNSIGNAL_OFF); |
520 | self:setReverseLightsVisibility(false); |
521 | |
522 | self:setBeaconLightsVisibility(false, true); |
523 | |
524 | if self.deactivateOnLeave then |
525 | if self.isServer then |
526 | if next(self.differentials) ~= nil and self.motorizedNode ~= nil then |
527 | setVehicleProps(self.motorizedNode, 0.0, 0.0, 0.0, self.motor.maxClutchTorque); |
528 | end |
529 | for _,wheel in pairs(self.wheels) do |
530 | setWheelShapeProps(wheel.node, wheel.wheelShape, 0, self.motor.brakeForce, 0); |
531 | end; |
532 | end; |
533 | self:onDeactivate(); |
534 | self:onDeactivateSounds(); |
535 | else |
536 | if self.deactivateLightsOnLeave then |
537 | self:onDeactivateAttachmentsLights(); |
538 | end; |
539 | self:onDeactivateSounds(); |
540 | end; |
541 | |
542 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF); |
543 | |
544 | if self.mirrorAvailable then |
545 | self:setMirrorVisible(false); |
546 | end; |
547 | end; |
548 | |
549 | function Drivable:setCruiseControlState(state, noEventSend) |
550 | if self.cruiseControl.state ~= state then |
551 | self.cruiseControl.state = state; |
552 | --[[ |
553 | if self.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF then |
554 | if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_ACTIVE then |
555 | self.cruiseControl.tempSpeed = self.cruiseControl.speed; |
556 | else |
557 | self.cruiseControl.tempSpeed = self.cruiseControl.maxSpeed; |
558 | end |
559 | else |
560 | self.cruiseControl.tempSpeed = math.huge; |
561 | end |
562 | ]]-- |
563 | if not self.isServer and (noEventSend == nil or not noEventSend) then |
564 | g_client:getServerConnection():sendEvent(SetCruiseControlStateEvent:new(self, state)); |
565 | end |
566 | end |
567 | end |
568 | |
569 | function Drivable:setCruiseControlMaxSpeed(speed) |
570 | if speed ~= nil then |
571 | speed = Utils.clamp(speed, self.cruiseControl.minSpeed, self.cruiseControl.maxSpeed); |
572 | if self.cruiseControl.speed ~= speed then |
573 | self.cruiseControl.speed = speed; |
574 | --self.cruiseControl.tempSpeed = speed; |
575 | if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_FULL then |
576 | self.cruiseControl.state = Drivable.CRUISECONTROL_STATE_ACTIVE; |
577 | end; |
578 | end; |
579 | |
580 | if self.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF then |
581 | if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_ACTIVE then |
582 | self.motor:setSpeedLimit(self.cruiseControl.speed); |
583 | else |
584 | self.motor:setSpeedLimit(math.huge); |
585 | end; |
586 | end; |
587 | end; |
588 | |
589 | end; |
590 | |
591 | function Drivable:getRpmLimit(superFunc) |
592 | if superFunc ~= nil then |
593 | return superFunc(self); |
594 | end; |
595 | return math.huge; |
596 | end; |
597 | |
598 | function Drivable:getDirtMultiplier(superFunc) |
599 | local multiplier = 0; |
600 | if superFunc ~= nil then |
601 | multiplier = multiplier + superFunc(self); |
602 | end; |
603 | |
604 | --[[ |
605 | for _, implement in pairs(self.attachedImplements) do |
606 | if implement.object.getDirtMultiplier ~= nil then |
607 | multiplier = math.max(multiplier, implement.object:getDirtMultiplier()); |
608 | end; |
609 | end; |
610 | ]]-- |
611 | |
612 | return multiplier; |
613 | end; |
614 | |
615 | function Drivable:setMirrorVisible(visible) |
616 | for _, mirror in pairs(self.mirrors) do |
617 | setVisibility(mirror, visible); |
618 | end; |
619 | end; |
620 | |
621 | function Drivable:stopMotor(noEventSend) |
622 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF, true); |
623 | end |
624 | |
625 | function Drivable:loadFromAttributesAndNodes(xmlFile, key, resetVehicles) |
626 | local maxSpeed = getXMLInt(xmlFile, key.."#cruiseControl"); |
627 | self:setCruiseControlMaxSpeed(maxSpeed); |
628 | |
629 | local dir = getXMLInt(xmlFile, key.."#attacherJointComboDir"); |
630 | if dir ~= nil then |
631 | self.attacherJointLowerCombo.direction = dir; |
632 | if dir == 1 then |
633 | self.attacherJointLowerCombo.currentTime = self.attacherJointLowerCombo.duration; |
634 | end; |
635 | end; |
636 | local operatingTime = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#operatingTime"), self.operatingTime) * 1000; |
637 | self:addOperatingTime(operatingTime); |
638 | |
639 | return BaseMission.VEHICLE_LOAD_OK; |
640 | end; |
641 | |
642 | function Drivable:getSaveAttributesAndNodes(nodeIdent) |
643 | local attributes = 'cruiseControl="'..tostring(self.cruiseControl.speed)..'" operatingTime="' .. tostring((self.operatingTime / 1000)) ..'"'; |
644 | |
645 | if self.attacherJointLowerCombo ~= nil then |
646 | attributes = attributes .. ' attacherJointComboDir="'..tostring(self.attacherJointLowerCombo.direction)..'"'; |
647 | end; |
648 | |
649 | local nodes = ""; |
650 | |
651 | return attributes, nodes; |
652 | end; |
653 | |
654 | function Drivable.updateVehiclePhysics(self, axisForward, axisForwardIsAnalog, axisSide, axisSideIsAnalog, dt) |
655 | |
656 | axisForward = self.reverserDirection * axisForward; |
657 | axisSide = self.reverserDirection * axisSide; |
658 | |
659 | local acceleration = 0; |
660 | if self.isMotorStarted and self.motorStartTime <= g_currentMission.time then |
661 | acceleration = -axisForward; |
662 | if math.abs(acceleration) > 0.8 then |
663 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF, true); |
664 | end; |
665 | if self.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF then |
666 | acceleration = self.reverserDirection * 1.0; |
667 | end |
668 | end; |
669 | if self.fuelFillLevel == 0 then |
670 | acceleration = 0; |
671 | end; |
672 | |
673 | local inputAxisX = axisSide; |
674 | if axisSideIsAnalog then |
675 | local targetRotatedTime = 0; |
676 | if inputAxisX < 0 then |
677 | -- 0 to maxRotTime |
678 | targetRotatedTime = math.min(-self.maxRotTime*inputAxisX, self.maxRotTime); |
679 | else |
680 | -- 0 to minRotTime |
681 | targetRotatedTime = math.max(self.minRotTime*inputAxisX, self.minRotTime); |
682 | end; |
683 | local maxTime = self.maxRotatedTimeSpeed*dt; |
684 | if math.abs(targetRotatedTime-self.rotatedTime) > maxTime then |
685 | if targetRotatedTime > self.rotatedTime then |
686 | self.rotatedTime = self.rotatedTime + maxTime; |
687 | else |
688 | self.rotatedTime = self.rotatedTime - maxTime; |
689 | end; |
690 | else |
691 | self.rotatedTime = targetRotatedTime; |
692 | end; |
693 | else |
694 | local rotScale = math.min(1.0/(self.lastSpeed*self.speedRotScale+self.speedRotScaleOffset), 1); |
695 | if inputAxisX < 0 then |
696 | self.rotatedTime = math.min(self.rotatedTime - dt*0.001*inputAxisX*rotScale, self.maxRotTime); |
697 | elseif inputAxisX > 0 then |
698 | self.rotatedTime = math.max(self.rotatedTime - dt*0.001*inputAxisX*rotScale, self.minRotTime); |
699 | else |
700 | if self.autoRotateBackSpeed ~= 0 then |
701 | if self.rotatedTime > 0 then |
702 | self.rotatedTime = math.max(self.rotatedTime - dt*0.001*self.autoRotateBackSpeed*rotScale, 0); |
703 | else |
704 | self.rotatedTime = math.min(self.rotatedTime + dt*0.001*self.autoRotateBackSpeed*rotScale, 0); |
705 | end; |
706 | end; |
707 | end; |
708 | end; |
709 | |
710 | if self.firstTimeRun then |
711 | self.motor.rpmLimit = self:getRpmLimit(); |
712 | WheelsUtil.updateWheelsPhysics(self, dt, self.lastSpeedReal, acceleration, false, self.requiredDriveMode) |
713 | end; |
714 | end; |
715 | |
716 | function Drivable:toggleLowerAllImplements(noEventSend) |
717 | if self.attacherJointLowerCombo ~= nil then |
718 | DrivableToggleLowerAllEvent.sendEvent(self, noEventSend); |
719 | self.attacherJointLowerCombo.direction = self.attacherJointLowerCombo.direction * -1; |
720 | self.attacherJointLowerCombo.isRunning = true; |
721 | end; |
722 | end; |
723 | |
724 | function Drivable:lowerImplementByJointIndex(jointDescIndex, doLowering) |
725 | local implementIndex = self:getImplementIndexByJointDescIndex(jointDescIndex); |
726 | if implementIndex ~= nil then |
727 | local implement = self.attachedImplements[implementIndex]; |
728 | if implement ~= nil then |
729 | implement.object:onLowerAll(doLowering, jointDescIndex); |
730 | end; |
731 | end; |
732 | end; |
733 | |
734 | function Drivable:addOperatingTime(addTime) |
735 | local t = math.max(Utils.getNoNil(addTime, 0), 0); |
736 | self.operatingTime = self.operatingTime + t; |
737 | |
738 | if self.operatingTime > self.maxOperatingTime then |
739 | self.operatingTime = 0; |
740 | end; |
741 | end; |
742 | |
743 | function Drivable:developmentReloadFromXML(xmlFile) |
744 | Drivable.loadSettingsFromXML(self, xmlFile); |
745 | end;
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de