LUADOC - Farming Simulator 17

Printable Version

Script v1.4.4.0

Engine v7.0.0.2

Foundation Reference

Drivable

Description
Specialization class for Drivables
Functions

prerequisitesPresent

Description
Checks if all prerequisite specializations are loaded
Definition
prerequisitesPresent(table specializations)
Arguments
tablespecializationsspecializations
Return Values
booleanhasPrerequisitetrue if all prerequisite specializations are loaded
Code
25function Drivable.prerequisitesPresent(specializations)
26 return SpecializationUtil.hasSpecialization(Motorized, specializations) and
27 SpecializationUtil.hasSpecialization(Steerable, specializations) and
28 SpecializationUtil.hasSpecialization(Lights, specializations) and
29 SpecializationUtil.hasSpecialization(AttacherJoints, specializations);
30end

preLoad

Description
Called before loading
Definition
preLoad(table savegame)
Arguments
tablesavegamesavegame
Code
35function Drivable:preLoad(savegame)
36 self.setOperatingTime = Utils.overwrittenFunction(self.setOperatingTime, Drivable.setOperatingTime);
37 self.getRpmLimit = Utils.overwrittenFunction(self.getRpmLimit, Drivable.getRpmLimit);
38 self.getDirtMultiplier = Utils.overwrittenFunction(self.getDirtMultiplier, Drivable.getDirtMultiplier);
39 self.setCruiseControlState = Drivable.setCruiseControlState;
40 self.setCruiseControlMaxSpeed = Drivable.setCruiseControlMaxSpeed;
41 self.toggleLowerAllImplements = Drivable.toggleLowerAllImplements;
42 self.lowerImplementByJointIndex = Drivable.lowerImplementByJointIndex;
43 self.setMirrorVisible = Drivable.setMirrorVisible;
44 self.loadAttacherJointFromXML = Utils.overwrittenFunction(self.loadAttacherJointFromXML, Drivable.loadAttacherJointFromXML);
45end

load

Description
Called on loading
Definition
load(table savegame)
Arguments
tablesavegamesavegame
Code
50function Drivable:load(savegame)
51 Drivable.loadSettingsFromXML(self, self.xmlFile);
52
53 self.isDrivable = true;
54 self.steeringEnabled = true;
55 self.nonTabbable = false
56
57 self.axisSmoothTime = 500;
58 self.axisForward = 0;
59 self.lastDigitalForward = 0;
60 self.axisForwardIsAnalog = false;
61 self.axisSide = 0;
62 self.axisSideIsAnalog = false;
63 self.doHandbrake = false;
64 self.reverserDirection = 1;
65
66 self.tickDt = 30;
67
68 -- mirrors
69 self.mirrors = {};
70 local useMirrors = g_gameSettings:getValue("maxNumMirrors") > 0;
71 local i = 0;
72 while true do
73 local key = string.format("vehicle.mirrors.mirror(%d)", i);
74 if not hasXMLProperty(self.xmlFile, key) then
75 break;
76 end
77 local node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, key.."#index"));
78 if node ~= nil then
79 local prio = Utils.getNoNil(getXMLInt(self.xmlFile, key.."#prio"), 2)
80 setReflectionMapObjectMasks(node, 2147483776, 2155872256, true); --0x80000080, 0x80800000
81 if getObjectMask(node) == 0 then
82 -- Mirrors should not be visible in other mirrors
83 setObjectMask(node, 16711807); -- 0x00FF007F
84 end
85
86 if useMirrors then
87 table.insert(self.mirrors, {node=node, prio=prio, cosAngle=1});
88 else
89 setVisibility(node, false)
90 end
91 end
92 i = i + 1;
93 end
94
95 self:setMirrorVisible(self.cameras[self.camIndex].useMirror);
96
97 self.cruiseControl = {};
98 self.cruiseControl.maxSpeed = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.cruiseControl#maxSpeed"), math.ceil(self.motor:getMaximumForwardSpeed()*3.6));
99 self.cruiseControl.minSpeed = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.cruiseControl#minSpeed"), math.min(1, self.cruiseControl.maxSpeed));
100 self.cruiseControl.speed = self.cruiseControl.maxSpeed;
101 self.cruiseControl.isActive = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.cruiseControl#enabled"), true);
102 self.cruiseControl.state = Drivable.CRUISECONTROL_STATE_OFF;
103 self.cruiseControl.topSpeedTime = 2000;
104 self.cruiseControl.changeDelay = 250;
105 self.cruiseControl.changeCurrentDelay = 0;
106 self.cruiseControl.changeMultiplier = 1;
107 self.cruiseControl.speedSent = self.cruiseControl.speed;
108
109 self.lastOperatingTime = 0;
110 self.maxOperatingTime = 99999.9 * 1000 * 60 * 60;
111
112 self.operatingTimeHud = VehicleHudUtils.loadHud(self, self.xmlFile, "operatingTime");
113 self.cruiseControlHud = VehicleHudUtils.loadHud(self, self.xmlFile, "cruiseControl");
114
115 if self.cruiseControlHud ~= nil then
116 VehicleHudUtils.setHudValue(self, self.cruiseControlHud, self.cruiseControl.speed, 9999);
117 end
118
119 if self.isClient then
120 self.turnLightRepetitionCount = 0;
121
122 local useDefaultSample = true;
123 if hasXMLProperty(self.xmlFile, "vehicle.turnLightSound") then
124 useDefaultSample = false;
125 local customFile = getXMLString(self.xmlFile, "vehicle.turnLightSound#file");
126 local customFilename = Utils.getFilename(customFile, self.baseDirectory);
127 useDefaultSample = g_currentMission.defaultVehicleSounds[customFilename] ~= nil;
128 end
129 if not useDefaultSample then
130 self.sampleTurnLight = SoundUtil.loadSample(self.xmlFile, {}, "vehicle.turnLightSound", defaultSample, self.baseDirectory);
131 end
132
133 local useDefaultSample = true;
134 if hasXMLProperty(self.xmlFile, "vehicle.waterSplashSound") then
135 useDefaultSample = false;
136 local customFile = getXMLString(self.xmlFile, "vehicle.waterSplashSound#file");
137 local customFilename = Utils.getFilename(customFile, self.baseDirectory);
138 useDefaultSample = g_currentMission.defaultVehicleSounds[customFilename] ~= nil;
139 end
140 if not useDefaultSample then
141 self.sampleWaterSplash = SoundUtil.loadSample(self.xmlFile, {}, "vehicle.waterSplashSound", defaultSample, self.baseDirectory);
142 end
143
144 end
145
146 self.drivableGroundFlag = self:getNextDirtyFlag();
147
148 if savegame ~= nil then
149 local maxSpeed = getXMLInt(savegame.xmlFile, savegame.key.."#cruiseControl");
150 self:setCruiseControlMaxSpeed(maxSpeed);
151
152 local dir = getXMLInt(savegame.xmlFile, savegame.key.."#attacherJointComboDir");
153 if dir ~= nil then
154 self.attacherJointCombos.direction = dir;
155 if dir == 1 then
156 self.attacherJointCombos.currentTime = self.attacherJointCombos.duration;
157 end
158 end
159 end
160
161 self.showChangeToolSelectionHelp = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.showChangeToolSelectionHelp"), true)
162 self.showToolSelectionHud = true;
163end

