73 | function Drivable:onLoad(savegame) |
74 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.steering#index", "vehicle.drivable.steeringWheel#node") --FS17 to FS19 |
75 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.steering#node", "vehicle.drivable.steeringWheel#node") --FS17 to FS19 |
76 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cruiseControl", "vehicle.drivable.cruiseControl") --FS17 to FS19 |
77 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.indoorHud.cruiseControl", "vehicle.drivable.dashboards.dashboard with valueType 'cruiseControl'") --FS17 to FS19 |
78 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.showChangeToolSelectionHelp") --FS17 to FS19 |
79 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.maxRotatedTimeSpeed#value") --FS17 to FS19 |
80 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.speedRotScale#scale", "vehicle.drivable.speedRotScale#scale") --FS17 to FS19 |
81 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.speedRotScale#offset", "vehicle.drivable.speedRotScale#offset") --FS17 to FS19 |
82 | |
83 | local spec = self.spec_drivable |
84 | |
85 | spec.showToolSelectionHud = true |
86 | |
87 | spec.doHandbrake = false |
88 | spec.doHandbrakeSend = false |
89 | spec.reverserDirection = 1 |
90 | |
91 | -- attributes set by action events |
92 | spec.lastInputValues = {} |
93 | spec.lastInputValues.axisAccelerate = 0 |
94 | spec.lastInputValues.axisBrake = 0 |
95 | spec.lastInputValues.axisSteer = 0 |
96 | spec.lastInputValues.axisSteerIsAnalog = false |
97 | spec.lastInputValues.axisSteerDeviceCategory = InputDevice.CATEGORY.UNKNOWN |
98 | spec.lastInputValues.cruiseControlValue = 0 |
99 | spec.lastInputValues.cruiseControlState = 0 |
100 | |
101 | -- 'final' attrbutes calculated |
102 | spec.axisForward = 0 |
103 | spec.axisForwardSend = 0 |
104 | |
105 | spec.axisSide = 0 |
106 | spec.axisSideSend = 0 |
107 | spec.axisSideLast = 0 |
108 | |
109 | spec.speedRotScale = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.drivable.speedRotScale#scale"), 80) |
110 | spec.speedRotScaleOffset = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.drivable.speedRotScale#offset"), 0.7) |
111 | |
112 | local motor = self:getMotor() |
113 | spec.cruiseControl = {} |
114 | spec.cruiseControl.maxSpeed = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.drivable.cruiseControl#maxSpeed"), math.ceil(motor:getMaximumForwardSpeed()*3.6)) |
115 | spec.cruiseControl.minSpeed = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.drivable.cruiseControl#minSpeed"), math.min(1, spec.cruiseControl.maxSpeed)) |
116 | spec.cruiseControl.speed = spec.cruiseControl.maxSpeed |
117 | spec.cruiseControl.isActive = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.drivable.cruiseControl#enabled"), true) |
118 | spec.cruiseControl.state = Drivable.CRUISECONTROL_STATE_OFF |
119 | spec.cruiseControl.topSpeedTime = 2000 |
120 | spec.cruiseControl.changeDelay = 250 |
121 | spec.cruiseControl.changeCurrentDelay = 0 |
122 | spec.cruiseControl.changeMultiplier = 1 |
123 | spec.cruiseControl.speedSent = spec.cruiseControl.speed |
124 | |
125 | local node = I3DUtil.indexToObject( self.components, getXMLString(self.xmlFile, "vehicle.drivable.steeringWheel#node"), self.i3dMappings) |
126 | if node ~= nil then |
127 | spec.steeringWheel = {} |
128 | spec.steeringWheel.node = node |
129 | local _,ry,_ = getRotation(spec.steeringWheel.node) |
130 | spec.steeringWheel.lastRotation = ry |
131 | spec.steeringWheel.indoorRotation = math.rad( Utils.getNoNil( getXMLFloat(self.xmlFile, "vehicle.drivable.steeringWheel#indoorRotation"), 0 ) ) |
132 | spec.steeringWheel.outdoorRotation = math.rad( Utils.getNoNil( getXMLFloat(self.xmlFile, "vehicle.drivable.steeringWheel#outdoorRotation"), 0 ) ) |
133 | end |
134 | |
135 | if self.loadDashboardsFromXML ~= nil then |
136 | local dashKey = "vehicle.drivable.dashboards" |
137 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "cruiseControl", |
138 | valueObject = spec.cruiseControl, |
139 | valueFunc = "speed"}) |
140 | |
141 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "directionForward", |
142 | valueObject = self, |
143 | valueFunc = "getIsDrivingForward"}) |
144 | |
145 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "directionBackward", |
146 | valueObject = self, |
147 | valueFunc = "getIsDrivingBackward"}) |
148 | |
149 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "movingDirection", |
150 | valueObject = self, |
151 | valueFunc = "getDrivingDirection", |
152 | minFunc = -1, |
153 | maxFunc = 1}) |
154 | |
155 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "cruiseControlActive", |
156 | valueObject = spec.cruiseControl, |
157 | valueFunc = "state", |
158 | valueCompare = Drivable.CRUISECONTROL_STATE_ACTIVE}) |
159 | |
160 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "accelerationAxis", |
161 | valueObject = self, |
162 | valueFunc = "getAccelerationAxis"}) |
163 | |
164 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "decelerationAxis", |
165 | valueObject = self, |
166 | valueFunc = "getDecelerationAxis"}) |
167 | |
168 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "ac_decelerationAxis", |
169 | valueObject = self, |
170 | valueFunc = "getAcDecelerationAxis"}) |
171 | |
172 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "steeringAngle", |
173 | valueObject = self, |
174 | valueFunc = "getDashboardSteeringAxis", |
175 | minFunc = -1, |
176 | maxFunc = 1}) |
177 | end |
178 | |
179 | if self.isClient then |
180 | spec.samples = {} |
181 | spec.samples.waterSplash = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.drivable.sounds", "waterSplash", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
182 | if self.isClient and g_isDevelopmentVersion then |
183 | if spec.samples.waterSplash == nil then |
184 | g_logManager:xmlDevWarning(self.configFileName, "Missing drivable waterSplash sound") |
185 | end |
186 | end |
187 | end |
188 | |
189 | if savegame ~= nil then |
190 | local maxSpeed = getXMLInt(savegame.xmlFile, savegame.key..".drivable#cruiseControl") |
191 | self:setCruiseControlMaxSpeed(maxSpeed) |
192 | end |
193 | |
194 | spec.dirtyFlag = self:getNextDirtyFlag() |
195 | end |
678 | function Drivable:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection) |
679 | if self.isClient then |
680 | local spec = self.spec_drivable |
681 | |
682 | spec.toggleCruiseControlEvent = nil |
683 | self:clearActionEventsTable(spec.actionEvents) |
684 | |
685 | local entered = true |
686 | if self.getIsEntered ~= nil then |
687 | entered = self:getIsEntered() |
688 | end |
689 | |
690 | local _, actionEventId |
691 | |
692 | local levelVery = g_isPresentationVersionShowDrivingHelp and GS_PRIO_VERY_HIGHT or GS_PRIO_VERY_LOW |
693 | local level = g_isPresentationVersionShowDrivingHelp and GS_PRIO_HIGH or GS_PRIO_LOW |
694 | local visibility = g_isPresentationVersionShowDrivingHelp |
695 | |
696 | if self:getIsActiveForInput(true, true) and entered then |
697 | if not self:getIsAIActive() then |
698 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.AXIS_ACCELERATE_VEHICLE, self, Drivable.actionEventAccelerate, false, false, true, true, nil) |
699 | g_inputBinding:setActionEventTextPriority(actionEventId, levelVery) |
700 | g_inputBinding:setActionEventTextVisibility(actionEventId, visibility) |
701 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.AXIS_BRAKE_VEHICLE, self, Drivable.actionEventBrake, false, false, true, true, nil) |
702 | g_inputBinding:setActionEventTextPriority(actionEventId, levelVery) |
703 | g_inputBinding:setActionEventTextVisibility(actionEventId, visibility) |
704 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.AXIS_MOVE_SIDE_VEHICLE, self, Drivable.actionEventSteer, false, false, true, true, nil) |
705 | g_inputBinding:setActionEventTextPriority(actionEventId, levelVery) |
706 | g_inputBinding:setActionEventTextVisibility(actionEventId, visibility) |
707 | g_inputBinding:setActionEventText(actionEventId, g_i18n:getText("action_steer")) |
708 | |
709 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.TOGGLE_CRUISE_CONTROL, self, Drivable.actionEventCruiseControlState, false, true, true, true, nil) |
710 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_LOW) |
711 | spec.toggleCruiseControlEvent = actionEventId |
712 | end |
713 | |
714 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.AXIS_CRUISE_CONTROL, self, Drivable.actionEventCruiseControlValue, false, true, true, true, nil) |
715 | g_inputBinding:setActionEventText(actionEventId, g_i18n:getText("action_changeCruiseControlLevel")) |
716 | g_inputBinding:setActionEventTextPriority(actionEventId, level) |
717 | end |
718 | end |
719 | end |
287 | function Drivable:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
288 | local spec = self.spec_drivable |
289 | |
290 | -- update inputs on client side for basic controls |
291 | if self.isClient then |
292 | if self.getIsEntered ~= nil and self:getIsEntered() then |
293 | if self:getIsActiveForInput(true, true) then |
294 | if self:getIsVehicleControlledByPlayer() then |
295 | spec.doHandbrake = false |
296 | |
297 | -- gas and brake pedal |
298 | local axisForward = MathUtil.clamp((spec.lastInputValues.axisAccelerate - spec.lastInputValues.axisBrake), -1, 1) |
299 | spec.axisForward = axisForward |
300 | |
301 | -- steering |
302 | local speedFactor = 1.0 |
303 | local sensitivitySetting = g_gameSettings:getValue(GameSettings.SETTING.STEERING_SENSITIVITY) |
304 | |
305 | if spec.lastInputValues.axisSteerIsAnalog then |
306 | local isArticulatedSteering = self.spec_articulatedAxis ~= nil and self.spec_articulatedAxis.componentJoint ~= nil |
307 | if isArticulatedSteering then |
308 | speedFactor = 1.5 |
309 | else |
310 | speedFactor = 2.5 |
311 | end |
312 | |
313 | -- only use steering speed for gamepads |
314 | if spec.lastInputValues.axisSteerDeviceCategory == InputDevice.CATEGORY.GAMEPAD then |
315 | speedFactor = speedFactor * sensitivitySetting |
316 | end |
317 | else |
318 | if spec.lastInputValues.axisSteer == 0 then |
319 | local rotateBackSpeedSetting = g_gameSettings:getValue(GameSettings.SETTING.STEERING_BACK_SPEED) / 10 |
320 | |
321 | -- if the setting is '1' we use the same speed as we use for steering |
322 | -- if the setting is smaller '1' we reduce the steering angle depending on the driving speed |
323 | if rotateBackSpeedSetting < 1 and self.speedDependentRotateBack then |
324 | local speed = self:getLastSpeed() / 36 |
325 | local setting = rotateBackSpeedSetting / 0.5 |
326 | speedFactor = speedFactor * math.min(speed * setting, 1) |
327 | end |
328 | |
329 | speedFactor = speedFactor * (self.autoRotateBackSpeed or 1) / 1.5 |
330 | else |
331 | speedFactor = math.min(1.0/(self.lastSpeed*spec.speedRotScale+spec.speedRotScaleOffset), 1); |
332 | speedFactor = speedFactor * sensitivitySetting |
333 | end |
334 | end |
335 | |
336 | local steeringDuration = (self.wheelSteeringDuration or 1) * 1000 |
337 | local rotDelta = (dt / steeringDuration) * speedFactor |
338 | |
339 | if spec.lastInputValues.axisSteer > spec.axisSide then |
340 | spec.axisSide = math.min(spec.lastInputValues.axisSteer, spec.axisSide + rotDelta) |
341 | elseif spec.lastInputValues.axisSteer < spec.axisSide then |
342 | spec.axisSide = math.max(spec.lastInputValues.axisSteer, spec.axisSide - rotDelta) |
343 | end |
344 | else |
345 | spec.axisForward = 0 |
346 | if self.rotatedTime < 0 then |
347 | spec.axisSide = self.rotatedTime / -self.maxRotTime / self:getSteeringDirection() |
348 | else |
349 | spec.axisSide = self.rotatedTime / self.minRotTime / self:getSteeringDirection() |
350 | end |
351 | end |
352 | else |
353 | spec.doHandbrake = true |
354 | spec.axisForward = 0 |
355 | end |
356 | |
357 | -- prepare for next frame |
358 | spec.lastInputValues.axisAccelerate = 0 |
359 | spec.lastInputValues.axisBrake = 0 |
360 | spec.lastInputValues.axisSteer = 0 |
361 | |
362 | -- prepare network update |
363 | if spec.axisForward ~= spec.axisForwardSend or spec.axisSide ~= spec.axisSideSend or spec.doHandbrake ~= spec.doHandbrakeSend then |
364 | spec.axisForwardSend = spec.axisForward |
365 | spec.axisSideSend = spec.axisSide |
366 | spec.doHandbrakeSend = spec.doHandbrake |
367 | self:raiseDirtyFlags(spec.dirtyFlag) |
368 | end |
369 | end |
370 | end |
371 | |
372 | -- update inputs on client side for cruise control |
373 | if self.isClient then |
374 | if self.getIsEntered ~= nil and self:getIsEntered() then |
375 | -- cruise control state |
376 | local inputValue = spec.lastInputValues.cruiseControlState |
377 | spec.lastInputValues.cruiseControlState = 0 |
378 | |
379 | if inputValue == 1 then |
380 | if spec.cruiseControl.topSpeedTime == Drivable.CRUISECONTROL_FULL_TOGGLE_TIME then |
381 | if spec.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF then |
382 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE) |
383 | else |
384 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF) |
385 | end |
386 | end |
387 | |
388 | if spec.cruiseControl.topSpeedTime > 0 then |
389 | spec.cruiseControl.topSpeedTime = spec.cruiseControl.topSpeedTime - dt |
390 | if spec.cruiseControl.topSpeedTime < 0 then |
391 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_FULL) |
392 | end |
393 | end |
394 | else |
395 | spec.cruiseControl.topSpeedTime = Drivable.CRUISECONTROL_FULL_TOGGLE_TIME |
396 | end |
397 | |
398 | -- cruise control value |
399 | local lastCruiseControlValue = spec.lastInputValues.cruiseControlValue |
400 | spec.lastInputValues.cruiseControlValue = 0 |
401 | |
402 | if lastCruiseControlValue ~= 0 then |
403 | spec.cruiseControl.changeCurrentDelay = spec.cruiseControl.changeCurrentDelay - (dt * spec.cruiseControl.changeMultiplier) |
404 | spec.cruiseControl.changeMultiplier = math.min(spec.cruiseControl.changeMultiplier + (dt * 0.003), 10) |
405 | |
406 | if spec.cruiseControl.changeCurrentDelay < 0 then |
407 | spec.cruiseControl.changeCurrentDelay = spec.cruiseControl.changeDelay |
408 | |
409 | local dir = MathUtil.sign(lastCruiseControlValue) |
410 | |
411 | local speed = spec.cruiseControl.speed + dir |
412 | self:setCruiseControlMaxSpeed(speed) |
413 | |
414 | if spec.cruiseControl.speed ~= spec.cruiseControl.speedSent then |
415 | if g_server ~= nil then |
416 | g_server:broadcastEvent(SetCruiseControlSpeedEvent:new(self, spec.cruiseControl.speed), nil, nil, self) |
417 | else |
418 | g_client:getServerConnection():sendEvent(SetCruiseControlSpeedEvent:new(self, spec.cruiseControl.speed)) |
419 | end |
420 | spec.cruiseControl.speedSent = spec.cruiseControl.speed |
421 | end |
422 | end |
423 | else |
424 | spec.cruiseControl.changeCurrentDelay = 0 |
425 | spec.cruiseControl.changeMultiplier = 1 |
426 | end |
427 | |
428 | end |
429 | end |
430 | |
431 | local isControlled = self.getIsControlled ~= nil and self:getIsControlled() |
432 | |
433 | -- update vehicle physics on server side |
434 | if self:getIsVehicleControlledByPlayer() then |
435 | if self.isServer then |
436 | if isControlled then |
437 | -- lock max speed to working tool |
438 | local speed,_ = self:getSpeedLimit(true) |
439 | if spec.cruiseControl.state == Drivable.CRUISECONTROL_STATE_ACTIVE then |
440 | speed = math.min(speed, spec.cruiseControl.speed) |
441 | end |
442 | self:getMotor():setSpeedLimit(speed) |
443 | |
444 | self:updateVehiclePhysics(spec.axisForward, spec.axisSide, spec.doHandbrake, dt) |
445 | end |
446 | end |
447 | end |
448 | |
449 | -- just a visual update of the steering wheel |
450 | if self.isClient and isControlled then |
451 | self:updateSteeringWheel(spec.steeringWheel, dt, 1) |
452 | end |
453 | end |
32 | function Drivable.registerFunctions(vehicleType) |
33 | SpecializationUtil.registerFunction(vehicleType, "updateSteeringWheel", Drivable.updateSteeringWheel) |
34 | SpecializationUtil.registerFunction(vehicleType, "setCruiseControlState", Drivable.setCruiseControlState) |
35 | SpecializationUtil.registerFunction(vehicleType, "setCruiseControlMaxSpeed", Drivable.setCruiseControlMaxSpeed) |
36 | SpecializationUtil.registerFunction(vehicleType, "getCruiseControlState", Drivable.getCruiseControlState) |
37 | SpecializationUtil.registerFunction(vehicleType, "getCruiseControlSpeed", Drivable.getCruiseControlSpeed) |
38 | SpecializationUtil.registerFunction(vehicleType, "getCruiseControlMaxSpeed", Drivable.getCruiseControlMaxSpeed) |
39 | SpecializationUtil.registerFunction(vehicleType, "getAxisForward", Drivable.getAxisForward) |
40 | SpecializationUtil.registerFunction(vehicleType, "getAccelerationAxis", Drivable.getAccelerationAxis) |
41 | SpecializationUtil.registerFunction(vehicleType, "getDecelerationAxis", Drivable.getDecelerationAxis) |
42 | SpecializationUtil.registerFunction(vehicleType, "getCruiseControlAxis", Drivable.getCruiseControlAxis) |
43 | SpecializationUtil.registerFunction(vehicleType, "getAcDecelerationAxis", Drivable.getAcDecelerationAxis) |
44 | SpecializationUtil.registerFunction(vehicleType, "getDashboardSteeringAxis", Drivable.getDashboardSteeringAxis) |
45 | SpecializationUtil.registerFunction(vehicleType, "setReverserDirection", Drivable.setReverserDirection) |
46 | SpecializationUtil.registerFunction(vehicleType, "getReverserDirection", Drivable.getReverserDirection) |
47 | SpecializationUtil.registerFunction(vehicleType, "getSteeringDirection", Drivable.getSteeringDirection) |
48 | SpecializationUtil.registerFunction(vehicleType, "getIsDrivingForward", Drivable.getIsDrivingForward) |
49 | SpecializationUtil.registerFunction(vehicleType, "getIsDrivingBackward", Drivable.getIsDrivingBackward) |
50 | SpecializationUtil.registerFunction(vehicleType, "getDrivingDirection", Drivable.getDrivingDirection) |
51 | SpecializationUtil.registerFunction(vehicleType, "getIsVehicleControlledByPlayer", Drivable.getIsVehicleControlledByPlayer) |
52 | SpecializationUtil.registerFunction(vehicleType, "updateVehiclePhysics", Drivable.updateVehiclePhysics) |
53 | end |
748 | function Drivable:updateVehiclePhysics(axisForward, axisSide, doHandbrake, dt) |
749 | local spec = self.spec_drivable |
750 | |
751 | axisForward = axisForward |
752 | axisSide = self:getSteeringDirection() * axisSide |
753 | |
754 | local acceleration = 0 |
755 | |
756 | if self:getIsMotorStarted() and self:getMotorStartTime() <= g_currentMission.time then |
757 | acceleration = axisForward |
758 | if math.abs(acceleration) > 0 then |
759 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF) |
760 | end |
761 | if spec.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_OFF then |
762 | acceleration = 1.0 |
763 | end |
764 | end |
765 | if not self:getCanMotorRun() then |
766 | acceleration = 0 |
767 | if self:getIsMotorStarted() then |
768 | self:stopMotor() |
769 | end |
770 | end |
771 | |
772 | -- only update steering if a player is in the vehicle |
773 | if self.getIsControlled ~= nil and self:getIsControlled() then |
774 | local targetRotatedTime = 0 |
775 | if self.maxRotTime ~= nil and self.minRotTime ~= nil then |
776 | if axisSide < 0 then |
777 | -- 0 to maxRotTime |
778 | targetRotatedTime = math.min(-self.maxRotTime * axisSide, self.maxRotTime) |
779 | else |
780 | -- 0 to minRotTime |
781 | targetRotatedTime = math.max(self.minRotTime * axisSide, self.minRotTime) |
782 | end |
783 | end |
784 | self.rotatedTime = targetRotatedTime |
785 | end |
786 | |
787 | if self.firstTimeRun then |
788 | if self.spec_wheels ~= nil then |
789 | WheelsUtil.updateWheelsPhysics(self, dt, self.lastSpeedReal*self.movingDirection, acceleration, doHandbrake, g_currentMission.missionInfo.stopAndGoBraking) |
790 | end |
791 | end |
792 | |
793 | return acceleration |
794 | end |