LUADOC - Farming Simulator 19

Script v1.7.1.0

Engine v1.7.1.0

Foundation Reference

Suspensions

Description
Specialization for non-wheel suspensions e.g. cabin, seat or player character suspension
Functions

getIsSuspensionNodeActive

Description
Definition
getIsSuspensionNodeActive()
Code
248function Suspensions:getIsSuspensionNodeActive(suspensionNode)
249 return suspensionNode.node ~= nil and suspensionNode.component ~= nil
250end

getSuspensionModfier

Description
Definition
getSuspensionModfier()
Code
303function Suspensions:getSuspensionModfier()
304 local spec = self.spec_suspensions
305 local index = 1
306 -- try to get the seat suspension, normally on index 2 (if no cabin suspension on index 1)
307 if #spec.suspensionNodes >= 2 and not spec.suspensionNodes[2].useCharacterTorso then
308 index = 2
309 end
310
311 local suspensionNode = spec.suspensionNodes[index]
312 if suspensionNode ~= nil then
313 if not suspensionNode.isRotational then
314 return suspensionNode.curTranslation[2]
315 end
316 end
317
318 return 0
319end

getSuspensionNodeFromIndex

Description
Definition
getSuspensionNodeFromIndex()
Code
242function Suspensions:getSuspensionNodeFromIndex(suspensionIndex)
243 return self.spec_suspensions.suspensionNodes[suspensionIndex]
244end

onEnterVehicle

Description
Definition
onEnterVehicle()
Code
270function Suspensions:onEnterVehicle(isControlling)
271 if self.getVehicleCharacter ~= nil then
272 local vehicleCharacter = self:getVehicleCharacter()
273
274 local spec = self.spec_suspensions
275 for _, suspensionNode in ipairs(spec.suspensionNodes) do
276 self:setSuspensionNodeCharacter(suspensionNode, vehicleCharacter)
277 end
278 end
279end

onLeaveVehicle

Description
Definition
onLeaveVehicle()
Code
283function Suspensions:onLeaveVehicle()
284 local spec = self.spec_suspensions
285 for _,suspension in ipairs(spec.suspensionNodes) do
286 if suspension.useCharacterTorso then
287 suspension.node = nil
288 end
289 end
290end

onLoad

Description
Definition
onLoad()
Code
39function Suspensions:onLoad(savegame)
40 if self.isClient then
41 local spec = self.spec_suspensions
42
43 spec.suspensionNodes = {}
44
45 local i = 0
46 while true do
47 local key = string.format("vehicle.suspensions.suspension(%d)", i)
48 if not hasXMLProperty(self.xmlFile, key) then
49 break
50 end
51
52 local entry = {}
53 entry.node = I3DUtil.indexToObject( self.components, getXMLString(self.xmlFile, key .. "#node"), self.i3dMappings)
54 entry.refNodeOffset = {0, 0, 0}
55 if entry.node ~= nil then
56 local component = self:getParentComponent(entry.node)
57 if component ~= nil then
58 entry.component = component
59 entry.refNodeOffset = {localToLocal(entry.node, component, 0, 0, 0)}
60 end
61 end
62
63 entry.useCharacterTorso = Utils.getNoNil(getXMLBool(self.xmlFile, key .. "#useCharacterTorso"), false)
64 if (entry.node ~= nil and entry.component ~= nil) or entry.useCharacterTorso then
65 entry.weight = Utils.getNoNil(getXMLFloat(self.xmlFile, key .. "#weight"), 500)
66
67 entry.minRotation = StringUtil.getRadiansFromString(getXMLString(self.xmlFile, key .. "#minRotation"), 3)
68 entry.maxRotation = StringUtil.getRadiansFromString(getXMLString(self.xmlFile, key .. "#maxRotation"), 3)
69 entry.isRotational = entry.minRotation ~= nil and entry.maxRotation ~= nil
70
71 if not entry.isRotational and not entry.useCharacterTorso then
72 entry.baseTranslation = {getTranslation(entry.node)}
73 entry.minTranslation = StringUtil.getVectorNFromString(getXMLString(self.xmlFile, key.."#minTranslation"), 3)
74 entry.maxTranslation = StringUtil.getVectorNFromString(getXMLString(self.xmlFile, key.."#maxTranslation"), 3)
75 end
76
77 entry.maxVelocityDifference = Utils.getNoNil(getXMLFloat(self.xmlFile, key .. "#maxVelocityDifference"), 0.1)
78
79 local suspensionParametersX = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(self.xmlFile, key .. "#suspensionParametersX"), "0 0"), 2)
80 local suspensionParametersY = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(self.xmlFile, key .. "#suspensionParametersY"), "0 0"), 2)
81 local suspensionParametersZ = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(self.xmlFile, key .. "#suspensionParametersZ"), "0 0"), 2)
82 entry.suspensionParameters = {}
83 entry.suspensionParameters[1] = {}
84 entry.suspensionParameters[2] = {}
85 entry.suspensionParameters[3] = {}
86 for j=1,2 do
87 entry.suspensionParameters[1][j] = suspensionParametersX[j] * 1000
88 entry.suspensionParameters[2][j] = suspensionParametersY[j] * 1000
89 entry.suspensionParameters[3][j] = suspensionParametersZ[j] * 1000
90 end
91
92 entry.inverseMovement = Utils.getNoNil(getXMLBool(self.xmlFile, key .. "#inverseMovement"), false)
93
94 entry.lastRefNodePosition = nil
95 entry.lastRefNodeVelocity = nil
96
97 entry.curRotation = {0, 0, 0}
98 entry.curRotationSpeed = {0, 0, 0}
99
100 entry.curTranslation = {0, 0, 0}
101 entry.curTranslationSpeed = {0, 0, 0}
102
103 entry.curAcc = {0, 0, 0}
104 end
105
106 table.insert(spec.suspensionNodes, entry)
107
108 i = i + 1
109 end
110
111 if #spec.suspensionNodes > 0 then
112 spec.suspensionAvailable = true
113 end
114 end
115end