loadSettingsFromXML

Description
Called on loading
Definition
loadSettingsFromXML(table savegame)
Arguments
tablesavegamesavegame
Code
168function Drivable:loadSettingsFromXML(xmlFile)
169 self.speedRotScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.speedRotScale#scale"), 80);
170 self.speedRotScaleOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.speedRotScale#offset"), 0.7);
171 self.maxRotatedTimeSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.maxRotatedTimeSpeed#value"), 1)*0.001;
172end

delete

Description
Called on deleting
Definition
delete()
Code
176function Drivable:delete()
177 if self.sampleTurnLight ~= nil then
178 SoundUtil.deleteSample(self.sampleTurnLight);
179 end
180 if self.sampleWaterSplash ~= nil then
181 SoundUtil.deleteSample(self.sampleWaterSplash);
182 end
183end

readStream

Description
Called on client side on join
Definition
readStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
189function Drivable:readStream(streamId, connection)
190 if self.cruiseControl.isActive then
191 self:setCruiseControlState(streamReadUIntN(streamId, 2), true);
192 local speed = streamReadUInt8(streamId);
193 self:setCruiseControlMaxSpeed(speed);
194 self.cruiseControl.speedSent = speed;
195 end
196
197 if self.attacherJointCombos ~= nil then
198 self.attacherJointCombos.direction = -1;
199 local dir = streamReadBool(streamId);
200 if dir then
201 self.attacherJointCombos.direction = 1;
202 self.attacherJointCombos.currentTime = self.attacherJointCombos.duration;
203 end
204 end
205end

writeStream

Description
Called on server side on join
Definition
writeStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
211function Drivable:writeStream(streamId, connection)
212 if self.cruiseControl.isActive then
213 streamWriteUIntN(streamId, self.cruiseControl.state, 2);
214 streamWriteUInt8(streamId, self.cruiseControl.speed);
215 end
216
217 if self.attacherJointCombos ~= nil then
218 streamWriteBool(streamId, self.attacherJointCombos.direction == 1);
219 end
220end

readUpdateStream

Description
Called on on update
Definition
readUpdateStream(integer streamId, integer timestamp, table connection)
Arguments
integerstreamIdstream ID
integertimestamptimestamp
tableconnectionconnection
Code
227function Drivable:readUpdateStream(streamId, timestamp, connection)
228 if connection:getIsServer() then
229 local brakeLightsVisibility = streamReadBool(streamId);
230 self:setBrakeLightsVisibility(brakeLightsVisibility);
231 local reverseLightsVisibility = streamReadBool(streamId);
232 self:setReverseLightsVisibility(reverseLightsVisibility);
233 else
234 local hasUpdate = streamReadBool(streamId);
235 if hasUpdate then
236 local axisForwardIsAnalog = streamReadBool(streamId);
237 local axisSideIsAnalog = streamReadBool(streamId);
238 local axisForward = streamReadFloat32(streamId);
239 local axisSide = streamReadFloat32(streamId);
240 local doHandbrake = streamReadBool(streamId);
241 local dt = streamReadFloat32(streamId);
242 if self.steeringEnabled and self.isControlled then
243 Drivable.updateVehiclePhysics(self, axisForward, axisForwardIsAnalog, axisSide, axisSideIsAnalog, doHandbrake, dt);
244 end
245 end
246 end
247end

writeUpdateStream

