├── 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 |  [](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 |
--------------------------------------------------------------------------------