onUpdate

Description
Definition
onUpdate()
Code
119function Suspensions:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
120 if self.isClient then
121 local spec = self.spec_suspensions
122 if spec.suspensionAvailable then
123 local timeDelta = 0.001 * g_physicsDt
124
125 for _,suspension in ipairs(spec.suspensionNodes) do
126 if suspension.node ~= nil and entityExists(suspension.node) then
127 -- calc velocity diff
128 suspension.curAcc[1], suspension.curAcc[2], suspension.curAcc[3] = 0, 0, 0
129
130 if self:getIsSuspensionNodeActive(suspension) then
131 local wx, wy, wz = localToWorld(suspension.component, unpack(suspension.refNodeOffset))
132
133 if suspension.lastRefNodePosition == nil then
134 suspension.lastRefNodePosition = {wx, wy, wz}
135 suspension.lastRefNodeVelocity = {0, 0, 0}
136 end
137
138 local direction = (suspension.inverseMovement and -1) or 1
139
140 local newVelX, newVelY, newVelZ = (wx - suspension.lastRefNodePosition[1]) / timeDelta * direction,
141 (wy - suspension.lastRefNodePosition[2]) / timeDelta * direction,
142 (wz - suspension.lastRefNodePosition[3]) / timeDelta * direction
143
144 local oldVelX, oldVelY, oldVelZ = unpack(suspension.lastRefNodeVelocity)
145
146 local velDiffX, velDiffY, velDiffZ = worldDirectionToLocal(getParent(suspension.node), newVelX-oldVelX, newVelY-oldVelY, newVelZ-oldVelZ)
147
148 velDiffX = MathUtil.clamp(velDiffX, -suspension.maxVelocityDifference, suspension.maxVelocityDifference)
149 velDiffY = MathUtil.clamp(velDiffY, -suspension.maxVelocityDifference, suspension.maxVelocityDifference)
150 velDiffZ = MathUtil.clamp(velDiffZ, -suspension.maxVelocityDifference, suspension.maxVelocityDifference)
151
152 if suspension.isRotational then
153 if suspension.useCharacterTorso then
154 suspension.curAcc[1], suspension.curAcc[2], suspension.curAcc[3] = MathUtil.crossProduct(velDiffX/timeDelta, velDiffY/timeDelta, velDiffZ/timeDelta, 1,0,0)
155 else
156 suspension.curAcc[1], suspension.curAcc[2], suspension.curAcc[3] = MathUtil.crossProduct(velDiffX/timeDelta, velDiffY/timeDelta, velDiffZ/timeDelta, 0,1,0)
157 end
158 else
159 suspension.curAcc[1], suspension.curAcc[2], suspension.curAcc[3] = -velDiffX/timeDelta, -velDiffY/timeDelta, -velDiffZ/timeDelta
160 end
161
162 -- prepare for next tick
163 suspension.lastRefNodePosition[1] = wx
164 suspension.lastRefNodePosition[2] = wy
165 suspension.lastRefNodePosition[3] = wz
166
167 suspension.lastRefNodeVelocity[1] = newVelX
168 suspension.lastRefNodeVelocity[2] = newVelY
169 suspension.lastRefNodeVelocity[3] = newVelZ
170 end
171
172 -- update spring/damper system, F = F_ext - k*x - c*(dx/dt)
173 -- using implicit euler for spring and damper, explicit euler for external force
174 for i=1, 3 do
175 local suspensionParameter = suspension.suspensionParameters[i]
176 if suspensionParameter[1] > 0 and suspensionParameter[2] > 0 then
177 local f = suspension.weight * suspension.curAcc[i]
178
179 local k = suspensionParameter[1]
180 local c = suspensionParameter[2]
181
182 if suspension.isRotational then
183 local x = suspension.curRotation[i]
184 local vx = suspension.curRotationSpeed[i]
185
186 local force = f - (k*x) - (c*vx)
187
188 -- 'Implicit Methods for Differential Equations' (Baraff), formula (4-6)
189 local m = suspension.weight
190 local h = timeDelta
191 local numerator = h * (force + h * (-k) * vx) / m
192 local denumerator = 1 - ((-c) + h * (-k)) * h / m
193 local curRotationSpeed = vx + numerator / denumerator
194
195 local newRotation = x + (curRotationSpeed * timeDelta)
196 newRotation = MathUtil.clamp(newRotation, suspension.minRotation[i], suspension.maxRotation[i])
197
198 suspension.curRotationSpeed[i] = (newRotation - x) / timeDelta
199 suspension.curRotation[i] = newRotation
200 else
201 local x = suspension.curTranslation[i]
202 local vx = suspension.curTranslationSpeed[i]
203
204 local force = f - (k*x) - (c*vx)
205
206 -- 'Implicit Methods for Differential Equations' (Baraff), formula (4-6)
207 local m = suspension.weight
208 local h = timeDelta
209 local numerator = h * (force + h * (-k) * vx) / m
210 local denumerator = 1 - ((-c) + h * (-k)) * h / m
211 local curTranslationSpeed = vx + numerator / denumerator
212
213 local newTranslation = x + (curTranslationSpeed * timeDelta)
214 newTranslation = MathUtil.clamp(newTranslation, suspension.minTranslation[i], suspension.maxTranslation[i])
215
216 suspension.curTranslationSpeed[i] = (newTranslation - x) / timeDelta
217 suspension.curTranslation[i] = newTranslation
218 end
219 end
220 end
221
222 if suspension.isRotational then
223 setRotation(suspension.node, suspension.curRotation[1], suspension.curRotation[2], suspension.curRotation[3])
224 else
225 setTranslation(suspension.node, suspension.baseTranslation[1] + suspension.curTranslation[1], suspension.baseTranslation[2] + suspension.curTranslation[2], suspension.baseTranslation[3] + suspension.curTranslation[3])
226 end
227
228 if self.setMovingToolDirty ~= nil then
229 self:setMovingToolDirty(suspension.node)
230 end
231 elseif suspension.node ~= nil then
232 g_logManager:xmlError(self.configFileName, "Failed to update suspension node %d. Node does not exist anymore!", suspension.node)
233 suspension.node = nil
234 end
235 end
236 end
237 end
238end