Description
Called on on update
Definition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)
Arguments
integerstreamIdstream ID
tableconnectionconnection
integerdirtyMaskdirty mask
Code
254function Drivable:writeUpdateStream(streamId, connection, dirtyMask)
255 if not connection:getIsServer() then
256 streamWriteBool(streamId, self.brakeLightsVisibility);
257 streamWriteBool(streamId, self.reverseLightsVisibility);
258 else
259 if streamWriteBool(streamId, bitAND(dirtyMask, self.drivableGroundFlag) ~= 0) then
260 streamWriteBool(streamId, self.axisForwardIsAnalog);
261 streamWriteBool(streamId, self.axisSideIsAnalog);
262 streamWriteFloat32(streamId, self.axisForward);
263 streamWriteFloat32(streamId, self.axisSide);
264 streamWriteBool(streamId, self.doHandbrake);
265 streamWriteFloat32(streamId, self.tickDt);
266 end
267 end
268end

mouseEvent

Description
Called on mouse event
Definition
mouseEvent(float posX, float posY, boolean isDown, boolean isUp, integer button)
Arguments
floatposXx position
floatposYy position
booleanisDownis mouse button down
booleanisUpis mouse button up
integerbuttonmouse button
Code
277function Drivable:mouseEvent(posX, posY, isDown, isUp, button)
278end

keyEvent

Description
Called on key event
Definition
keyEvent(integer unicode, integer sym, integer modifier, boolean isDown)
Arguments
integerunicodeunicode
integersymsym
integermodifiermodifier
booleanisDownis button down
Code
286function Drivable:keyEvent(unicode, sym, modifier, isDown)
287end

update

