├── hub.zip ├── FS19_fendt500Favorit.zip ├── README.md └── TirePressure.lua /hub.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stijnwop/tirePressure/HEAD/hub.zip -------------------------------------------------------------------------------- /FS19_fendt500Favorit.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stijnwop/tirePressure/HEAD/FS19_fendt500Favorit.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tire pressure system for Farming Simulator 19 2 | 3 | ![For Farming Simulator 19](https://img.shields.io/badge/Farming%20Simulator-19-FF7C00.svg) [![Releases](https://img.shields.io/github/release/stijnwop/guidanceSteering.svg)](https://github.com/stijnwop/guidanceSteering/releases) 4 | 5 | Make sure to add the following (besides the specialization entry) to your modDesc: 6 | 7 | The registering of actions. 8 | ```xml 9 | 10 | 11 | 12 | 13 | ``` 14 | 15 | The binding of inputs (which key maps to what action). 16 | ```xml 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ``` 27 | 28 | The localisation entry. 29 | ```xml 30 | 31 | 32 | Tire pressure [target: %1.2f bar] [current: %1.2f bar] 33 | Reifenluftdruck [Soll: %1.2f bar] [Ist: %1.2f bar] 34 | Presión Neumático [Fijar en: %1.2f bar] [Actual: %1.2f bar] 35 | Pressione pneumatico [obiett.: %1.2f bar] [attuale: %1.2f bar] 36 | 37 | 38 | Toggle pressure 39 | Druck ändern 40 | Cambiar presión 41 | Modifica pressione 42 | 43 | 44 | Update pressure 45 | Druck aktualisieren 46 | Actualizar presión 47 | Aggiorna pressione 48 | 49 | 50 | Inflate 51 | Aufpumpen 52 | Inflar 53 | Gonfia 54 | 55 | 56 | Deflate 57 | Ablassen 58 | Desinflar 59 | Sgonfia 60 | 61 | 62 | ``` 63 | 64 | In the vehicle xml you can configure the following (which are all OPTIONAL): 65 | - `min`: the minimum kPa 66 | - `max`: the maximum kPa 67 | - `configurationName`: if you want to activate tire pressure for specific configurations you can use this entry to specify the configuration. (e.g. `wheels`) 68 | - `configurationIndices`: the indexes of the given `configurationName` that should activate the tire pressure. 69 | 70 | >PLEASE NOTE: `configurationIndices` can only be used when `configurationName` is set. 71 | 72 | Below an example entry for your vehicle XML. 73 | Don't blindly copy paste as you will have to change the attributes yourself to the correct values or remove them when not needed. 74 | ```xml 75 | If you only want to active it for a specific configuration/configurations you will have to set the configurationName and configurationIndices attributes. 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | ``` 88 | 89 | ### Please read this when your vehicle has vehicleTypeConfigurations! 90 | A common mistake is that people forget to change the vehicleTypes in the vehicleTypeConfigurations. When those configurations are present the mod will default to the first configuration given, resulting in any custom vehicle type you defined to be overwritten. 91 | 92 | So, make sure you change them to your new vehicleType! 93 | -------------------------------------------------------------------------------- /TirePressure.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------------------------------- 2 | -- TirePressure 3 | ---------------------------------------------------------------------------------------------------- 4 | -- Purpose: allows setting tire pressure on the wheels. 5 | -- 6 | -- Copyright (c) Wopster, 2019 7 | ---------------------------------------------------------------------------------------------------- 8 | 9 | --[[ 10 | Add the following (besides the specialization entry) to the your modDesc: 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Tire pressure [target: %1.2f bar] [current: %1.2f bar] 30 | Reifenluftdruck [Soll: %1.2f bar] [Ist: %1.2f bar] 31 | 32 | 33 | Toggle pressure 34 | Druck ändern 35 | 36 | 37 | Update pressure 38 | Druck aktualisieren 39 | 40 | 41 | Inflate 42 | Aufpumpen 43 | 44 | 45 | Deflate 46 | Ablassen 47 | 48 | 49 | 50 | Example entry for the vehicle XML: 51 | If you only want to active it for a specific configuration/configurations you will have to set the configurationName and configurationIndices attributes. 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | ]] 64 | 65 | ---@class TirePressure 66 | TirePressure = {} 67 | TirePressure.MOD_NAME = g_currentModName 68 | 69 | TirePressure.PRESSURE_MIN = 80 -- kPa 70 | TirePressure.PRESSURE_LOW = 80 -- kPa 71 | TirePressure.PRESSURE_NORMAL = 180 -- kPa 72 | TirePressure.PRESSURE_MAX = 180 -- kPa 73 | 74 | TirePressure.INCREASE = 1.15 75 | TirePressure.FLATE_MULTIPLIER = 0.005 76 | 77 | TirePressure.MAX_INPUT_MULTIPLIER = 10 78 | TirePressure.INPUT_MULTIPLIER_STEP = 0.01 79 | 80 | function TirePressure.prerequisitesPresent(specializations) 81 | return SpecializationUtil.hasSpecialization(Wheels, specializations) 82 | end 83 | 84 | function TirePressure.registerFunctions(vehicleType) 85 | SpecializationUtil.registerFunction(vehicleType, "updateInflation", TirePressure.updateInflation) 86 | SpecializationUtil.registerFunction(vehicleType, "updateInflationPressure", TirePressure.updateInflationPressure) 87 | SpecializationUtil.registerFunction(vehicleType, "getInflationPressure", TirePressure.getInflationPressure) 88 | SpecializationUtil.registerFunction(vehicleType, "setInflationPressure", TirePressure.setInflationPressure) 89 | SpecializationUtil.registerFunction(vehicleType, "getInflationPressureTarget", TirePressure.getInflationPressureTarget) 90 | SpecializationUtil.registerFunction(vehicleType, "setInflationPressureTarget", TirePressure.setInflationPressureTarget) 91 | end 92 | 93 | function TirePressure.registerEventListeners(vehicleType) 94 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", TirePressure) 95 | SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", TirePressure) 96 | SpecializationUtil.registerEventListener(vehicleType, "onDelete", TirePressure) 97 | SpecializationUtil.registerEventListener(vehicleType, "onReadStream", TirePressure) 98 | SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", TirePressure) 99 | SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", TirePressure) 100 | SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", TirePressure) 101 | SpecializationUtil.registerEventListener(vehicleType, "onUpdate", TirePressure) 102 | SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", TirePressure) 103 | end 104 | 105 | function TirePressure.registerOverwrittenFunctions(vehicleType) 106 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanBeSelected", TirePressure.getCanBeSelected) 107 | end 108 | 109 | function TirePressure:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection) 110 | if self.isClient then 111 | local spec = self.spec_tirePressure 112 | self:clearActionEventsTable(spec.actionEvents) 113 | 114 | if spec.isActive then 115 | if isActiveForInput then 116 | local _, actionEventIdInflate = self:addActionEvent(spec.actionEvents, InputAction.TP_AXIS_PRESSURE, self, TirePressure.actionEventInflatePressure, false, true, true, true, nil, nil, true) 117 | local _, actionEventIdTogglePressure = self:addActionEvent(spec.actionEvents, InputAction.TP_TOGGLE_PRESSURE, self, TirePressure.actionEventTogglePressure, false, true, false, true, nil, nil, true) 118 | 119 | g_inputBinding:setActionEventText(actionEventIdTogglePressure, g_i18n:getText("action_toggleTirePressure")) 120 | g_inputBinding:setActionEventTextVisibility(actionEventIdTogglePressure, true) 121 | g_inputBinding:setActionEventTextPriority(actionEventIdTogglePressure, GS_PRIO_NORMAL) 122 | g_inputBinding:setActionEventActive(actionEventIdInflate, true) 123 | g_inputBinding:setActionEventActive(actionEventIdTogglePressure, true) 124 | end 125 | end 126 | end 127 | end 128 | 129 | ---Checks whether or not the configuration allows the tire pressure to be active. 130 | function TirePressure.isEnabledByConfiguration(configuration, configurationIndices) 131 | if configuration ~= nil then 132 | for _, configurationIndex in pairs(configurationIndices) do 133 | if configuration == configurationIndex then 134 | return true 135 | end 136 | end 137 | end 138 | 139 | return false 140 | end 141 | 142 | function TirePressure:onLoad(savegame) 143 | self.spec_tirePressure = self[("spec_%s.tirePressure"):format(TirePressure.MOD_NAME)] 144 | local spec = self.spec_tirePressure 145 | spec.isActive = true -- default enabled. 146 | 147 | local configurationName = getXMLString(self.xmlFile, "vehicle.tirePressure#configurationName") 148 | if configurationName ~= nil then 149 | local configurationIndices = Utils.getNoNil(StringUtil.getVectorNFromString(getXMLString(self.xmlFile, "vehicle.tirePressure#configurationIndices")), { 1 }) 150 | if configurationIndices ~= nil then 151 | spec.isActive = TirePressure.isEnabledByConfiguration(self.configurations[configurationName], configurationIndices) 152 | end 153 | end 154 | 155 | spec.inflationPressure = TirePressure.PRESSURE_NORMAL 156 | spec.inflationPressureTarget = TirePressure.PRESSURE_NORMAL 157 | spec.isInflating = false 158 | spec.allWheelsAreCrawlers = true 159 | spec.pressureMax = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.tirePressure#max"), TirePressure.PRESSURE_MAX) 160 | spec.pressureMin = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.tirePressure#min"), TirePressure.PRESSURE_MIN) 161 | 162 | spec.lastInputChangePressureValue = 0 163 | spec.lastPressureValue = 0 164 | spec.changeCurrentDelay = 0 165 | spec.changeMultiplier = 1 166 | spec.changePushUpdate = false 167 | 168 | local tireTypeCrawler = WheelsUtil.getTireType("crawler") 169 | for _, wheel in ipairs(self:getWheels()) do 170 | if wheel.tireType ~= tireTypeCrawler then 171 | spec.allWheelsAreCrawlers = false 172 | end 173 | end 174 | 175 | spec.inflationDirtyFlag = self:getNextDirtyFlag() 176 | 177 | if self.isClient then 178 | spec.samples = {} 179 | spec.samples.air = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.tirePressure.sounds", "air", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) 180 | end 181 | end 182 | 183 | function TirePressure:onPostLoad(savegame) 184 | local spec = self.spec_tirePressure 185 | if spec.isActive and savegame ~= nil and not savegame.resetVehicles then 186 | local key = savegame.key .. "." .. TirePressure.MOD_NAME .. ".tirePressure" 187 | local tirePressure = Utils.getNoNil(getXMLFloat(savegame.xmlFile, key .. "#inflationPressure"), spec.inflationPressure) 188 | 189 | if tirePressure ~= nil then 190 | self:setInflationPressure(tirePressure) 191 | end 192 | end 193 | end 194 | 195 | function TirePressure:onDelete() 196 | local spec = self.spec_tirePressure 197 | if self.isClient then 198 | g_soundManager:deleteSamples(spec.samples) 199 | end 200 | end 201 | 202 | function TirePressure:saveToXMLFile(xmlFile, key, usedModNames) 203 | local spec = self.spec_tirePressure 204 | setXMLFloat(xmlFile, key .. "#inflationPressure", spec.inflationPressure) 205 | end 206 | 207 | function TirePressure:onReadStream(streamId, connection) 208 | local spec = self.spec_tirePressure 209 | spec.isInflating = streamReadBool(streamId) 210 | local inflationPressure = streamReadFloat32(streamId) 211 | self:setInflationPressure(inflationPressure) 212 | end 213 | 214 | function TirePressure:onWriteStream(streamId, connection) 215 | local spec = self.spec_tirePressure 216 | streamWriteBool(streamId, spec.isInflating) 217 | streamWriteFloat32(streamId, spec.inflationPressure) 218 | end 219 | 220 | function TirePressure:onReadUpdateStream(streamId, timestamp, connection) 221 | local spec = self.spec_tirePressure 222 | if connection:getIsServer() then 223 | if streamReadBool(streamId) then 224 | local inflationPressure = streamReadFloat32(streamId) 225 | self:setInflationPressure(inflationPressure) 226 | end 227 | end 228 | end 229 | 230 | function TirePressure:onWriteUpdateStream(streamId, connection, dirtyMask) 231 | local spec = self.spec_tirePressure 232 | 233 | if not connection:getIsServer() then 234 | if streamWriteBool(streamId, bitAND(dirtyMask, spec.inflationDirtyFlag) ~= 0) then 235 | streamWriteFloat32(streamId, spec.inflationPressure) 236 | end 237 | end 238 | end 239 | 240 | function TirePressure:onUpdate(dt) 241 | local spec = self.spec_tirePressure 242 | 243 | if self.isClient then 244 | local actionEventPressure = spec.actionEvents[InputAction.TP_AXIS_PRESSURE] 245 | if actionEventPressure ~= nil then 246 | g_inputBinding:setActionEventActive(actionEventPressure.actionEventId, spec.isActive) 247 | end 248 | local actionEventToggle = spec.actionEvents[InputAction.TP_TOGGLE_PRESSURE] 249 | if actionEventToggle ~= nil then 250 | g_inputBinding:setActionEventActive(actionEventToggle.actionEventId, spec.isActive) 251 | end 252 | end 253 | 254 | if spec.allWheelsAreCrawlers or not spec.isActive then 255 | return 256 | end 257 | 258 | local pressure = self:getInflationPressure() 259 | 260 | if self.isClient then 261 | local lastInputChangePressureValue = spec.lastInputChangePressureValue 262 | spec.lastInputChangePressureValue = 0 263 | 264 | if lastInputChangePressureValue ~= 0 then 265 | spec.changeCurrentDelay = spec.changeCurrentDelay - (dt * spec.changeMultiplier) 266 | spec.changeMultiplier = math.min(spec.changeMultiplier + (dt * TirePressure.INPUT_MULTIPLIER_STEP), TirePressure.MAX_INPUT_MULTIPLIER) 267 | 268 | if spec.changeCurrentDelay < 0 then 269 | spec.changeCurrentDelay = 250 270 | local dir = MathUtil.sign(lastInputChangePressureValue) 271 | local pressureChange = dt * spec.changeMultiplier * TirePressure.FLATE_MULTIPLIER 272 | pressureChange = pressureChange * dir 273 | spec.inflationPressureTarget = MathUtil.clamp(spec.inflationPressureTarget + pressureChange, spec.pressureMin, spec.pressureMax) 274 | spec.changePushUpdate = true 275 | end 276 | else 277 | spec.changeCurrentDelay = 0 278 | spec.changeMultiplier = 1 279 | 280 | if spec.changePushUpdate then 281 | spec.changePushUpdate = false 282 | g_client:getServerConnection():sendEvent(SetInflationPressureEvent:new(self, spec.isInflating, spec.inflationPressureTarget)) 283 | end 284 | end 285 | end 286 | 287 | if self.isServer then 288 | if spec.isInflating then 289 | local diff = spec.inflationPressureTarget - pressure 290 | if math.abs(diff) > 0.05 then 291 | local dir = MathUtil.sign(diff) 292 | local pressureChange = dt * TirePressure.FLATE_MULTIPLIER 293 | self:setInflationPressure(pressure + (pressureChange * dir)) 294 | else 295 | self:updateInflation(false) 296 | end 297 | end 298 | 299 | if pressure == self:getInflationPressure() and spec.isInflating then 300 | self:updateInflation(false) 301 | end 302 | end 303 | 304 | if self.isClient then 305 | local isCapped = pressure == spec.pressureMin or pressure == spec.inflationPressureTarget 306 | 307 | if spec.isInflating and not isCapped then 308 | if not g_soundManager:getIsSamplePlaying(spec.samples.air) then 309 | g_soundManager:playSample(spec.samples.air) 310 | end 311 | else 312 | if g_soundManager:getIsSamplePlaying(spec.samples.air) then 313 | g_soundManager:stopSample(spec.samples.air) 314 | end 315 | end 316 | 317 | --Due to a vanilla bug (with calling onDraw on attached implements to implements) we have to render it in the update frame instead of onDraw. 318 | if not spec.allWheelsAreCrawlers and spec.isActive and self:getIsActiveForInput() then 319 | g_currentMission:addExtraPrintText(g_i18n:getText("information_tirePressure"):format(self:getInflationPressureTarget() / 100, self:getInflationPressure() / 100)) 320 | end 321 | end 322 | end 323 | 324 | function TirePressure:updateInflationPressure() 325 | local spec = self.spec_tirePressure 326 | local tireTypeCrawler = WheelsUtil.getTireType("crawler") 327 | 328 | for _, wheel in pairs(self:getWheels()) do 329 | if wheel.tireType ~= tireTypeCrawler then 330 | if wheel.tpMaxDeformation == nil then 331 | wheel.tpMaxDeformation = Utils.getNoNil(wheel.maxDeformation, 0) 332 | wheel.tpSuspTravel = wheel.suspTravel 333 | wheel.tpFrictionScale = wheel.frictionScale 334 | end 335 | 336 | local deformation = MathUtil.clamp((wheel.deltaY + 0.04 - (wheel.tpSuspTravel * 0.5)) * (TirePressure.INCREASE - (spec.inflationPressure - TirePressure.PRESSURE_LOW) / 100), 0, wheel.maxDeformation) 337 | wheel.suspTravel = wheel.tpSuspTravel - deformation 338 | local delta = TirePressure.PRESSURE_NORMAL / spec.inflationPressure 339 | wheel.maxDeformation = wheel.tpMaxDeformation * delta 340 | wheel.frictionScale = wheel.tpFrictionScale * delta - 0.5 341 | 342 | self:setWheelPositionDirty(wheel) 343 | self:setWheelTireFrictionDirty(wheel) 344 | end 345 | end 346 | end 347 | 348 | function TirePressure:updateInflation(isInflating, noEventSend) 349 | local spec = self.spec_tirePressure 350 | if isInflating ~= spec.isInflating then 351 | SetInflationPressureEvent.sendEvent(self, isInflating, noEventSend) 352 | spec.isInflating = isInflating 353 | end 354 | end 355 | 356 | function TirePressure:getInflationPressure() 357 | return self.spec_tirePressure.inflationPressure 358 | end 359 | 360 | function TirePressure:setInflationPressure(pressure) 361 | local spec = self.spec_tirePressure 362 | local inflationPressure = MathUtil.clamp(pressure, spec.pressureMin, spec.pressureMax) 363 | 364 | if spec.inflationPressure ~= inflationPressure then 365 | spec.inflationPressure = inflationPressure 366 | self:updateInflationPressure() 367 | 368 | if self.isServer then 369 | self:raiseDirtyFlags(spec.inflationDirtyFlag) 370 | spec.inflationPressureSent = inflationPressure 371 | end 372 | end 373 | end 374 | 375 | function TirePressure:setInflationPressureTarget(target) 376 | self.spec_tirePressure.inflationPressureTarget = target 377 | end 378 | 379 | function TirePressure:getInflationPressureTarget() 380 | return self.spec_tirePressure.inflationPressureTarget 381 | end 382 | 383 | function TirePressure:getValveLoadPercentage() 384 | if self.spec_tirePressure ~= nil then 385 | local spec = self.spec_tirePressure 386 | return MathUtil.sign(spec.inflationPressureTarget - spec.inflationPressure) > 0 and 1 or 0 387 | end 388 | 389 | return 0 390 | end 391 | 392 | g_soundManager:registerModifierType("VALVE_LOAD", TirePressure.getValveLoadPercentage) 393 | 394 | function TirePressure:getCanBeSelected(superFunc) 395 | return true 396 | end 397 | 398 | function TirePressure.actionEventInflatePressure(self, actionName, inputValue, callbackState, isAnalog) 399 | local spec = self.spec_tirePressure 400 | 401 | if not spec.allWheelsAreCrawlers and spec.isActive then 402 | spec.lastInputChangePressureValue = inputValue 403 | spec.lastPressureValue = inputValue 404 | end 405 | end 406 | 407 | function TirePressure.actionEventTogglePressure(self, actionName, inputValue, callbackState, isAnalog) 408 | local spec = self.spec_tirePressure 409 | 410 | if not spec.allWheelsAreCrawlers and spec.isActive then 411 | self:updateInflation(not spec.isInflating) 412 | end 413 | end 414 | 415 | SetInflationPressureEvent = {} 416 | SetInflationPressureEvent_mt = Class(SetInflationPressureEvent, Event) 417 | 418 | InitEventClass(SetInflationPressureEvent, "SetInflationPressureEvent") 419 | 420 | function SetInflationPressureEvent:emptyNew() 421 | local event = Event:new(SetInflationPressureEvent_mt) 422 | return event 423 | end 424 | 425 | function SetInflationPressureEvent:new(object, isInflating, inflationPressureTarget) 426 | local event = SetInflationPressureEvent:emptyNew() 427 | 428 | event.object = object 429 | event.isInflating = isInflating 430 | event.inflationPressureTarget = inflationPressureTarget 431 | 432 | return event 433 | end 434 | 435 | function SetInflationPressureEvent:readStream(streamId, connection) 436 | self.object = NetworkUtil.readNodeObject(streamId) 437 | self.isInflating = streamReadBool(streamId) 438 | 439 | if streamReadBool(streamId) then 440 | self.inflationPressureTarget = streamReadFloat32(streamId) 441 | end 442 | 443 | self:run(connection) 444 | end 445 | 446 | function SetInflationPressureEvent:writeStream(streamId, connection) 447 | NetworkUtil.writeNodeObject(streamId, self.object) 448 | streamWriteBool(streamId, self.isInflating) 449 | local hasTarget = self.inflationPressureTarget ~= nil 450 | streamWriteBool(streamId, hasTarget) 451 | if hasTarget then 452 | streamWriteFloat32(streamId, self.inflationPressureTarget) 453 | end 454 | end 455 | 456 | function SetInflationPressureEvent:run(connection) 457 | self.object:updateInflation(self.isInflating, true) 458 | 459 | if self.inflationPressureTarget ~= nil then 460 | self.object:setInflationPressureTarget(self.inflationPressureTarget) 461 | end 462 | 463 | if not connection:getIsServer() then 464 | g_server:broadcastEvent(self, false, connection, self.object) 465 | end 466 | end 467 | 468 | function SetInflationPressureEvent.sendEvent(object, isInflating, noEventSend) 469 | if noEventSend == nil or noEventSend == false then 470 | if g_server ~= nil then 471 | g_server:broadcastEvent(SetInflationPressureEvent:new(object, isInflating), nil, nil, object) 472 | else 473 | g_client:getServerConnection():sendEvent(SetInflationPressureEvent:new(object, isInflating)) 474 | end 475 | end 476 | end 477 | --------------------------------------------------------------------------------