32 | function Drivable.initSpecialization() |
33 | local schema = Vehicle.xmlSchema |
34 | schema:setXMLSpecializationType("Drivable") |
35 | |
36 | schema:register(XMLValueType.FLOAT, "vehicle.drivable.speedRotScale#scale", "Speed dependent steering speed scale", 80) |
37 | schema:register(XMLValueType.FLOAT, "vehicle.drivable.speedRotScale#offset", "Speed dependent steering speed offset", 0.7) |
38 | |
39 | schema:register(XMLValueType.FLOAT, "vehicle.drivable.cruiseControl#maxSpeed", "Max. cruise control speed", "Max. vehicle speed") |
40 | schema:register(XMLValueType.FLOAT, "vehicle.drivable.cruiseControl#minSpeed", "Min. cruise control speed", 1) |
41 | schema:register(XMLValueType.FLOAT, "vehicle.drivable.cruiseControl#maxSpeedReverse", "Max. cruise control speed in reverse", "Max. value reverse speed") |
42 | schema:register(XMLValueType.BOOL, "vehicle.drivable.cruiseControl#enabled", "Cruise control enabled", true) |
43 | |
44 | schema:register(XMLValueType.NODE_INDEX, "vehicle.drivable.steeringWheel#node", "Steering wheel node") |
45 | schema:register(XMLValueType.ANGLE, "vehicle.drivable.steeringWheel#indoorRotation", "Steering wheel indoor rotation", 0) |
46 | schema:register(XMLValueType.ANGLE, "vehicle.drivable.steeringWheel#outdoorRotation", "Steering wheel outdoor rotation", 0) |
47 | |
48 | schema:register(XMLValueType.BOOL, "vehicle.drivable.idleTurning#allowed", "When vehicle is not moving and steering keys are pressed turns on the same spot", false) |
49 | schema:register(XMLValueType.BOOL, "vehicle.drivable.idleTurning#updateSteeringWheel", "Update steering wheel", true) |
50 | schema:register(XMLValueType.BOOL, "vehicle.drivable.idleTurning#lockDirection", "Defines if the direction is locked until player accelerates again", true) |
51 | schema:register(XMLValueType.INT, "vehicle.drivable.idleTurning#direction", "Driving direction [-1, 1]", 1) |
52 | schema:register(XMLValueType.FLOAT, "vehicle.drivable.idleTurning#maxSpeed", "Max. speed while turning", 10) |
53 | schema:register(XMLValueType.FLOAT, "vehicle.drivable.idleTurning#steeringFactor", "Steering speed factor", 100) |
54 | schema:register(XMLValueType.NODE_INDEX, "vehicle.drivable.idleTurning.wheel(?)#node", "Wheel node to change") |
55 | schema:register(XMLValueType.ANGLE, "vehicle.drivable.idleTurning.wheel(?)#steeringAngle", "Steering angle while idle turning") |
56 | schema:register(XMLValueType.BOOL, "vehicle.drivable.idleTurning.wheel(?)#inverted", "Acceleration is inverted", false) |
57 | |
58 | Dashboard.registerDashboardXMLPaths(schema, "vehicle.drivable.dashboards", "cruiseControl | directionForward | directionBackward | movingDirection | cruiseControlActive | accelerationAxis | decelerationAxis | ac_decelerationAxis | steeringAngle") |
59 | SoundManager.registerSampleXMLPaths(schema, "vehicle.drivable.sounds", "waterSplash") |
60 | |
61 | schema:setXMLSpecializationType() |
62 | |
63 | local schemaSavegame = Vehicle.xmlSchemaSavegame |
64 | schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).drivable#cruiseControl", "Current cruise control speed") |
65 | schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).drivable#cruiseControlReverse", "Current cruise control speed reverse") |
66 | end |
130 | function Drivable:onLoad(savegame) |
131 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.steering#index", "vehicle.drivable.steeringWheel#node") --FS17 to FS19 |
132 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.steering#node", "vehicle.drivable.steeringWheel#node") --FS17 to FS19 |
133 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.cruiseControl", "vehicle.drivable.cruiseControl") --FS17 to FS19 |
134 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.indoorHud.cruiseControl", "vehicle.drivable.dashboards.dashboard with valueType 'cruiseControl'") --FS17 to FS19 |
135 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.showChangeToolSelectionHelp") --FS17 to FS19 |
136 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.maxRotatedTimeSpeed#value") --FS17 to FS19 |
137 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.speedRotScale#scale", "vehicle.drivable.speedRotScale#scale") --FS17 to FS19 |
138 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.speedRotScale#offset", "vehicle.drivable.speedRotScale#offset") --FS17 to FS19 |
139 | |
140 | local spec = self.spec_drivable |
141 | |
142 | spec.showToolSelectionHud = true |
143 | |
144 | spec.doHandbrake = false |
145 | spec.doHandbrakeSend = false |
146 | spec.reverserDirection = 1 |
147 | |
148 | -- attributes set by action events |
149 | spec.lastInputValues = {} |
150 | spec.lastInputValues.axisAccelerate = 0 |
151 | spec.lastInputValues.axisBrake = 0 |
152 | spec.lastInputValues.axisSteer = 0 |
153 | spec.lastInputValues.axisSteerIsAnalog = false |
154 | spec.lastInputValues.axisSteerDeviceCategory = InputDevice.CATEGORY.UNKNOWN |
155 | spec.lastInputValues.cruiseControlValue = 0 |
156 | spec.lastInputValues.cruiseControlState = 0 |
157 | |
158 | -- 'final' attrbutes calculated |
159 | spec.axisForward = 0 |
160 | spec.axisForwardSend = 0 |
161 | |
162 | spec.axisSide = 0 |
163 | spec.axisSideSend = 0 |
164 | spec.axisSideLast = 0 |
165 | |
166 | spec.lastIsControlled = false |
167 | |
168 | spec.speedRotScale = self.xmlFile:getValue("vehicle.drivable.speedRotScale#scale", 80) |
169 | spec.speedRotScaleOffset = self.xmlFile:getValue("vehicle.drivable.speedRotScale#offset", 0.7) |
170 | |
171 | local motor = self:getMotor() |
172 | spec.cruiseControl = {} |
173 | spec.cruiseControl.maxSpeed = self.xmlFile:getValue("vehicle.drivable.cruiseControl#maxSpeed", MathUtil.round(motor:getMaximumForwardSpeed()*3.6)) |
174 | spec.cruiseControl.minSpeed = self.xmlFile:getValue("vehicle.drivable.cruiseControl#minSpeed", math.min(1, spec.cruiseControl.maxSpeed)) |
175 | spec.cruiseControl.speed = spec.cruiseControl.maxSpeed |
176 | spec.cruiseControl.maxSpeedReverse = self.xmlFile:getValue("vehicle.drivable.cruiseControl#maxSpeedReverse", MathUtil.round(motor:getMaximumBackwardSpeed()*3.6)) |
177 | spec.cruiseControl.speedReverse = spec.cruiseControl.maxSpeedReverse |
178 | spec.cruiseControl.isActive = self.xmlFile:getValue("vehicle.drivable.cruiseControl#enabled", true) |
179 | spec.cruiseControl.state = Drivable.CRUISECONTROL_STATE_OFF |
180 | spec.cruiseControl.topSpeedTime = 1000 |
181 | spec.cruiseControl.changeDelay = 250 |
182 | spec.cruiseControl.changeCurrentDelay = 0 |
183 | spec.cruiseControl.changeMultiplier = 1 |
184 | spec.cruiseControl.speedSent = spec.cruiseControl.speed |
185 | spec.cruiseControl.speedReverseSent = spec.cruiseControl.speedReverse |
186 | |
187 | local node = self.xmlFile:getValue("vehicle.drivable.steeringWheel#node", nil, self.components, self.i3dMappings) |
188 | if node ~= nil then |
189 | spec.steeringWheel = {} |
190 | spec.steeringWheel.node = node |
191 | local _,ry,_ = getRotation(spec.steeringWheel.node) |
192 | spec.steeringWheel.lastRotation = ry |
193 | spec.steeringWheel.indoorRotation = self.xmlFile:getValue("vehicle.drivable.steeringWheel#indoorRotation", 0) |
194 | spec.steeringWheel.outdoorRotation = self.xmlFile:getValue("vehicle.drivable.steeringWheel#outdoorRotation", 0) |
195 | end |
196 | |
197 | spec.idleTurningAllowed = self.xmlFile:getValue("vehicle.drivable.idleTurning#allowed", false) |
198 | spec.idleTurningUpdateSteeringWheel = self.xmlFile:getValue("vehicle.drivable.idleTurning#updateSteeringWheel", true) |
199 | spec.idleTurningLockDirection = self.xmlFile:getValue("vehicle.drivable.idleTurning#lockDirection", true) |
200 | spec.idleTurningDrivingDirection = self.xmlFile:getValue("vehicle.drivable.idleTurning#direction", 1) |
201 | spec.idleTurningMaxSpeed = self.xmlFile:getValue("vehicle.drivable.idleTurning#maxSpeed", 10) |
202 | spec.idleTurningSteeringFactor = self.xmlFile:getValue("vehicle.drivable.idleTurning#steeringFactor", 100) |
203 | |
204 | spec.idleTurningWheels = {} |
205 | self.xmlFile:iterate("vehicle.drivable.idleTurning.wheel", function(index, key) |
206 | local wheelNode = self.xmlFile:getValue(key .. "#node", nil, self.components, self.i3dMappings) |
207 | if wheelNode ~= nil then |
208 | local entry = {} |
209 | entry.wheelNode = wheelNode |
210 | entry.steeringAngle = self.xmlFile:getValue(key .. "#steeringAngle", 0) |
211 | entry.inverted = self.xmlFile:getValue(key .. "#inverted", false) |
212 | |
213 | table.insert(spec.idleTurningWheels, entry) |
214 | end |
215 | end) |
216 | |
217 | |
218 | spec.idleTurningActive = false |
219 | spec.idleTurningDirection = 0 |
220 | |
221 | self.customSteeringAngleFunction = self.customSteeringAngleFunction or #spec.idleTurningWheels > 0 |
222 | |
223 | spec.forceFeedback = {} |
224 | spec.forceFeedback.isActive = false |
225 | spec.forceFeedback.device = nil |
226 | spec.forceFeedback.binding = nil |
227 | spec.forceFeedback.axisIndex = 0 |
228 | spec.forceFeedback.intensity = g_gameSettings:getValue(GameSettings.SETTING.FORCE_FEEDBACK) |
229 | |
230 | if self.loadDashboardsFromXML ~= nil then |
231 | local dashKey = "vehicle.drivable.dashboards" |
232 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "cruiseControl", |
233 | valueObject = spec.cruiseControl, |
234 | valueFunc = "speed"}) |
235 | |
236 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "directionForward", |
237 | valueObject = self, |
238 | valueFunc = "getIsDrivingForward"}) |
239 | |
240 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "directionBackward", |
241 | valueObject = self, |
242 | valueFunc = "getIsDrivingBackward"}) |
243 | |
244 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "movingDirection", |
245 | valueObject = self, |
246 | valueFunc = "getDrivingDirection", |
247 | minFunc = -1, |
248 | maxFunc = 1}) |
249 | |
250 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "cruiseControlActive", |
251 | valueObject = spec.cruiseControl, |
252 | valueFunc = "state", |
253 | valueCompare = Drivable.CRUISECONTROL_STATE_ACTIVE}) |
254 | |
255 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "accelerationAxis", |
256 | valueObject = self, |
257 | valueFunc = "getAccelerationAxis"}) |
258 | |
259 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "decelerationAxis", |
260 | valueObject = self, |
261 | valueFunc = "getDecelerationAxis"}) |
262 | |
263 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "ac_decelerationAxis", |
264 | valueObject = self, |
265 | valueFunc = "getAcDecelerationAxis"}) |
266 | |
267 | self:loadDashboardsFromXML(self.xmlFile, dashKey, {valueTypeToLoad = "steeringAngle", |
268 | valueObject = self, |
269 | valueFunc = "getDashboardSteeringAxis", |
270 | minFunc = -1, |
271 | maxFunc = 1}) |
272 | end |
273 | |
274 | if self.isClient then |
275 | spec.samples = {} |
276 | spec.samples.waterSplash = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.drivable.sounds", "waterSplash", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
277 | if self.isClient and g_isDevelopmentVersion then |
278 | if spec.samples.waterSplash == nil then |
279 | Logging.xmlDevWarning(self.xmlFile, "Missing drivable waterSplash sound") |
280 | end |
281 | end |
282 | end |
283 | |
284 | if savegame ~= nil then |
285 | local maxSpeed = savegame.xmlFile:getValue(savegame.key..".drivable#cruiseControl") |
286 | local maxSpeedReverse = savegame.xmlFile:getValue(savegame.key..".drivable#cruiseControlReverse") |
287 | self:setCruiseControlMaxSpeed(maxSpeed, maxSpeedReverse) |
288 | end |
289 | |
290 | spec.dirtyFlag = self:getNextDirtyFlag() |
291 | end |
986 | function Drivable:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection) |
987 | if self.isClient then |
988 | local spec = self.spec_drivable |
989 | |
990 | spec.toggleCruiseControlEvent = nil |
991 | self:clearActionEventsTable(spec.actionEvents) |
992 | |
993 | local entered = true |
994 | if self.getIsEntered ~= nil then |
995 | entered = self:getIsEntered() |
996 | end |
997 | |
998 | local _, actionEventId |
999 | |
1000 | if self:getIsActiveForInput(true, true) and entered then |
1001 | if not self:getIsAIActive() then |
1002 | _, actionEventId = self:addPoweredActionEvent(spec.actionEvents, InputAction.AXIS_ACCELERATE_VEHICLE, self, Drivable.actionEventAccelerate, false, false, true, true, nil) |
1003 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_LOW) |
1004 | g_inputBinding:setActionEventTextVisibility(actionEventId, false) |
1005 | _, actionEventId = self:addPoweredActionEvent(spec.actionEvents, InputAction.AXIS_BRAKE_VEHICLE, self, Drivable.actionEventBrake, false, false, true, true, nil) |
1006 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_LOW) |
1007 | g_inputBinding:setActionEventTextVisibility(actionEventId, false) |
1008 | _, actionEventId = self:addPoweredActionEvent(spec.actionEvents, InputAction.AXIS_MOVE_SIDE_VEHICLE, self, Drivable.actionEventSteer, false, false, true, true, nil) |
1009 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_LOW) |
1010 | g_inputBinding:setActionEventTextVisibility(actionEventId, false) |
1011 | g_inputBinding:setActionEventText(actionEventId, g_i18n:getText("action_steer")) |
1012 | |
1013 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.TOGGLE_CRUISE_CONTROL, self, Drivable.actionEventCruiseControlState, false, true, true, true, nil) |
1014 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_LOW) |
1015 | spec.toggleCruiseControlEvent = actionEventId |
1016 | end |
1017 | |
1018 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.AXIS_CRUISE_CONTROL, self, Drivable.actionEventCruiseControlValue, false, true, true, true, nil) |
1019 | g_inputBinding:setActionEventText(actionEventId, g_i18n:getText("action_changeCruiseControlLevel")) |
1020 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_LOW) |
1021 | end |
1022 | end |
1023 | end |
383 | function Drivable:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
384 | local spec = self.spec_drivable |
385 | |
386 | -- update inputs on client side for basic controls |
387 | if self.isClient then |
388 | if self.getIsEntered ~= nil and self:getIsEntered() then |
389 | if self.isActiveForInputIgnoreSelectionIgnoreAI then |
390 | if self:getIsVehicleControlledByPlayer() then |
391 | local lastSpeed = self:getLastSpeed() |
392 | spec.doHandbrake = false |
393 | |
394 | -- gas and brake pedal |
395 | local axisForward = MathUtil.clamp((spec.lastInputValues.axisAccelerate - spec.lastInputValues.axisBrake), -1, 1) |
396 | spec.axisForward = axisForward |
397 | |
398 | -- brake until we reach 1 km/h if brakeToStop is active |
399 | if spec.brakeToStop then |
400 | spec.lastInputValues.targetSpeed = 0.51 |
401 | spec.lastInputValues.targetDirection = 1 |
402 | |
403 | if lastSpeed < 1 then |
404 | spec.brakeToStop = false |
405 | |
406 | spec.lastInputValues.targetSpeed = nil |
407 | spec.lastInputValues.targetDirection = nil |
408 | end |
409 | end |
410 | |
411 | -- axis forward calculated by current target speed |
412 | if spec.lastInputValues.targetSpeed ~= nil then |
413 | local currentSpeed = lastSpeed * self.movingDirection |
414 | local targetSpeed = spec.lastInputValues.targetSpeed * spec.lastInputValues.targetDirection |
415 | |
416 | local speedDifference = targetSpeed - currentSpeed |
417 | if math.abs(speedDifference) > 0.1 and math.abs(targetSpeed) > 0.5 then |
418 | spec.axisForward = MathUtil.clamp(speedDifference * 0.1, -1, 1) |
419 | end |
420 | end |
421 | |
422 | -- steering |
423 | if self:getIsPowered() then |
424 | local speedFactor = 1.0 |
425 | local sensitivitySetting = g_gameSettings:getValue(GameSettings.SETTING.STEERING_SENSITIVITY) |
426 | |
427 | local axisSteer = spec.lastInputValues.axisSteer |
428 | |
429 | if spec.lastInputValues.axisSteerIsAnalog then |
430 | local isArticulatedSteering = self.spec_articulatedAxis ~= nil and self.spec_articulatedAxis.componentJoint ~= nil |
431 | if isArticulatedSteering then |
432 | speedFactor = 1.5 |
433 | else |
434 | speedFactor = 2.5 |
435 | end |
436 | |
437 | if GS_IS_MOBILE_VERSION then |
438 | -- Fixed speed factor on mobile, and instead control non-linearity with sensitivity slider |
439 | --speedFactor = speedFactor * math.min(1.0/(self.lastSpeed*spec.speedRotScale+spec.speedRotScaleOffset), 1) |
440 | |
441 | speedFactor = speedFactor * 1.5 |
442 | axisSteer = math.pow(math.abs(axisSteer), 1.0/sensitivitySetting) * (axisSteer >= 0 and 1 or -1) |
443 | else |
444 | -- only use steering speed for gamepads |
445 | if spec.lastInputValues.axisSteerDeviceCategory == InputDevice.CATEGORY.GAMEPAD then |
446 | speedFactor = speedFactor * sensitivitySetting |
447 | end |
448 | end |
449 | |
450 | local forceFeedback = spec.forceFeedback |
451 | if forceFeedback.isActive then |
452 | if forceFeedback.intensity > 0 then |
453 | local angleFactor = math.abs(axisSteer) |
454 | local speedForceFactor = math.max(math.min((lastSpeed-2) / 15, 1), 0) |
455 | local targetState = axisSteer - (0.2 * speedForceFactor) * MathUtil.sign(axisSteer) |
456 | local force = math.max(math.abs(targetState) * 0.4, 0.25) * speedForceFactor * angleFactor * 2 * forceFeedback.intensity |
457 | forceFeedback.device:setForceFeedback(forceFeedback.axisIndex, force, targetState) |
458 | else |
459 | forceFeedback.device:setForceFeedback(forceFeedback.axisIndex, 0, axisSteer) |
460 | end |
461 | end |
462 | else |
463 | if spec.lastInputValues.axisSteer == 0 then |
464 | local rotateBackSpeedSetting = g_gameSettings:getValue(GameSettings.SETTING.STEERING_BACK_SPEED) / 10 |
465 | |
466 | -- if the setting is '1' we use the same speed as we use for steering |
467 | -- if the setting is smaller '1' we reduce the steering angle depending on the driving speed |
468 | if rotateBackSpeedSetting < 1 and self.speedDependentRotateBack then |
469 | local speed = lastSpeed / 36 |
470 | local setting = rotateBackSpeedSetting / 0.5 |
471 | speedFactor = speedFactor * math.min(speed * setting, 1) |
472 | end |
473 | |
474 | speedFactor = speedFactor * (self.autoRotateBackSpeed or 1) / 1.5 |
475 | else |
476 | speedFactor = math.min(1.0/(self.lastSpeed*spec.speedRotScale+spec.speedRotScaleOffset), 1) |
477 | speedFactor = speedFactor * sensitivitySetting |
478 | end |
479 | end |
480 | |
481 | -- idle turning |
482 | if spec.idleTurningAllowed then |
483 | if axisForward == 0 and axisSteer ~= 0 and lastSpeed < 1 then |
484 | if not spec.idleTurningActive then |
485 | spec.idleTurningActive = true |
486 | spec.idleTurningDirection = MathUtil.sign(axisSteer) * spec.idleTurningDrivingDirection |
487 | end |
488 | end |
489 | |
490 | if axisForward ~= 0 then |
491 | if spec.idleTurningActive then |
492 | spec.idleTurningActive = false |
493 | end |
494 | end |
495 | |
496 | if spec.idleTurningActive then |
497 | if not spec.idleTurningLockDirection then |
498 | spec.idleTurningDirection = spec.idleTurningDrivingDirection |
499 | |
500 | if axisSteer == 0 then |
501 | spec.idleTurningActive = false |
502 | spec.axisSide = 0 |
503 | end |
504 | end |
505 | end |
506 | |
507 | if spec.idleTurningActive then |
508 | spec.axisForward = spec.idleTurningDirection * MathUtil.sign(axisSteer) |
509 | |
510 | axisSteer = math.abs(axisSteer) * spec.idleTurningDirection |
511 | speedFactor = speedFactor * spec.idleTurningSteeringFactor |
512 | end |
513 | end |
514 | |
515 | local steeringDuration = (self.wheelSteeringDuration or 1) * 1000 |
516 | local rotDelta = (dt / steeringDuration) * speedFactor |
517 | |
518 | if axisSteer > spec.axisSide then |
519 | spec.axisSide = math.min(axisSteer, spec.axisSide + rotDelta) |
520 | elseif axisSteer < spec.axisSide then |
521 | spec.axisSide = math.max(axisSteer, spec.axisSide - rotDelta) |
522 | end |
523 | end |
524 | else |
525 | spec.axisForward = 0 |
526 | spec.idleTurningModeActive = false |
527 | if self.rotatedTime < 0 then |
528 | spec.axisSide = self.rotatedTime / -self.maxRotTime / self:getSteeringDirection() |
529 | else |
530 | spec.axisSide = self.rotatedTime / self.minRotTime / self:getSteeringDirection() |
531 | end |
532 | end |
533 | else |
534 | spec.doHandbrake = true |
535 | spec.axisForward = 0 |
536 | spec.idleTurningModeActive = false |
537 | end |
538 | |
539 | -- prepare for next frame |
540 | spec.lastInputValues.axisAccelerate = 0 |
541 | spec.lastInputValues.axisBrake = 0 |
542 | spec.lastInputValues.axisSteer = 0 |
543 | |
544 | -- prepare network update |
545 | if spec.axisForward ~= spec.axisForwardSend or spec.axisSide ~= spec.axisSideSend or spec.doHandbrake ~= spec.doHandbrakeSend then |
546 | spec.axisForwardSend = spec.axisForward |
547 | spec.axisSideSend = spec.axisSide |
548 | spec.doHandbrakeSend = spec.doHandbrake |
549 | self:raiseDirtyFlags(spec.dirtyFlag) |
550 | end |
551 | end |
552 | end |
553 | |
554 | -- update inputs on client side for cruise control |
555 | if self.isClient then |
556 | if self.getIsEntered ~= nil and self:getIsEntered() then |
557 | -- cruise control state |
558 | local inputValue = spec.lastInputValues.cruiseControlState |
559 | spec.lastInputValues.cruiseControlState = 0 |
560 | |
561 | if inputValue == 1 then |
562 | if spec.cruiseControl.topSpeedTime == Drivable.CRUISECONTROL_FULL_TOGGLE_TIME then |
563 | if spec.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF then |
564 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE) |
565 | else |
566 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF) |
567 | end |
568 | end |
569 | |
570 | if spec.cruiseControl.topSpeedTime > 0 then |
571 | spec.cruiseControl.topSpeedTime = spec.cruiseControl.topSpeedTime - dt |
572 | if spec.cruiseControl.topSpeedTime < 0 then |
573 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_FULL) |
574 | end |
575 | end |
576 | else |
577 | spec.cruiseControl.topSpeedTime = Drivable.CRUISECONTROL_FULL_TOGGLE_TIME |
578 | end |
579 | |
580 | -- cruise control value |
581 | local lastCruiseControlValue = spec.lastInputValues.cruiseControlValue |
582 | spec.lastInputValues.cruiseControlValue = 0 |
583 | |
584 | if lastCruiseControlValue ~= 0 then |
585 | spec.cruiseControl.changeCurrentDelay = spec.cruiseControl.changeCurrentDelay - (dt * spec.cruiseControl.changeMultiplier) |
586 | spec.cruiseControl.changeMultiplier = math.min(spec.cruiseControl.changeMultiplier + (dt * 0.003), 10) |
587 | |
588 | if spec.cruiseControl.changeCurrentDelay < 0 then |
589 | spec.cruiseControl.changeCurrentDelay = spec.cruiseControl.changeDelay |
590 | |
591 | local dir = MathUtil.sign(lastCruiseControlValue) |
592 | |
593 | local speed, speedReverse = spec.cruiseControl.speed, spec.cruiseControl.speedReverse |
594 | local useReverseSpeed = self:getDrivingDirection() < 0 |
595 | if self:getReverserDirection() < 0 then |
596 | useReverseSpeed = not useReverseSpeed |
597 | end |
598 | |
599 | if useReverseSpeed then |
600 | speedReverse = speedReverse + dir |
601 | else |
602 | speed = speed + dir |
603 | end |
604 | |
605 | self:setCruiseControlMaxSpeed(speed, speedReverse) |
606 | |
607 | if spec.cruiseControl.speed ~= spec.cruiseControl.speedSent or spec.cruiseControl.speedReverse ~= spec.cruiseControl.speedReverseSent then |
608 | if g_server ~= nil then |
609 | g_server:broadcastEvent(SetCruiseControlSpeedEvent.new(self, spec.cruiseControl.speed, spec.cruiseControl.speedReverse), nil, nil, self) |
610 | else |
611 | g_client:getServerConnection():sendEvent(SetCruiseControlSpeedEvent.new(self, spec.cruiseControl.speed, spec.cruiseControl.speedReverse)) |
612 | end |
613 | spec.cruiseControl.speedSent = spec.cruiseControl.speed |
614 | spec.cruiseControl.speedReverseSent = spec.cruiseControl.speedReverse |
615 | end |
616 | end |
617 | else |
618 | spec.cruiseControl.changeCurrentDelay = 0 |
619 | spec.cruiseControl.changeMultiplier = 1 |
620 | end |
621 | |
622 | end |
623 | end |
624 | |
625 | local isControlled = self.getIsControlled ~= nil and self:getIsControlled() |
626 | |
627 | -- update vehicle physics on server side |
628 | if self:getIsVehicleControlledByPlayer() then |
629 | if self.isServer then |
630 | if isControlled then |
631 | local cruiseControlSpeed = math.huge |
632 | if spec.cruiseControl.state == Drivable.CRUISECONTROL_STATE_ACTIVE then |
633 | if self:getReverserDirection() < 0 then |
634 | cruiseControlSpeed = spec.cruiseControl.speedReverse |
635 | else |
636 | cruiseControlSpeed = spec.cruiseControl.speed |
637 | end |
638 | end |
639 | if self:getDrivingDirection() < 0 then |
640 | if self:getReverserDirection() < 0 then |
641 | cruiseControlSpeed = math.min(cruiseControlSpeed, spec.cruiseControl.speed) |
642 | else |
643 | cruiseControlSpeed = math.min(cruiseControlSpeed, spec.cruiseControl.speedReverse) |
644 | end |
645 | end |
646 | |
647 | -- while cruise control is active we interpolate between the speed while changing them |
648 | -- this reduces the peak motor load while this change happens |
649 | if cruiseControlSpeed ~= math.huge then |
650 | spec.cruiseControl.speedInterpolated = spec.cruiseControl.speedInterpolated or cruiseControlSpeed |
651 | if cruiseControlSpeed ~= spec.cruiseControl.speedInterpolated then |
652 | local diff = cruiseControlSpeed - spec.cruiseControl.speedInterpolated |
653 | local dir = MathUtil.sign(diff) |
654 | local limit = dir == 1 and math.min or math.max |
655 | spec.cruiseControl.speedInterpolated = limit(spec.cruiseControl.speedInterpolated + dt * 0.0025 * math.max(1, math.abs(diff)) * dir, cruiseControlSpeed) |
656 | cruiseControlSpeed = spec.cruiseControl.speedInterpolated |
657 | end |
658 | else |
659 | spec.cruiseControl.speedInterpolated = nil |
660 | end |
661 | |
662 | local maxSpeed, _ = self:getSpeedLimit(true) |
663 | maxSpeed = math.min(maxSpeed, cruiseControlSpeed) |
664 | |
665 | if spec.idleTurningAllowed then |
666 | if spec.idleTurningActive then |
667 | maxSpeed = math.min(maxSpeed, spec.idleTurningMaxSpeed) |
668 | end |
669 | end |
670 | |
671 | self:getMotor():setSpeedLimit(maxSpeed) |
672 | |
673 | self:updateVehiclePhysics(spec.axisForward, spec.axisSide, spec.doHandbrake, dt) |
674 | else |
675 | if spec.lastIsControlled then |
676 | SpecializationUtil.raiseEvent(self, "onVehiclePhysicsUpdate", 0, 0, true, 0) |
677 | end |
678 | end |
679 | end |
680 | end |
681 | |
682 | -- just a visual update of the steering wheel |
683 | if self.isClient and isControlled then |
684 | local allowed = true |
685 | if spec.idleTurningAllowed then |
686 | if spec.idleTurningActive and not spec.idleTurningUpdateSteeringWheel then |
687 | allowed = false |
688 | end |
689 | end |
690 | |
691 | if allowed then |
692 | self:updateSteeringWheel(spec.steeringWheel, dt, 1) |
693 | end |
694 | end |
695 | |
696 | spec.lastIsControlled = isControlled |
697 | |
698 | -- steering based on mobile device orientation |
699 | if self:getIsActiveForInput(true) then |
700 | local inputHelpMode = g_inputBinding:getInputHelpMode() |
701 | if inputHelpMode ~= GS_INPUT_HELP_MODE_GAMEPAD or GS_PLATFORM_SWITCH then |
702 | if g_gameSettings:getValue(GameSettings.SETTING.GYROSCOPE_STEERING) then |
703 | local dx, dy, dz = getGravityDirection() |
704 | local steeringValue = MathUtil.getSteeringAngleFromDeviceGravity(dx, dy, dz) |
705 | |
706 | self:setSteeringInput(steeringValue, true, InputDevice.CATEGORY.WHEEL) |
707 | end |
708 | end |
709 | end |
710 | end |
70 | function Drivable.registerFunctions(vehicleType) |
71 | SpecializationUtil.registerFunction(vehicleType, "updateSteeringWheel", Drivable.updateSteeringWheel) |
72 | SpecializationUtil.registerFunction(vehicleType, "setCruiseControlState", Drivable.setCruiseControlState) |
73 | SpecializationUtil.registerFunction(vehicleType, "setCruiseControlMaxSpeed", Drivable.setCruiseControlMaxSpeed) |
74 | SpecializationUtil.registerFunction(vehicleType, "getCruiseControlState", Drivable.getCruiseControlState) |
75 | SpecializationUtil.registerFunction(vehicleType, "getCruiseControlSpeed", Drivable.getCruiseControlSpeed) |
76 | SpecializationUtil.registerFunction(vehicleType, "getCruiseControlMaxSpeed", Drivable.getCruiseControlMaxSpeed) |
77 | SpecializationUtil.registerFunction(vehicleType, "getCruiseControlDisplayInfo", Drivable.getCruiseControlDisplayInfo) |
78 | SpecializationUtil.registerFunction(vehicleType, "getAxisForward", Drivable.getAxisForward) |
79 | SpecializationUtil.registerFunction(vehicleType, "getAccelerationAxis", Drivable.getAccelerationAxis) |
80 | SpecializationUtil.registerFunction(vehicleType, "getDecelerationAxis", Drivable.getDecelerationAxis) |
81 | SpecializationUtil.registerFunction(vehicleType, "getCruiseControlAxis", Drivable.getCruiseControlAxis) |
82 | SpecializationUtil.registerFunction(vehicleType, "getAcDecelerationAxis", Drivable.getAcDecelerationAxis) |
83 | SpecializationUtil.registerFunction(vehicleType, "getDashboardSteeringAxis", Drivable.getDashboardSteeringAxis) |
84 | SpecializationUtil.registerFunction(vehicleType, "setReverserDirection", Drivable.setReverserDirection) |
85 | SpecializationUtil.registerFunction(vehicleType, "getReverserDirection", Drivable.getReverserDirection) |
86 | SpecializationUtil.registerFunction(vehicleType, "getSteeringDirection", Drivable.getSteeringDirection) |
87 | SpecializationUtil.registerFunction(vehicleType, "getIsDrivingForward", Drivable.getIsDrivingForward) |
88 | SpecializationUtil.registerFunction(vehicleType, "getIsDrivingBackward", Drivable.getIsDrivingBackward) |
89 | SpecializationUtil.registerFunction(vehicleType, "getDrivingDirection", Drivable.getDrivingDirection) |
90 | SpecializationUtil.registerFunction(vehicleType, "getIsVehicleControlledByPlayer", Drivable.getIsVehicleControlledByPlayer) |
91 | SpecializationUtil.registerFunction(vehicleType, "updateVehiclePhysics", Drivable.updateVehiclePhysics) |
92 | SpecializationUtil.registerFunction(vehicleType, "setAccelerationPedalInput", Drivable.setAccelerationPedalInput) |
93 | SpecializationUtil.registerFunction(vehicleType, "setBrakePedalInput", Drivable.setBrakePedalInput) |
94 | SpecializationUtil.registerFunction(vehicleType, "setTargetSpeedAndDirection", Drivable.setTargetSpeedAndDirection) |
95 | SpecializationUtil.registerFunction(vehicleType, "setSteeringInput", Drivable.setSteeringInput) |
96 | SpecializationUtil.registerFunction(vehicleType, "brakeToStop", Drivable.brakeToStop) |
97 | SpecializationUtil.registerFunction(vehicleType, "updateSteeringAngle", Drivable.updateSteeringAngle) |
98 | end |