Description
Called on update
Definition
update(float dt)
Arguments
floatdttime since last call in ms
Code
292function Drivable:update(dt)
293 if self.isEntered and self.isClient then
294
295 local turnLightRepetitionCount = math.floor((getShaderTimeSec()*7 + math.acos(-0.2)) / (math.pi*2));
296 if self.turnLightRepetitionCount ~= nil and turnLightRepetitionCount ~= self.turnLightRepetitionCount then
297 if self.turnLightState > Lights.TURNLIGHT_OFF then
298 if self.sampleTurnLight then
299 SoundUtil.playSample(self.sampleTurnLight, 1, 0, nil);
300 else
301 SoundUtil.playSample(g_currentMission.sampleTurnLight, 1, 0, nil);
302 end
303 end
304 end
305 self.turnLightRepetitionCount = turnLightRepetitionCount;
306
307 --
308 if self.cruiseControl.isActive then
309 -- Cruise Control
310 if self:getIsActiveForInput(false, false) then
311 if InputBinding.hasEvent(InputBinding.TOGGLE_CRUISE_CONTROL) then
312 if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF then
313 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE);
314 else
315 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF);
316 end
317 self.cruiseControl.topSpeedTime = 500;
318 elseif InputBinding.isPressed(InputBinding.TOGGLE_CRUISE_CONTROL) then
319 self.cruiseControl.topSpeedTime = self.cruiseControl.topSpeedTime - dt;
320 if self.cruiseControl.topSpeedTime < 0 then
321 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_FULL);
322 end
323 else
324 self.cruiseControl.topSpeedTime = 500;
325 end
326 end
327
328 if not (g_gui:getIsGuiVisible() or g_currentMission.isPlayerFrozen or g_currentMission.inGameMessage:getIsVisible()) then --and self.selectedImplement == nil then
329 -- Cruise Control speed
330 local inputW = InputBinding.getDigitalInputAxis(InputBinding.AXIS_CRUISE_CONTROL);
331 if InputBinding.isAxisZero(inputW) then
332 inputW = InputBinding.getAnalogInputAxis(InputBinding.AXIS_CRUISE_CONTROL)
333 end
334
335 if inputW ~= 0 then
336 self.cruiseControl.changeCurrentDelay = self.cruiseControl.changeCurrentDelay - dt*self.cruiseControl.changeMultiplier;
337 self.cruiseControl.changeMultiplier = math.min(self.cruiseControl.changeMultiplier + dt*0.003, 10);
338 if self.cruiseControl.changeCurrentDelay < 0 then
339 local dir = 0;
340 if inputW > g_analogStickVTolerance then
341 dir = 1;
342 elseif inputW < -g_analogStickVTolerance then
343 dir = -1;
344 end
345
346 if dir ~= 0 then
347 self.cruiseControl.wasSpeedChanged = true;
348 self:setCruiseControlMaxSpeed(self.cruiseControl.speed + dir);
349 self.cruiseControl.changeCurrentDelay = self.cruiseControl.changeDelay;
350 end
351 end
352 else
353 self.cruiseControl.wasSpeedChanged = false;
354 self.cruiseControl.changeMultiplier = 1;
355 self.cruiseControl.changeCurrentDelay = 0;
356 end
357
358 if not self.cruiseControl.wasSpeedChanged and self.cruiseControl.speed ~= self.cruiseControl.speedSent then
359 if g_server ~= nil then
360 g_server:broadcastEvent(SetCruiseControlSpeedEvent:new(self, self.cruiseControl.speed), nil, nil, self);
361 else
362 g_client:getServerConnection():sendEvent(SetCruiseControlSpeedEvent:new(self, self.cruiseControl.speed));
363 end
364 self.cruiseControl.speedSent = self.cruiseControl.speed;
365 end
366
367 end
368 end
369
370 if not self.isHired then
371 -- do all the input handling
372 if self:getIsActiveForInput(false, false) then
373
374 if InputBinding.hasEvent(InputBinding.TOGGLE_TIPSTATE) then
375 self:handleToggleTipStateEvent();
376 end
377
378 if InputBinding.hasEvent(InputBinding.ATTACH) then
379 self:handleAttachEvent();
380 end
381
382 if InputBinding.hasEvent(InputBinding.LOWER_IMPLEMENT) then
383 self:handleLowerImplementEvent();
384 end
385
386 if InputBinding.hasEvent(InputBinding.LOWER_ALL_IMPLEMENTS) then
387 self:toggleLowerAllImplements();
388 end
389
390 if InputBinding.hasEvent(InputBinding.SWITCH_IMPLEMENT) then
391 self:selectNextSelectableImplement();
392 end
393
394 if self.hasTurnLights then
395 if InputBinding.hasEvent(InputBinding.TOGGLE_TURNLIGHT_HAZARD) then
396 local state = Lights.TURNLIGHT_OFF;
397 if self.turnLightState ~= Lights.TURNLIGHT_HAZARD then
398 state = Lights.TURNLIGHT_HAZARD;
399 end
400 self:setTurnLightState(state);
401 end
402 if InputBinding.hasEvent(InputBinding.TOGGLE_TURNLIGHT_LEFT) then
403 local state = Lights.TURNLIGHT_OFF;
404 if self.turnLightState ~= Lights.TURNLIGHT_LEFT then
405 state = Lights.TURNLIGHT_LEFT;
406 end
407 self:setTurnLightState(state);
408 end
409 if InputBinding.hasEvent(InputBinding.TOGGLE_TURNLIGHT_RIGHT) then
410 local state = Lights.TURNLIGHT_OFF;
411 if self.turnLightState ~= Lights.TURNLIGHT_RIGHT then
412 state = Lights.TURNLIGHT_RIGHT;
413 end
414 self:setTurnLightState(state);
415 end
416 end
417
418 if InputBinding.hasEvent(InputBinding.TOGGLE_BEACON_LIGHTS) then
419 self:setBeaconLightsVisibility(not self.beaconLightsActive);
420 end
421
422 end
423
424 if self:getIsActiveForInput(false, false) then
425
426 self.doHandbrake = false;
427 local axisAccelerate = InputBinding.getDigitalInputAxis(InputBinding.AXIS_ACCELERATE_VEHICLE);
428 local axisBrake = InputBinding.getDigitalInputAxis(InputBinding.AXIS_BRAKE_VEHICLE)
429 local axisForward = Utils.clamp((axisAccelerate-axisBrake)*0.5, -1, 1);
430
431 if InputBinding.isAxisZero(axisForward) then
432 axisAccelerate = InputBinding.getAnalogInputAxis(InputBinding.AXIS_ACCELERATE_VEHICLE);
433 axisBrake = InputBinding.getAnalogInputAxis(InputBinding.AXIS_BRAKE_VEHICLE)
434 self.axisForward = Utils.clamp((axisAccelerate-axisBrake)*0.5, -1, 1);
435 if not InputBinding.isAxisZero(self.axisForward) then
436 self.axisForwardIsAnalog = true;
437 end
438 self.lastDigitalForward = 0;
439 if math.abs(self.axisForward) > 0.1 and g_gui.currentGuiName ~= "ChatDialog" then
440 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF);
441 end
442 else
443 self.axisForward = axisForward;
444 self.axisForwardIsAnalog = false;
445 self.lastDigitalForward = self.axisForward;
446 if g_gui.currentGuiName ~= "ChatDialog" then
447 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF);
448 end
449 end
450 else
451 self.doHandbrake = true;
452 end
453 else
454 self.axisForward = 0;
455 self.axisForwardIsAnalog = false;
456 end
457
458 -- do mirror checks
459 if self.activeCamera ~= nil and self.activeCamera.useMirror then
460 self:setMirrorVisible(true)
461 end
462
463 if not self.isHired then
464 self.axisSide = InputBinding.getDigitalInputAxis(InputBinding.AXIS_MOVE_SIDE_VEHICLE);
465 if InputBinding.isAxisZero(self.axisSide) then
466 self.axisSide = InputBinding.getAnalogInputAxis(InputBinding.AXIS_MOVE_SIDE_VEHICLE)
467 if not InputBinding.isAxisZero(self.axisSide) then
468 self.axisSideIsAnalog = true;
469 end
470 else
471 self.axisSideIsAnalog = false;
472 end
473
474 if not self:getIsActiveForInput(false, true) then
475 if not self.axisSideIsAnalog or g_gui.currentGuiName == "PlacementScreen" then
476 self.axisSide = 0;
477 end
478 if not self.axisForwardIsAnalog or g_gui.currentGuiName == "PlacementScreen" then
479 self.axisForward = 0;
480 end
481
482 if self.steeringEnabled then
483 if g_gui:getIsGuiVisible() and g_gui.currentGuiName ~= "ChatDialog" then
484 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF);
485 end
486 end
487 end
488
489 if g_isServerStreamingVersion then self.axisSide = self.axisSide * 0.5; end -- This is the factor to slow down the steering
490 end
491
492 if self.isServer then
493 if self.steeringEnabled then
494 Drivable.updateVehiclePhysics(self, self.axisForward, self.axisForwardIsAnalog, self.axisSide, self.axisSideIsAnalog, self.doHandbrake, dt);
495 end
496 else
497 self:raiseDirtyFlags(self.drivableGroundFlag);
498 end
499 end
500
501 if self:getIsActive() then
502 -- lock max speed to working tool
503 local speed,_ = self:getSpeedLimit(true);
504 if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_ACTIVE then
505 speed = math.min(speed, self.cruiseControl.speed);
506 end
507 self.motor:setSpeedLimit(speed);
508 end
509end

updateTick

