meta data for this page
Vehicles/Vehicle.lua
The Vehicle class is at the heart of all vehicle-related features, together with VehicleManager. It handles all aspects that affect all vehicles (such as buying, selling or initializing vehicle scripts).
27 28 Vehicle = Vehicle or {}; 29 local VehicleClass = Class(Vehicle); 30
Vehicle:addVehicleScript(vehicleScript)
Adds the Vehicle script class vehicleScript
(a table with functions, usually) to this vehicle's scripts.
34 function Vehicle:addVehicleScript(vehicleScript) 35 table.insert(self.vehicleScripts, vehicleScript); 36 end;
Vehicle:callVehicleScripts(functionName, ...)
Calls the function named functionName
(string) for all vehicle scripts that are registered to this vehicle.
39 function Vehicle:callVehicleScripts(functionName, ...) 40 -- calls all vehicle script functions that are named the same, using the proper arguments 41 for _, script in pairs(self.vehicleScripts) do 42 -- ensure its not nil 43 if script[functionName] ~= nil then 44 script[functionName](self, ...); 45 end; 46 end; 47 end;
Vehicle:callSafe(functionName, ...)
Safely calls the function named functionName
(string) of this vehicle. If this function does not exist, no error will be thrown.
It makes sense to call this function e.g. if it is implemented by another vehicle script that could also be left out.
53 function Vehicle:callSafe(functionName, ...) 54 local func = self[functionName]; 55 56 if func ~= nil then 57 func(self, ...); 58 end; 59 end;
Vehicle:load(bundleId, modName, templateName, dataTable)
Loads the newly created Vehicle instance using some parameters:
bundleId
(int) id of the mod's asset bundle, 0 for vanilla contentmodName
(string) name of the mod,“”
for vanilla contenttemplateName
(string) name of the content, this will either bedefault.xyz
ormods.yourmodname.xyz
.dataTable
(table) the data table that was passed over toModLoader.addContent(name, dataTable)
.
67 function Vehicle:load(bundleId, modName, templateName, dataTable) 68 69 -- first load our vehicle prefab 70 local prefabName = dataTable.prefab or ""; 71 local id = Utils.loadBundleGameObject(bundleId, prefabName); 72 setPosition(id, 0,0,0); 73 setRotation(id, 0,0,0); 74 75 -- first store the main id 76 self.id = id; 77 self.bundleId = bundleId; 78 79 -- store these two for the savegame 80 self.modName = modName; 81 self.templateName = templateName; 82 self.balanceSheetId = 0; 83 84 self.vehicleScripts = {}; 85 86 self:loadComponents(dataTable); 87 self:loadVehicleScripts(dataTable); 88 89 VehicleManager:registerVehicle(self); 90 91 -- some parameter reading 92 self.maxWheelTorque = dataTable.maxWheelTorque or 1200; 93 94 self.maxBrakeTorque = dataTable.maxBrakeTorque or 1200; 95 self.parkingBrakeTorque = dataTable.parkingBrakeTorque or 550; 96 97 self.maxSteerAngle = dataTable.steeringAngle or 30; 98 self.steeringSteerSpeed = dataTable.steeringSteerSpeed or 2; 99 self.steeringReturnSpeed = dataTable.steeringReturnSpeed or 4; 100 self.steeringSlowdownCoeff = dataTable.steeringSlowdownCoeff or 0.01; 101 self.steeringMaxSlowdown = dataTable.steeringMaxSlowdown or 0.5; 102 103 -- runtime variables 104 self.parkingBrake = 1; -- 1 means applied, 0 released 105 self.brakeValue = 0; 106 self.throttleValue = 0; -- handled by this script, but applied by VehicleMotor 107 self.steerValue = 0; 108 109 self.isEntered = false; 110 111 local steeringWheelIndex = dataTable.steeringWheelIndex or ""; 112 if steeringWheelIndex ~= "" then 113 self.steeringWheelId = getChild(self.id, steeringWheelIndex); 114 self.steeringWheelAngle = dataTable.steeringWheelAngle or 450; 115 self.steeringWheelAxle = dataTable.steeringWheelAxis or 3; 116 end; 117 118 local steeringAnimationIndex = dataTable.steeringAnimationIndex; 119 if steeringAnimationIndex ~= nil then 120 self.steeringAnimationId = getChild(self.id, steeringAnimationIndex); 121 self.steeringAnimationLength = Animation.getLength(self.steeringAnimationId); 122 Animation.stop(self.steeringAnimationId); 123 Animation.sampleTime(self.steeringAnimationId, 0.5 * self.steeringAnimationLength); 124 end; 125 126 self:loadSounds(dataTable.sounds); 127 128 129 self:loadAxles(dataTable); 130 131 -- load exhausts 132 self.exhaustParticleSystems = {}; 133 if dataTable.exhaustParticleSystems ~= nil then 134 for k, v in pairs(dataTable.exhaustParticleSystems) do 135 local id = getChild(self.id, v.index); 136 137 if id ~= 0 then 138 local particleSystemId = Utils.loadBundleGameObject(self.bundleId, v.particleSystem or "$vehicles/particles/ExhaustParticles"); 139 140 setParent(particleSystemId, id); 141 setPosition(particleSystemId, 0,0,0); 142 setRotation(particleSystemId, 0,0,0); 143 setActive(particleSystemId, false); 144 145 table.insert(self.exhaustParticleSystems, { id = particleSystemId }); 146 end; 147 end; 148 end; 149 150 self:callVehicleScripts("load", dataTable); 151 152 -- apply brake torque 153 if not self.customWheelScriptActive then 154 for n, axle in pairs(self.axles) do 155 Wheel.setTorques(axle.leftWheel, 0, self.maxBrakeTorque); 156 Wheel.setTorques(axle.rightWheel, 0, self.maxBrakeTorque); 157 end; 158 end; 159 end;
Vehicle:loadVehicleScripts(dataTable)
Loads all vehicle scripts from dataTable
and registers them to this vehicle.
163 function Vehicle:loadVehicleScripts(dataTable) 164 165 for i, class in pairs(dataTable.vehicleScripts) do 166 167 if type(class) == "table" then 168 self:addVehicleScript(class); 169 else 170 print("Error while loading vehicle " .. tostring(self.templateName) .. "'s VehicleScripts: failed to find script number " .. tostring(i) .. ". Check if it's existent within the scope of your data table or if there's a spelling mistake somewhere (case sensitive)."); 171 end; 172 end; 173 174 end;
Vehicle:loadSounds(dataTable)
Loads some basic sounds (parking brake, switch sound).
177 function Vehicle:loadSounds(dataTable) 178 if dataTable == nil then return end; 179 180 self.parkingBrakeSound0 = self:loadSingleSound(dataTable.releaseParkingBrake); 181 self.parkingBrakeSound1 = self:loadSingleSound(dataTable.setParkingBrake); 182 183 self.switchSound0 = self:loadSingleSound(dataTable.switchTurnOff); 184 self.switchSound1 = self:loadSingleSound(dataTable.switchTurnOn); 185 end;
Vehicle:loadSingleSound(data, soundOnly)
Loads a sound from the configuration table given in data
(table). If soundOnly
is equal to true, the volume specified in data
will not be applied.
This function is used several times by other classes, such as FuelTank, VehicleLighting, VehicleMotor and WarningSound. In general, it is recommended to use this function for all one-shot sounds.
190 function Vehicle:loadSingleSound(data, soundOnly) 191 if data == nil then 192 return nil; 193 end; 194 195 local index = nil; 196 197 if type(data) == "string" then 198 index = data; 199 elseif type(data) == "table" then 200 index = data.prefab; 201 end; 202 203 if index == nil or index == "" then 204 return nil; 205 end; 206 207 local id = Utils.loadBundleGameObject(self.bundleId, index); 208 209 if id == 0 then 210 return nil; 211 end; 212 213 -- make it a child to us 214 setParent(id, self.id); 215 setPosition(id, 0,0,0); 216 217 if not soundOnly and type(data) == "table" then 218 if data.volume ~= nil then 219 AudioSource.setVolume(id, data.volume); 220 end; 221 end; 222 223 -- and return it back to the caller 224 return id; 225 end;
Vehicle:playSound(sound)
Plays the specified sound with id sound
(int), if existing.
228 function Vehicle:playSound(sound) 229 if sound ~= nil then 230 AudioSource.play(sound); 231 end; 232 end;
Vehicle:stopSound(sound)
Stops playing the specified sound with id sound
(int), if existing.
235 function Vehicle:stopSound(sound) 236 if sound ~= nil then 237 AudioSource.stop(sound); 238 end; 239 end; 240
Vehicle:playOnOffSound(condition, onSound, offSound)
Starts or stops playing the specified sound with id sound
(int), depending on condition
(bool), unless that sound does not exist.
243 function Vehicle:playOnOffSound(condition, onSound, offSound) 244 if condition then 245 if onSound ~= nil then 246 AudioSource.play(onSound); 247 end; 248 else 249 if offSound ~= nil then 250 AudioSource.play(offSound); 251 end; 252 end; 253 end;
Vehicle:setBalanceSheetId(balanceSheetId)
Assigns the balance sheet id balanceSheetId
(id) to this vehicle. This is called after the vehicle has been bought.
Please do not modify balance sheet ids. This can distort the ecosystem and especially financial figures due to the double book-keeping system in WRS.
258 function Vehicle:setBalanceSheetId(balanceSheetId) 259 self.balanceSheetId = balanceSheetId; 260 end;
Vehicle:saveToTable()
Joins all relevant variables that shall be saved from any vehicle script into a single table and returns it.
263 function Vehicle:saveToTable() 264 -- write our position etc in here 265 local px, py, pz = getPosition(self.id); 266 local rx, ry, rz = getRotation(self.id); 267 local tbl = { 268 modName = self.modName, 269 templateName = self.templateName, 270 balanceSheetId = self.balanceSheetId, 271 px = px, 272 py = py, 273 pz = pz, 274 rx = rx, 275 ry = ry, 276 rz = rz 277 }; 278 self:callVehicleScripts("saveToTable", tbl); 279 return tbl; 280 end;
Vehicle:loadFromTable(tbl)
Restores the vehicle's variables and calls all vehicle scripts to load them as well.
Please note that position and rotation are already handled by VehicleManager when the vehicle is spawned.
285 function Vehicle:loadFromTable(tbl) 286 if tbl == nil then return end; 287 288 self:setBalanceSheetId(tbl.balanceSheetId or self.balanceSheetId); 289 self:callVehicleScripts("loadFromTable", tbl); 290 end;
Vehicle:getAge()
Returns the age of the vehicle in days (if possible, otherwise nil
).
293 function Vehicle:getAge() 294 local asset = g_scenario.accounting:getBalanceSheetEntry(self.balanceSheetId); 295 296 if asset == nil then 297 return nil; 298 end; 299 return asset.age; 300 end;
Vehicle:getMaintenanceCost()
Returns the daily maintenance cost.
After its acquisition, the vehicle will cost template.minMaintenance
per day, while it will cost template.maxMaintenance
per day once the depreciationPeriod
is exceeded.
304 function Vehicle:getMaintenanceCost() 305 local asset = g_scenario.accounting:getBalanceSheetEntry(self.balanceSheetId); 306 local template = VehicleManager.vehicleTemplates[self.templateName]; 307 308 -- if there's no balance sheet item, then it is no longer in useful life 309 local remainingUsefulLife = 0; 310 311 if asset ~= nil and template ~= nil and asset.age ~= nil then 312 remainingUsefulLife = map(0, template.depreciationPeriod, 1, 0, asset.age); 313 end; 314 315 return map(1, 0, template.minMaintenance, template.maxMaintenance, clamp01(remainingUsefulLife)); 316 end;
Vehicle:getSellValue()
Returns the vehicle's current sell value in the game's currency.
319 function Vehicle:getSellValue() 320 local asset = g_scenario.accounting:getBalanceSheetEntry(self.balanceSheetId); 321 local template = VehicleManager.vehicleTemplates[self.templateName]; 322 323 -- if there's no balance sheet item, then it is no longer in useful life 324 local remainingUsefulLife = 0; 325 326 if asset ~= nil and template ~= nil and asset.age ~= nil then 327 remainingUsefulLife = map(0, template.depreciationPeriod, 1, 0, asset.age); 328 end; 329 330 return map(1, 0, 0.8*template.price, 0.2*template.price, clamp01(remainingUsefulLife)); 331 end;
Vehicle:sell()
Sells the vehicle, pays the money over to the player and destroys the vehicle.
334 function Vehicle:sell() 335 -- determine the residual value 336 local sellValue = self:getSellValue(); 337 338 -- leave if entered 339 if self.isEntered then 340 self:leaveByTab(); 341 end; 342 343 -- get some money 344 g_scenario.accounting:sellAsset(self.balanceSheetId, sellValue); 345 346 self:callVehicleScripts("onSell"); 347 348 -- next step: destroy 349 self:destroy(); 350 end;
Vehicle:spawnAt(x,y,z, rx,ry,rz)
Teleports the vehicle to the position x,y,z
(all float) with rotation rx,ry,rz
(all float).
353 function Vehicle:spawnAt(x,y,z, rx,ry,rz) 354 -- just apply this position to our main component 355 -- any other component has to be adjusted automatically by joints 356 -- set the main component to kinematic 357 if Rigidbody.isRigidbody(self.mainId) then 358 Rigidbody.setIsKinematic(self.mainId, true); 359 end; 360 setWorldPosition(self.mainId, x,y,z); 361 setWorldRotation(self.mainId, rx,ry,rz); 362 363 self.resetKinematic = 0.5; -- block vehicle for 30 physics simulations 364 end;
Vehicle:reset()
Resets the vehicle to a reset position returned by VehicleManager:findPosition
. View VehicleManager for more details.
367 function Vehicle:reset() 368 -- ask for a place to reset 369 local template = VehicleManager.vehicleTemplates[self.templateName]; 370 local x,y,z,ry = VehicleManager:findPosition(template.width, template.length); 371 372 if x ~= nil then 373 -- call scripts 374 self:callVehicleScripts("onReset"); 375 376 self:spawnAt(x,y,z, 0,ry,0); 377 return true; 378 end; 379 380 return false; 381 end;
Vehicle:loadComponents(dataTable)
Experimental feature only Loads all components if the vehicle consists of multiple components. Otherwise, it sets up the main component. This can be useful for some special vehicles.
385 function Vehicle:loadComponents(dataTable) 386 if dataTable.components == nil then 387 self.mainId = self.id; 388 self.components = { main = self.id }; 389 390 if dataTable.massCenter ~= nil and Rigidbody.isRigidbody(self.mainId) then 391 Rigidbody.setMassCenter(self.mainId, dataTable.massCenter.x, dataTable.massCenter.y, dataTable.massCenter.z); 392 end; 393 394 else 395 self.components = {}; 396 397 -- register all components 398 for k, v in pairs(dataTable.components) do 399 local id = getChild(self.id, v.index or ""); 400 local name = v.name or "main"; 401 402 if self.components[name] ~= nil then 403 print("Error while loading vehicle of template " .. tostring(self.templateName) .. ": Component name '" .. tostring(v.name) .. "' was assigned to two or more vehicle components. Please assign an individual name to each component."); 404 else 405 self.components[name] = id; 406 end; 407 408 if self.mainId == nil then 409 -- first component is always main component 410 self.mainId = id; 411 self.components.main = id; 412 end; 413 414 if v.massCenter ~= nil and Rigidboy.isRigidbody(id) then 415 Rigidbody.setMassCenter(id, v.massCenter.x, v.massCenter.y, v.massCenter.z); 416 end; 417 end; 418 end; 419 420 -- add collision ignore pairs 421 if dataTable.ignoreCollisions ~= nil then 422 for k, v in pairs(dataTable.ignoreCollisions) do 423 local componentA = self.components[v[1] or v.componentA or ""]; 424 local componentB = self.components[v[2] or v.componentB or ""]; 425 426 if componentA == nil then 427 print("Error while loading vehicle of template " .. tostring(self.templateName) .. ": Invalid joint component name '" .. tostring(v.componentA) .. "' (component). Make sure this vehicle component actually exists."); 428 elseif componentB == nil then 429 print("Error while loading vehicle of template " .. tostring(self.templateName) .. ": Invalid joint component name '" .. tostring(v.componentB) .. "' (attachTo). Make sure this vehicle component actually exists."); 430 431 else 432 Physics.ignoreCollision(componentA, componentB, true); 433 end; 434 end; 435 end; 436 437 -- let's go on to joints 438 if dataTable.componentJoints ~= nil then 439 self.componentJoints = {}; 440 441 for k, v in pairs(dataTable.componentJoints) do 442 local component = self.components[v.component or ""]; 443 local attachTo = self.components[v.attachTo or ""]; 444 445 local attachTarget = 0; 446 if v.attachPosition ~= nil then 447 attachTarget = getChild(self.id, v.attachPosition); 448 end; 449 local attachAnchor = 0; 450 if v.attachAnchor ~= nil then 451 attachAnchor = getChild(self.id, v.attachAnchor); 452 end; 453 454 if component == nil then 455 print("Error while loading vehicle of template " .. tostring(self.templateName) .. ": Invalid joint component name '" .. tostring(v.component) .. "' (component). Make sure this vehicle component actually exists."); 456 elseif attachTo == nil then 457 print("Error while loading vehicle of template " .. tostring(self.templateName) .. ": Invalid joint component name '" .. tostring(v.attachTo) .. "' (attachTo). Make sure this vehicle component actually exists."); 458 elseif v.attachPosition ~= nil and attachTarget == 0 then 459 print("Error while loading vehicle of template " .. tostring(self.templateName) .. ": Failed to index joint position index '" .. tostring(v.attachPosition) .. "'. Make sure this object actually exists."); 460 elseif v.attachAnchor ~= nil and attachAnchor == 0 then 461 print("Error while loading vehicle of template " .. tostring(self.templateName) .. ": Failed to index joint anchor index '" .. tostring(v.attachAnchor) .. "'. Make sure this object actually exists."); 462 elseif component == attachTo then 463 print("Error while loading vehicle of template " .. tostring(self.templateName) .. ": Cannot connect a vehicle component to itself using a joint. Make sure there is no joint connecting to itself (components '" .. tostring(v.component) .. "' and '" .. tostring(v.attachTo) .. "')."); 464 465 else 466 -- alright, we may create this joint 467 local joint = {}; 468 joint.id = Joint.addJoint(component, attachTo, v.axis, attachTarget or 0, attachAnchor or 0); 469 470 if v.springForce ~= nil then 471 Joint.setSpring(joint.id, v.springForce, v.springDamper or v.springForce * 0.01, v.targetPosition); 472 joint.useSpring = true; 473 end; 474 if v.limits ~= nil then 475 Joint.setLimits(joint.id, unpack(v.limits)); 476 joint.useLimits = true; 477 end; 478 479 joint.springForce = v.springForce or 0; 480 joint.springDamper = v.springDamper or 0.01 * joint.springForce; 481 joint.targetPosition = v.targetPosition or 0; 482 end; 483 end; 484 end; 485 end;
Vehicle:loadAxles(dataTable)
Loads the axles from the configuration table, and calls the function customLoadAxle()
for all vehicle scripts. An example for a use is VehicleMotor:customLoadAxle()
(see VehicleMotor)
488 function Vehicle:loadAxles(dataTable) 489 self.axles = {}; 490 491 if dataTable.axles == nil then 492 return; 493 end; 494 495 local axleCount = #dataTable.axles; 496 497 for i, v in pairs(dataTable.axles) do 498 local leftWheelIndex = v.leftWheelIndex or ""; 499 local rightWheelIndex = v.rightWheelIndex or ""; 500 501 -- !!TODO!! check whether these are existing 502 local leftWheelId = getChild(self.id, leftWheelIndex); 503 local rightWheelId = getChild(self.id, rightWheelIndex); 504 505 local axle = self:newAxle(leftWheelId, rightWheelId); 506 self.axles[i] = axle; 507 508 -- now fill it up with some data 509 axle.motorGear = v.motor or axle.motorGear; -- unused as long as VehicleMotor script is not invoked 510 axle.brake = v.brake or false; 511 axle.steeringScale = v.steeringScale or axle.steeringScale; 512 513 if v.leftWheelCaliper ~= nil then 514 axle.leftWheelCaliper = getChild(self.id, v.leftWheelCaliper); 515 end; 516 if v.rightWheelCaliper ~= nil then 517 axle.rightWheelCaliper = getChild(self.id, v.rightWheelCaliper); 518 end; 519 520 self:callVehicleScripts("customLoadAxle", axle, v); 521 end; 522 end;
Vehicle:newAxle(leftId, rightId)
Creates a new axle from the specified transform ids leftId
and rightId
(both int) and returns it.
525 function Vehicle:newAxle(leftId, rightId) 526 local axle = {}; 527 axle.leftId = leftId; 528 axle.rightId = rightId; 529 axle.leftWheel = Wheel.getWheelId(leftId); 530 axle.rightWheel = Wheel.getWheelId(rightId); 531 532 if getNumOfChildren(leftId) > 0 then axle.leftVisual = getChildAt(leftId, 0); end; 533 if getNumOfChildren(rightId) > 0 then axle.rightVisual = getChildAt(rightId, 0); end; 534 535 axle.motorGear = 0; -- 0: no power attached to this wheel, 1.0: all motor power to there 536 axle.brake = true; -- shall the brake be applied to this wheel? 537 axle.steeringScale = 0; -- coefficient defining steer angle min/max 538 return axle; 539 end; 540
Vehicle:getIsActive()
Returns whether the vehicle is active. Use this for all code blocks that shall be active when the vehicle shall be updated (e.g. lowering/lifting a snow groomer).
543 function Vehicle:getIsActive() 544 return self.isEntered; 545 end;
Vehicle:getIsInputActive()
Returns whether scripts on this vehicle may handle input. ALWAYS make sure to call if self:getIsInputActive() then
before any input queries. Besides some rare exceptions, input keys should never be active if Vehicle:getIsInputActive()
could return false.
548 function Vehicle:getIsInputActive() 549 return self.isEntered; 550 end;
Vehicle:getIsGUIActive()
Returns whether GUI elements regarding this vehicle are active and shall be updated. Currently, this is always true when the vehicle is entered, but this could change in the future (e.g. if we were to introduce new concepts for the overview menu).
553 function Vehicle:getIsGUIActive() 554 return self.isEntered; 555 end;
Vehicle:update(dt)
This handles a whole lot of things that need to be checked every frame:
- Checks whether the vehicle is active
- Handles input (both for separate throttle/brake and for combined controls) for the commonly used statements
self.throttleValue
,self.brakeValue
(both floats, range 0-1),self.steerValue
(float, range -1 to 1), andself.parkingBrake
(0 or 1) - Visual update of steering wheel and wheels.
561 function Vehicle:update(dt) 562 563 if self.resetKinematic ~= nil then 564 self.resetKinematic = self.resetKinematic - dt; 565 566 if self.resetKinematic <= 0 then 567 if Rigidbody.isRigidbody(self.mainId) then 568 Rigidbody.setIsKinematic(self.mainId, false); 569 end; 570 self.resetKinematic = nil; 571 end; 572 end; 573 574 self.isActive = self:getIsActive(); 575 576 if self:getIsGUIActive() then 577 -- update UI 578 setActive(getChild(GUI.vehicleHUD, "Icons1/Park/dark"), self.parkingBrake <= 0.5); 579 setActive(getChild(GUI.vehicleHUD, "Icons1/Park/bright"), self.parkingBrake > 0.5); 580 581 -- turn indicators are controlled in VehicleLighting.lua 582 end; 583 584 if self:getIsInputActive() then 585 586 if GameplaySettings.throttleBrakeSeparate then 587 self.throttleValue = InputMapper:getAxis(InputMapper.Axis_Vehicle_Throttle); 588 self.brakeValue = InputMapper:getAxis(InputMapper.Axis_Vehicle_Brake); 589 590 else 591 local inputValue = InputMapper:getAxis(InputMapper.Axis_Vehicle_Throttle_Brake_Combined); 592 593 if inputValue > 0 then 594 self.throttleValue = inputValue; 595 self.brakeValue = 0; 596 else 597 self.throttleValue = 0; 598 self.brakeValue = -inputValue; 599 end; 600 end; 601 602 local steerValue, fixed = InputMapper:getAxis(InputMapper.Axis_Vehicle_Steer); 603 -- directly write the analog value, but smoothen out digital values 604 if fixed then 605 self.steerValue = steerValue; 606 607 elseif steerValue ~= self.steerValue then 608 609 local returnSpeed = self.steeringSteerSpeed; 610 611 if steerValue == 0 or (self.steerValue >= 0) ~= (steerValue >= 0) and self.steeringReturnSpeed > self.steeringSteerSpeed then 612 returnSpeed = self.steeringReturnSpeed; 613 end; 614 615 local speedCoeff = 1; 616 if self.currentSpeed ~= nil then 617 speedCoeff = math.abs(1 - clamp(self.currentSpeed * self.steeringSlowdownCoeff, 0, self.steeringMaxSlowdown)); 618 end; 619 620 if self.steerValue < steerValue then 621 self.steerValue = math.min(self.steerValue + returnSpeed * speedCoeff * dt, steerValue); 622 623 else 624 self.steerValue = math.max(self.steerValue - returnSpeed * speedCoeff * dt, steerValue); 625 end; 626 end; 627 628 if self.parkingBrake > 0.5 then 629 g_GUI:addKeyHint(InputMapper.Vehicle_ParkingBrake, l10n.get("Input_Vehicle_ParkingBrake_release")); 630 end; 631 632 if InputMapper:getKeyDown(InputMapper.Vehicle_ParkingBrake) then 633 self:setParkingBrake(self.parkingBrake ~= 1); 634 635 -- play a sound as feedback 636 self:playOnOffSound(self.parkingBrake == 1, self.parkingBrakeSound1, self.parkingBrakeSound0); 637 end; 638 end; 639 640 if self.isActive then 641 -- update wheel visuals 642 for n, axle in pairs(self.axles) do 643 self:wheelPositionToVisual(axle.leftWheel, axle.leftVisual, axle.leftWheelCaliper); 644 self:wheelPositionToVisual(axle.rightWheel, axle.rightVisual, axle.rightWheelCaliper); 645 end; 646 647 -- update steering wheel 648 if self.steeringWheelId ~= nil then 649 if self.steeringWheelAxle == 1 then 650 setRotationX(self.steeringWheelId, self.steeringWheelAngle * self.steerValue); 651 652 elseif self.steeringWheelAxle == 2 then 653 setRotationY(self.steeringWheelId, self.steeringWheelAngle * self.steerValue); 654 655 else 656 setRotationZ(self.steeringWheelId, self.steeringWheelAngle * self.steerValue); 657 end; 658 end; 659 if self.steeringAnimationId ~= nil then 660 Animation.sampleTime(self.steeringAnimationId, self.steeringAnimationLength * 0.5 * (1 + self.steerValue)); 661 end; 662 end; 663 664 self:callVehicleScripts("update", dt); 665 end;
Vehicle:fixedUpdate(dt)
This is called every time the physics system is updated. This can happen more than once per frame. Please make sure that your vehicle scripts therefore do not waste any performance in here.
668 function Vehicle:fixedUpdate(dt) 669 -- apply wheel torques and steering angles 670 if not self.customWheelScriptActive and #self.axles > 0 then 671 self:wheelsUpdate(self:getCurrentMotorTorque(), self:getCurrentBrakeTorque(), self:getCurrentSteerAngle()); 672 end; 673 674 self:callVehicleScripts("fixedUpdate", dt); 675 end;
Vehicle:wheelsUpdate(motorTorque, brakeTorque, steerAngle)
Physics update for the vehicle's wheels.
678 function Vehicle:wheelsUpdate(motorTorque, brakeTorque, steerAngle) 679 for n, axle in pairs(self.axles) do 680 -- apply wheel torques 681 Wheel.setTorques(axle.leftWheel, motorTorque * axle.motorGear, axle.brake and brakeTorque or 0); 682 Wheel.setTorques(axle.rightWheel, motorTorque * axle.motorGear, axle.brake and brakeTorque or 0); 683 684 -- and now steering 685 if axle.steeringScale ~= 0 then 686 Wheel.setSteerAngle(axle.leftWheel, steerAngle * axle.steeringScale); 687 Wheel.setSteerAngle(axle.rightWheel, steerAngle * axle.steeringScale); 688 end; 689 end; 690 end;
Vehicle:wheelPositionToVisual(wheelId, visualWheel, caliperId)
Applies the wheel's position as determined by physics to the wheel's visual. If existing, the caliper will also be adjusted accordingly.
693 function Vehicle:wheelPositionToVisual(wheelId, visualWheel, caliperId) 694 if visualWheel == 0 or visualWheel == nil then return; end; 695 696 Wheel.applyPose(wheelId, visualWheel); 697 698 if caliperId ~= nil and caliperId ~= 0 then 699 Wheel.applyCaliperPose(wheelId, caliperId); 700 end; 701 end;
Vehicle:onEnter()
This calls onEnter
for all vehicle scripts and handles the parking brake.
704 function Vehicle:onEnter() 705 VehicleManager:onEnterVehicle(self); 706 self:callVehicleScripts("onEnter"); 707 708 if GameplaySettings.autoReleaseParkingBrake then 709 self:setParkingBrake(false); 710 end; 711 712 GUI:setVehicleHUDActive(true); 713 self:callVehicleScripts("onHUDActivate"); 714 end;
Vehicle:onLeave()
This calls onLeave
for all vehicle scripts and resets several variables to their default values.
717 function Vehicle:onLeave() 718 VehicleManager:onLeaveVehicle(self); 719 self:callVehicleScripts("onLeave"); 720 self:callVehicleScripts("onHUDDeactivate"); 721 GUI:setVehicleHUDActive(false); 722 723 -- reset throttle and set the parking brake 724 self.throttleValue = 0; 725 self.brakeValue = 0; 726 self:setParkingBrake(true); 727 728 -- directly apply parking brake (because fixedUpdate will no longer be called) 729 if not self.customWheelScriptActive then 730 for n, axle in pairs(self.axles) do 731 Wheel.setTorques(axle.leftWheel, 0, self.maxBrakeTorque); 732 Wheel.setTorques(axle.rightWheel, 0, self.maxBrakeTorque); 733 end; 734 end; 735 end;
Vehicle:enterByTab()
This is called by VehicleManager in case this vehicle shall be activated. Usually, it will be handled by the Seats vehicle script.
738 function Vehicle:enterByTab() 739 self:callVehicleScripts("enterByTab"); 740 -- onEnter is then called by Seats vehicle script 741 end;
Vehicle:leaveByTab()
This is called by VehicleManager in case the player wants to leave this vehicle. Again, it will usually be handled by Seats.
744 function Vehicle:leaveByTab() 745 self:callVehicleScripts("leaveByTab"); 746 -- onLeave is then called by vehiclescripts 747 end; 748
Vehicle:getCurrentMotorTorque()
This is a dummy function that is usually replaced by VehicleMotor.
751 function Vehicle:getCurrentMotorTorque() 752 -- to be replaced by a motorising vehicle script 753 return 0; 754 end;
Vehicle:getCurrentBrakeTorque()
Returns the current brake torque in Nm per wheel.
757 function Vehicle:getCurrentBrakeTorque() 758 return self.maxBrakeTorque * self.brakeValue + self.parkingBrakeTorque * self.parkingBrake; 759 end;
Vehicle:getCurrentSteerAngle()
Returns the steering angle in degrees.
762 function Vehicle:getCurrentSteerAngle() 763 return self.maxSteerAngle * self.steerValue; 764 end; 765
Vehicle:setGear(value)
Shifts the gear box to gear number value
(ranging from 1 to the number of gears).
768 function Vehicle:setGear(value) 769 self.currentGear = clamp(value, 1, #self.gears); 770 771 -- avoid automatic drive interruption 772 self.gearSwitchTimer = -self.gearSwitchPace; 773 end;
Vehicle:setParkingBrake(value)
Sets the vehicle's parking brake, depending on value
(bool).
776 function Vehicle:setParkingBrake(value) 777 self.parkingBrake = value and 1 or 0; 778 end;
Vehicle:setIsEntered(value)
Sets whether the player has entered the vehicle. This is usually called by the Seats vehicle script.
781 function Vehicle:setIsEntered(value) 782 783 if value == self.isEntered then 784 return; 785 end; 786 787 self.isEntered = value; 788 789 if value then 790 self:onEnter(); 791 else 792 self:onLeave(); 793 end; 794 end;
Vehicle:destroy()
Destroys the vehicle, e.g. if it is sold or if the game is closed.
797 function Vehicle:destroy() 798 VehicleManager:unregisterVehicle(self); 799 800 self:callVehicleScripts("destroy"); 801 802 delete(self.id); 803 end; 804
Copyright
All contents of this page may be used for modding use for Winter Resort Simulator only. Any use exceeding this regulation is not permitted.
Copyright (C) HR Innoways, 2020. All Rights Reserved.