onVehicleCharacterChanged

Description
Definition
onVehicleCharacterChanged()
Code
294function Suspensions:onVehicleCharacterChanged(character)
295 local spec = self.spec_suspensions
296 for _, suspensionNode in ipairs(spec.suspensionNodes) do
297 self:setSuspensionNodeCharacter(suspensionNode, character)
298 end
299end

prerequisitesPresent

Description
Definition
prerequisitesPresent()
Code
15function Suspensions.prerequisitesPresent(specializations)
16 return true
17end

registerEventListeners

Description
Definition
registerEventListeners()
Code
29function Suspensions.registerEventListeners(vehicleType)
30 SpecializationUtil.registerEventListener(vehicleType, "onLoad", Suspensions)
31 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Suspensions)
32 SpecializationUtil.registerEventListener(vehicleType, "onEnterVehicle", Suspensions)
33 SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", Suspensions)
34 SpecializationUtil.registerEventListener(vehicleType, "onVehicleCharacterChanged", Suspensions)
35end

registerFunctions

Description
Definition
registerFunctions()
Code
21function Suspensions.registerFunctions(vehicleType)
22 SpecializationUtil.registerFunction(vehicleType, "getSuspensionNodeFromIndex", Suspensions.getSuspensionNodeFromIndex)
23 SpecializationUtil.registerFunction(vehicleType, "getIsSuspensionNodeActive", Suspensions.getIsSuspensionNodeActive)
24 SpecializationUtil.registerFunction(vehicleType, "setSuspensionNodeCharacter", Suspensions.setSuspensionNodeCharacter)
25end

setSuspensionNodeCharacter

Description
Definition
setSuspensionNodeCharacter()
Code
254function Suspensions:setSuspensionNodeCharacter(suspensionNode, character)
255 if suspensionNode.useCharacterTorso then
256 suspensionNode.node = character.thirdPersonSuspensionNode
257
258 if suspensionNode.node ~= nil then
259 local component = self:getParentComponent(suspensionNode.node)
260 if component ~= nil then
261 suspensionNode.refNodeOffset = {localToLocal(character.characterNode, component, 0, 0, 0)}
262 suspensionNode.component = component
263 end
264 end
265 end
266end