Description
Called on update tick
Definition
updateTick(float dt)
Arguments
floatdttime since last call in ms
Code
514function Drivable:updateTick(dt)
515 self.tickDt = dt;
516 if self:getIsActive() then
517 local xt,yt,zt = getTranslation(self.components[1].node);
518 local deltaWater = yt-g_currentMission.waterY+2.5;
519 if deltaWater < 0 then
520 if self.aiIsStarted then
521 self:stopAIVehicle(AIVehicle.STOP_REASON_UNKOWN);
522 end
523 self.isBroken = true;
524 g_currentMission:onSunkVehicle(self);
525
526 if self.isEntered then
527 if self:getIsActiveForSound() then
528 local volume = math.min(1, self:getLastSpeed()/30);
529 if self.sampleWaterSplash ~= nil then
530 SoundUtil.playSample(self.sampleWaterSplash, 1, 0, volume);
531 else
532 SoundUtil.playSample(g_currentMission.sampleWaterSplash, 1, 0, volume);
533 end
534 end
535 g_currentMission:onLeaveVehicle();
536 end
537 end
538 self.showWaterWarning = deltaWater < 2;
539
540 if self.attacherJointCombos ~= nil and self.attacherJointCombos.isRunning then
541 for k, joint in pairs(self.attacherJointCombos.joints) do
542 if self.attacherJointCombos.direction == 1 and self.attacherJointCombos.currentTime >= joint.time then
543 self:lowerImplementByJointIndex(joint.jointIndex, true);
544 elseif self.attacherJointCombos.direction == -1 and self.attacherJointCombos.currentTime <= self.attacherJointCombos.duration-joint.time then
545 self:lowerImplementByJointIndex(joint.jointIndex, false);
546 end
547 end
548
549 if (self.attacherJointCombos.direction == -1 and self.attacherJointCombos.currentTime == 0) or
550 (self.attacherJointCombos.direction == 1 and self.attacherJointCombos.currentTime == self.attacherJointCombos.duration) then
551 self.attacherJointCombos.isRunning = false;
552 end
553
554 self.attacherJointCombos.currentTime = Utils.clamp(self.attacherJointCombos.currentTime + dt*self.attacherJointCombos.direction, 0, self.attacherJointCombos.duration);
555 end
556 end
557end

draw

Description
Called on draw
Definition
draw()
Code
561function Drivable:draw()
562
563 if not self.isEntered then
564 return;
565 end
566
567 -- mirror debug rendering
568 --[[for k, mirror in pairs(self.mirrors) do
569 setTextAlignment(RenderText.ALIGN_LEFT);
570 renderText(0.5, 0.9-(k-1)*0.015, 0.012, string.format("%s\t: %s %.4f", getName(mirror.node), tostring(getVisibility(mirror.node)), math.deg(math.acos(mirror.cosAngle))))
571
572 local x,y,z = getWorldTranslation(mirror.node)
573 Utils.renderTextAtWorldPosition(x,y,z, string.format("%s: %.3f", getName(mirror.node), math.deg(math.acos(mirror.cosAngle))), 0.015, 0)
574 end]]
575
576 g_currentMission:drawVehicleHud(self);
577
578 if self.showWaterWarning then
579 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_dontDriveIntoWater"), 2000);
580 end
581
582 if not self.isHired then
583 if self == g_currentMission.controlledVehicle then
584 if g_currentMission.trailerInTipRange ~= nil and (g_currentMission.trailerInTipRange.getFillLevel ~= nil and g_currentMission.trailerInTipRange:getFillLevel() > 0) then
585 g_currentMission:enableHudIcon("tip", 4);
586 g_currentMission:addHelpButtonText(g_currentMission.trailerInTipRange.tipText, InputBinding.TOGGLE_TIPSTATE, nil, GS_PRIO_VERY_HIGH);
587 end
588
589 -- enable attachment icon if needed
590 if (g_currentMission.attachableInMountRange ~= nil and g_currentMission.attachableInMountRangeVehicle.attacherJoints[g_currentMission.attachableInMountRangeIndex].jointIndex == 0) then
591 g_currentMission:enableHudIcon("attach", 3);
592 g_currentMission:addHelpButtonText(g_i18n:getText("action_attach"), InputBinding.ATTACH, nil, GS_PRIO_VERY_HIGH);
593 else
594 -- if something is detachable is attached, show button help
595 if self:detachingIsPossible() then
596 g_currentMission:addHelpButtonText(g_i18n:getText("action_detach"), InputBinding.ATTACH, nil, GS_PRIO_VERY_HIGH);
597 end
598 end
599
600 if self.selectedImplement ~= nil and self.selectedImplement.object ~= nil and self.selectedImplement.object.attacherVehicle ~= nil and not self.isHired then
601 local object = self.selectedImplement.object;
602 local jointDesc = object.attacherVehicle.attacherJoints[self.selectedImplement.jointDescIndex];
603
604 if object.attacherJoint.allowsLowering and jointDesc.allowsLowering and object.foldMiddleAnimTime == nil then
605 if jointDesc.moveDown then
606 g_currentMission:addHelpButtonText(string.format(g_i18n:getText("action_liftOBJECT"), object.typeDesc), InputBinding.LOWER_IMPLEMENT, nil, GS_PRIO_HIGH);
607 else
608 g_currentMission:addHelpButtonText(string.format(g_i18n:getText("action_lowerOBJECT"), object.typeDesc), InputBinding.LOWER_IMPLEMENT, nil, GS_PRIO_HIGH);
609 end
610 end
611 end
612 end
613
614 if #self.beaconLights > 0 then
615 g_currentMission:addHelpButtonText(g_i18n:getText("input_TOGGLE_BEACON_LIGHTS"), InputBinding.TOGGLE_BEACON_LIGHTS, nil, GS_PRIO_VERY_LOW);
616 end
617
618 if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF then
619 g_currentMission:addHelpButtonText(g_i18n:getText("action_activateCruiseControl"), InputBinding.TOGGLE_CRUISE_CONTROL, nil, GS_PRIO_LOW);
620 else
621 g_currentMission:addHelpButtonText(g_i18n:getText("action_deactivateCruiseControl"), InputBinding.TOGGLE_CRUISE_CONTROL, nil, GS_PRIO_LOW);
622 end
623
624 g_currentMission:addHelpButtonText(g_i18n:getText("action_changeCruiseControlLevel"), InputBinding.AXIS_CRUISE_CONTROL, nil, GS_PRIO_LOW);
625
626 local numDirectImplements = table.getn(self.attachedImplements);
627 local hasMoreThan1Selectable = (numDirectImplements > 1) or (self:getIsSelectable() and numDirectImplements > 0);
628 if not hasMoreThan1Selectable then
629 for _, implement in pairs(self.attachedImplements) do
630 if implement.object ~= nil and table.getn(implement.object.attachedImplements) > 0 then
631 hasMoreThan1Selectable = true;
632 break;
633 end
634 end
635 end
636
637 if hasMoreThan1Selectable and self.showChangeToolSelectionHelp then
638 g_currentMission:addHelpButtonText(g_i18n:getText("action_changeToolSelection"), InputBinding.SWITCH_IMPLEMENT, nil, GS_PRIO_NORMAL);
639 end
640 end
641
642 if GS_IS_CONSOLE_VERSION then
643 g_currentMission:addHelpButtonText(g_i18n:getText("input_RADIO_TOGGLE"), InputBinding.RADIO_TOGGLE, nil, GS_PRIO_VERY_LOW);
644 if g_gameSettings:getValue("radioIsActive") then
645 g_currentMission:addHelpButtonText(g_i18n:getText("input_RADIO_NEXT_CHANNEL"), InputBinding.RADIO_NEXT_CHANNEL, nil, GS_PRIO_VERY_LOW);
646 g_currentMission:addHelpButtonText(g_i18n:getText("input_RADIO_PREVIOUS_CHANNEL"), InputBinding.RADIO_PREVIOUS_CHANNEL, nil, GS_PRIO_VERY_LOW);
647 end
648 end
649end

onEnter

Description
Called on enter vehicle
Definition
onEnter(boolean isControlling)
Arguments
booleanisControllingis player controlling the vehicle
Code
654function Drivable:onEnter(isControlling)
655 self:onActivateAttachments();
656end

onLeave

Description
Called on leaving the vehicle
Definition
onLeave()
Code
660function Drivable:onLeave()
661 --if self:getDeactivateOnLeave() then
662 if self.isServer then
663 if next(self.differentials) ~= nil and self.motorizedNode ~= nil then
664 setVehicleProps(self.motorizedNode, 0.0, 0.0, 0.0, self.motor:getMaxClutchTorque(), self.motor:getRotInertia(), self.motor:getDampingRate());
665 end
666 for _,wheel in pairs(self.wheels) do
667 setWheelShapeProps(wheel.node, wheel.wheelShape, 0, self.motor:getBrakeForce()*wheel.brakeFactor, 0, wheel.rotationDamping);
668 end
669 end
670 -- end
671
672 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF);
673
674 self:setMirrorVisible(false);
675end

setCruiseControlState

Description
Changes cruise control state
Definition
setCruiseControlState(integer state, boolean noEventSend)
Arguments
integerstatenew state
booleannoEventSendno event send
Code
681function Drivable:setCruiseControlState(state, noEventSend)
682 if self.cruiseControl.state ~= state then
683 self.cruiseControl.state = state;
684 if not self.isServer and (noEventSend == nil or not noEventSend) then
685 g_client:getServerConnection():sendEvent(SetCruiseControlStateEvent:new(self, state));
686 end
687 end
688end

setCruiseControlMaxSpeed

Description
Sets cruise control max speed
Definition
setCruiseControlMaxSpeed(float speed)
Arguments
floatspeednew max speed
Code
693function Drivable:setCruiseControlMaxSpeed(speed)
694 if speed ~= nil then
695 speed = Utils.clamp(speed, self.cruiseControl.minSpeed, self.cruiseControl.maxSpeed);
696 if self.cruiseControl.speed ~= speed then
697 self.cruiseControl.speed = speed;
698 if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_FULL then
699 self.cruiseControl.state = Drivable.CRUISECONTROL_STATE_ACTIVE;
700 end
701 end
702
703 if self.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF then
704 if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_ACTIVE then
705 self.motor:setSpeedLimit(self.cruiseControl.speed);
706 else
707 self.motor:setSpeedLimit(math.huge);
708 end
709 end
710
711 if self.cruiseControlHud ~= nil then
712 VehicleHudUtils.setHudValue(self, self.cruiseControlHud, self.cruiseControl.speed, 9999);
713 end
714 end
715
716end

getRpmLimit

Description
Returns rpm limit
Definition
getRpmLimit()
Code
720function Drivable:getRpmLimit(superFunc)
721 if superFunc ~= nil then
722 return superFunc(self);
723 end
724 return math.huge;
725end

getDirtMultiplier

Description
Returns current dirt multiplier
Definition
getDirtMultiplier()
Return Values
floatdirtMultipliercurrent dirt multiplier
Code
730function Drivable:getDirtMultiplier(superFunc)
731 local multiplier = 0;
732 if superFunc ~= nil then
733 multiplier = multiplier + superFunc(self);
734 end
735
736 return multiplier;
737end

setMirrorVisible

Description
Changes visibility of mirrors
Definition
setMirrorVisible(boolean visible)
Arguments
booleanvisiblevisible
Code
742function Drivable:setMirrorVisible(visible)
743 if next(self.mirrors) == nil then
744 return
745 end
746 if visible then
747 local numVisibleMirrors = 0
748 for _, mirror in pairs(self.mirrors) do
749 -- only use mirrors which are visible on screen
750 if getIsInCameraFrustum(mirror.node, self.activeCamera.cameraNode, g_presentedScreenAspectRatio) then
751 -- calculate angle between mirror and camera
752 local dirX, dirY, dirZ = worldToLocal(self.activeCamera.cameraNode, getWorldTranslation(mirror.node))
753
754 dirY = dirY * g_screenAspectRatio
755 local length = Utils.vector3Length(dirX, dirY, dirZ)
756 mirror.cosAngle = -dirZ / length
757 else
758 mirror.cosAngle = math.huge
759 end
760 end
761
762 -- sort mirrors based on prio and angle
763 table.sort(self.mirrors,
764 function(mirror1, mirror2)
765 if mirror1.prio == mirror2.prio then
766 -- the bigger cosAngle, the smaller the angle to the z-axis
767 return mirror1.cosAngle > mirror2.cosAngle
768 else
769 return mirror1.prio < mirror2.prio
770 end
771 end)
772
773 local maxNumMirrors = g_gameSettings:getValue("maxNumMirrors")
774 -- show first mirrors within the limit
775 for _, mirror in ipairs(self.mirrors) do
776 if mirror.cosAngle ~= math.huge and numVisibleMirrors < maxNumMirrors then
777 setVisibility(mirror.node, true)
778 numVisibleMirrors = numVisibleMirrors + 1
779 else
780 setVisibility(mirror.node, false)
781 end
782 end
783 else
784 for _, mirror in pairs(self.mirrors) do
785 setVisibility(mirror.node, false);
786 end
787 end
788end

onCameraChanged

Description
Called on camera change
Definition
onCameraChanged(table camera, integer camIndex)
Arguments
tablecameranew camera
integercamIndexindex of new camera
Code
794function Drivable:onCameraChanged(camera, camIndex)
795 self:setMirrorVisible(camera.useMirror);
796end

stopMotor

Description
Called on motor stop
Definition
stopMotor(boolean noEventSend)
Arguments
booleannoEventSendno event send
Code
801function Drivable:stopMotor(noEventSend)
802 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF, true);
803end

getSaveAttributesAndNodes

Description
Returns attributes and nodes to save
Definition
getSaveAttributesAndNodes(table nodeIdent)
Arguments
tablenodeIdentnode ident
Return Values
stringattributesattributes
stringnodesnodes
Code
810function Drivable:getSaveAttributesAndNodes(nodeIdent)
811 local attributes = 'cruiseControl="'..tostring(self.cruiseControl.speed)..'" ';
812
813 if self.attacherJointCombos ~= nil then
814 attributes = attributes .. ' attacherJointComboDir="'..tostring(self.attacherJointCombos.direction)..'"';
815 end
816
817 local nodes = "";
818
819 return attributes, nodes;
820end

updateVehiclePhysics

Description
Update vehicle physics
Definition
updateVehiclePhysics(float axisForward, boolean axisForwardIsAnalog, float axisSide, boolean axisSideIsAnalog, boolean doHandbrake, float dt)
Arguments
floataxisForwardaxis forward value
booleanaxisForwardIsAnalogforward axis is analog
floataxisSideside axis
booleanaxisSideIsAnalogside axis is analog
booleandoHandbrakedo handbrake
floatdttime since lsat call in ms
Code
830function Drivable.updateVehiclePhysics(self, axisForward, axisForwardIsAnalog, axisSide, axisSideIsAnalog, doHandbrake, dt)
831
832 axisForward = axisForward;
833 axisSide = self.reverserDirection * axisSide;
834
835 local acceleration = 0;
836 if self.isMotorStarted and self.motorStartTime <= g_currentMission.time then
837 acceleration = -axisForward;
838 if math.abs(acceleration) > 0.8 then
839 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF, true);
840 end
841 if self.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF then
842 acceleration = 1.0;
843 end
844 end
845 if self.fuelFillLevel == 0 then
846 acceleration = 0;
847 end
848
849 -- only update steering if a player is in the vehicle
850 if self.isControlled then
851 local inputAxisX = axisSide;
852 if axisSideIsAnalog then
853 local targetRotatedTime = 0;
854 if inputAxisX < 0 then
855 -- 0 to maxRotTime
856 targetRotatedTime = math.min(-self.maxRotTime*inputAxisX, self.maxRotTime);
857 else
858 -- 0 to minRotTime
859 targetRotatedTime = math.max(self.minRotTime*inputAxisX, self.minRotTime);
860 end
861 local maxTime = self.maxRotatedTimeSpeed*dt;
862 if math.abs(targetRotatedTime-self.rotatedTime) > maxTime then
863 if targetRotatedTime > self.rotatedTime then
864 self.rotatedTime = self.rotatedTime + maxTime;
865 else
866 self.rotatedTime = self.rotatedTime - maxTime;
867 end
868 else
869 self.rotatedTime = targetRotatedTime;
870 end
871 else
872 local rotScale = math.min(1.0/(self.lastSpeed*self.speedRotScale+self.speedRotScaleOffset), 1);
873 if inputAxisX < 0 then
874 self.rotatedTime = math.min(self.rotatedTime - dt*0.001*inputAxisX*rotScale, self.maxRotTime);
875 elseif inputAxisX > 0 then
876 self.rotatedTime = math.max(self.rotatedTime - dt*0.001*inputAxisX*rotScale, self.minRotTime);
877 else
878 if self.autoRotateBackSpeed ~= 0 then
879 if self.rotatedTime > 0 then
880 self.rotatedTime = math.max(self.rotatedTime - dt*0.001*self.autoRotateBackSpeed*rotScale, 0);
881 else
882 self.rotatedTime = math.min(self.rotatedTime + dt*0.001*self.autoRotateBackSpeed*rotScale, 0);
883 end
884 end
885 end
886 end
887 end
888
889 if self.firstTimeRun then
890 if self.motorType == "locomotive" then
891 self.motor:updateInput(dt, acceleration);
892 else
893 self.motor:setRpmLimit( self:getRpmLimit() );
894 WheelsUtil.updateWheelsPhysics(self, dt, self.lastSpeedReal, acceleration, doHandbrake, self.requiredDriveMode)
895 end
896 end
897end

toggleLowerAllImplements

Description
Lowers all implements
Definition
toggleLowerAllImplements(boolean noEventSend)
Arguments
booleannoEventSendno event send
Code
902function Drivable:toggleLowerAllImplements(noEventSend)
903 if self.attacherJointCombos ~= nil then
904 DrivableToggleLowerAllEvent.sendEvent(self, noEventSend);
905 self.attacherJointCombos.direction = self.attacherJointCombos.direction * -1;
906 self.attacherJointCombos.isRunning = true;
907 end
908end

onReverseDirectionChanged

Description
Called on reverse direction change
Definition
onReverseDirectionChanged(integer direction)
Arguments
integerdirectiondirection [-1, 0, 1]
Code
913function Drivable:onReverseDirectionChanged(direction)
914 if self.attacherJointCombos ~= nil then
915 for k, joint in pairs(self.attacherJointCombos.joints) do
916 joint.time = math.abs(joint.time - self.attacherJointCombos.duration)
917 end
918 end
919end

lowerImplementByJointIndex

Description
Lower implement by joint desc index
Definition
lowerImplementByJointIndex(integer jointDescIndex, boolean doLowering)
Arguments
integerjointDescIndexindex of attacher joint
booleandoLoweringdo lowering
Code
925function Drivable:lowerImplementByJointIndex(jointDescIndex, doLowering)
926 local implementIndex = self:getImplementIndexByJointDescIndex(jointDescIndex);
927 if implementIndex ~= nil then
928 local implement = self.attachedImplements[implementIndex];
929 if implement ~= nil then
930 implement.object:onLowerAll(doLowering, jointDescIndex);
931 end
932 end
933end

setOperatingTime

Description
Set operating time
Definition
setOperatingTime(float operatingTime, boolean isLoading)
Arguments
floatoperatingTimeoperating time in ms
booleanisLoadingfunction called on loading
Code
939function Drivable:setOperatingTime(superFunc, operatingTime, isLoading)
940 if superFunc ~= nil then
941 superFunc(self, operatingTime, isLoading)
942 end;
943
944 if self.operatingTimeHud ~= nil and math.abs(self.lastOperatingTime-self.operatingTime) > 1000*60 then
945 local operatingTime = self.operatingTime
946 if operatingTime > self.maxOperatingTime then
947 operatingTime = 0;
948 end
949
950 local minutes = operatingTime / (1000 * 60);
951 local hours = math.floor(minutes / 60);
952 minutes = math.floor((minutes - hours * 60) / 6);
953 local minutesString = string.format("%02d", minutes*10);
954 VehicleHudUtils.setHudValue(self, self.operatingTimeHud, tonumber(hours.."."..minutesString), self.maxOperatingTime);
955 self.lastOperatingTime = self.operatingTime;
956 end
957end

loadAttacherJointFromXML

Description
Load attacher joint from xml
Definition
loadAttacherJointFromXML(table attacherJoint, integer xmlFile, string baseName, integer index)
Arguments
tableattacherJointattacherJoint
integerxmlFileid of xml object
stringbaseNamebaseName
integerindexindex of attacher joint
Return Values
booleansuccesssuccess
Code
966function Drivable:loadAttacherJointFromXML(superFunc, attacherJoint, xmlFile, baseName, index)
967 if superFunc ~= nil then
968 if not superFunc(self, attacherJoint, xmlFile, baseName, index) then
969 return false;
970 end;
971 end;
972
973 if self.attacherJointCombos == nil then
974 self.attacherJointCombos = {}
975 self.attacherJointCombos.duration = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.attacherJoints#comboDuration"), 2) * 1000;
976 self.attacherJointCombos.currentTime = 0;
977 self.attacherJointCombos.direction = -1;
978 self.attacherJointCombos.isRunning = false;
979 self.attacherJointCombos.joints = {};
980 end
981
982 local t = getXMLFloat(xmlFile, baseName .. "#comboTime")
983 if t ~= nil then
984 table.insert(self.attacherJointCombos.joints, {jointIndex=index+1, time=Utils.clamp(t, 0, 1)*self.attacherJointCombos.duration});
985 end
986
987 return true;
988end