Script v1_7_1_0
- AI
- Animals
- Collections
- Contracts
- Debug
- Economy
- Elements
- EnvironmentalScore
- Errors
- Events
- GUI
- Handtools
- Hud
- I3d
- Input
- Jobs
- Maps
- Materials
- Misc
- Objects
- Parameters
- Placeables
- Placement
- Player
- Shop
- Sounds
- Specialization
- Specializations
- StateMachine
- Statistics
- Tasks
- Triggers
- Utils
- Vehicles
Engine v1_7_1_0
- AI
- Animation
- Camera
- Entity
- Fillplanes
- general
- General
- I3D
- Input
- Lighting
- Math
- Network
- Node
- NoteNode
- Overlays
- Particle System
- Physics
- Rendering
- Scenegraph
- Shape
- Sound
- Spline
- String
- Terrain Detail
- Text Rendering
- Tire Track
- VoiceChat
- XML
Foundation Reference
VehicleMotor
DescriptionClass for vehicle motorsFunctions
- applyTargetGear
- calculatePhysicalMaximumBackwardSpeed
- calculatePhysicalMaximumForwardSpeed
- calculatePhysicalMaximumSpeed
- changeDirection
- delete
- findGearChangeTargetGearPrediction
- getAccelerationLimit
- getBestGear
- getBestGearRatio
- getBestStartGear
- getBrakeForce
- getCanMotorRun
- getClutchPedal
- getClutchRotSpeed
- getCurMaxRpm
- getDampingRateFullThrottle
- getDampingRateZeroThrottleClutchDisengaged
- getDampingRateZeroThrottleClutchEngaged
- getDrivingDirection
- getEqualizedMotorRpm
- getGearGroupToDisplay
- getGearRatio
- getGearRatioMultiplier
- getGearToDisplay
- getIsGearChangeAllowed
- getIsGearGroupChangeAllowed
- getIsInNeutral
- getLastModulatedMotorRpm
- getLastMotorRpm
- getLastRealMotorRpm
- getManualClutchPedal
- getMaxClutchTorque
- getMaximumBackwardSpeed
- getMaximumForwardSpeed
- getMaxRpm
- getMinMaxGearRatio
- getMinRpm
- getMotorAppliedTorque
- getMotorAvailableTorque
- getMotorExternalTorque
- getMotorRotationAccelerationLimit
- getMotorRotSpeed
- getNonClampedMotorRpm
- getPeakTorque
- getPtoMotorRpmRatio
- getRequiredMotorRpmRange
- getRequiredRpmAtSpeedLimit
- getRotInertia
- getSmoothedClutchPedal
- getSmoothLoadPercentage
- getSpeedLimit
- getStartInGearFactor
- getTorque
- getTorqueAndSpeedValues
- getTorqueCurve
- getTorqueCurveValue
- getUseAutomaticGearShifting
- getUseAutomaticGroupShifting
- new
- onManualClutchChanged
- postLoad
- readGearDataFromStream
- selectGear
- selectGroup
- setAccelerationLimit
- setAutoGearChangeTime
- setDampingRateScale
- setDirectionChange
- setDirectionChangeMode
- setEqualizedMotorRpm
- setExternalTorqueVirtualMultiplicator
- setGear
- setGearChangeTime
- setGearGroup
- setGearGroups
- setGearShiftMode
- setLastRpm
- setLowBrakeForce
- setManualShift
- setMotorRotationAccelerationLimit
- setRotInertia
- setRpmLimit
- setSpeedLimit
- setStartGearThreshold
- setTransmissionDirection
- shiftGear
- shiftGroup
- update
- updateGear
- updateSmoothLoadPercentage
- updateStartGearValues
- writeGearDataToStream
applyTargetGear
DescriptionApply target gearDefinition
applyTargetGear()Code
1695 | function VehicleMotor:applyTargetGear() |
1696 | local gearRatioMultiplier = self:getGearRatioMultiplier() |
1697 | self.gear = self.targetGear |
1698 | |
1699 | if self.gearShiftMode ~= VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then |
1700 | if self.currentGears[self.gear] ~= nil then |
1701 | self.minGearRatio = self.currentGears[self.gear].ratio * gearRatioMultiplier |
1702 | self.maxGearRatio = self.minGearRatio |
1703 | else |
1704 | self.minGearRatio = 0 |
1705 | self.maxGearRatio = 0 |
1706 | end |
1707 | |
1708 | self.startDebug = 0 |
1709 | end |
1710 | |
1711 | self.gearChangeTime = self.gearChangeTimeOrig |
1712 | |
1713 | local directionMultiplier = self.directionChangeUseGear and self.currentDirection or 1 |
1714 | SpecializationUtil.raiseEvent(self.vehicle, "onGearChanged", self.gear * directionMultiplier, self.targetGear * directionMultiplier, 0) |
1715 | end |
calculatePhysicalMaximumBackwardSpeed
DescriptionReturns physical maximum backward speedDefinition
calculatePhysicalMaximumBackwardSpeed()Return Values
float | physicalMaxBackwardSpeed | physical maximum backward speed |
902 | function VehicleMotor:calculatePhysicalMaximumBackwardSpeed() |
903 | return VehicleMotor.calculatePhysicalMaximumSpeed(self.minBackwardGearRatio, self.backwardGears or self.forwardGears, self.maxRpm) |
904 | end |
calculatePhysicalMaximumForwardSpeed
DescriptionReturns physical maximum forward speedDefinition
calculatePhysicalMaximumForwardSpeed()Return Values
float | physicalMaxForwardSpeed | physical maximum forward speed |
895 | function VehicleMotor:calculatePhysicalMaximumForwardSpeed() |
896 | return VehicleMotor.calculatePhysicalMaximumSpeed(self.minForwardGearRatio, self.forwardGears, self.maxRpm) |
897 | end |
calculatePhysicalMaximumSpeed
DescriptionReturns physical maximum speedDefinition
calculatePhysicalMaximumSpeed(float minGearRatio, table gears, Integer maxRpm)Arguments
float | minGearRatio | min gear ratio |
table | gears | gears |
Integer | maxRpm | max rpm |
float | physicalMaxSpeed | physical maximum speed |
912 | function VehicleMotor.calculatePhysicalMaximumSpeed(minGearRatio, gears, maxRpm) |
913 | local minRatio |
914 | if minGearRatio ~= nil then |
915 | minRatio = minGearRatio |
916 | elseif gears ~= nil then |
917 | minRatio = math.huge |
918 | for _, gear in pairs(gears) do |
919 | minRatio = math.min(minRatio, gear.ratio) |
920 | end |
921 | else |
922 | printCallstack() |
923 | return 0 |
924 | end |
925 | |
926 | return maxRpm * math.pi / (30 * minRatio) |
927 | end |
changeDirection
DescriptionDefinitionchangeDirection()Code
2144 | function VehicleMotor:changeDirection(direction, force) |
2145 | local targetDirection |
2146 | if direction == nil then |
2147 | targetDirection = -self.currentDirection |
2148 | else |
2149 | targetDirection = direction |
2150 | end |
2151 | |
2152 | if self.backwardGears == nil and self.forwardGears == nil then |
2153 | self.currentDirection = targetDirection |
2154 | SpecializationUtil.raiseEvent(self.vehicle, "onGearDirectionChanged", self.currentDirection) |
2155 | return |
2156 | end |
2157 | |
2158 | local changeAllowed = (self.directionChangeUseGroup and not self.gearGroupChangedIsLocked) |
2159 | or (self.directionChangeUseGear and not self.gearChangedIsLocked) |
2160 | or (not self.directionChangeUseGear and not self.directionChangeUseGroup) |
2161 | if changeAllowed then |
2162 | if targetDirection ~= self.currentDirection or force then |
2163 | self.currentDirection = targetDirection |
2164 | |
2165 | if self.directionChangeTime > 0 then |
2166 | self.directionChangeTimer = self.directionChangeTime |
2167 | self.gear = 0 |
2168 | self.minGearRatio = 0 |
2169 | self.maxGearRatio = 0 |
2170 | end |
2171 | |
2172 | local oldGearGroupIndex = self.activeGearGroupIndex |
2173 | if self.currentDirection < 0 then |
2174 | if self.directionChangeUseGear then |
2175 | self.directionLastGear = self.targetGear |
2176 | if not self:getUseAutomaticGearShifting() or not self.lastManualShifterActive then |
2177 | self.targetGear = self.directionChangeGearIndex |
2178 | end |
2179 | |
2180 | self.currentGears = self.backwardGears or self.forwardGears |
2181 | elseif self.directionChangeUseGroup then |
2182 | self.directionLastGroup = self.activeGearGroupIndex |
2183 | self.activeGearGroupIndex = self.directionChangeGroupIndex |
2184 | end |
2185 | else |
2186 | if self.directionChangeUseGear then |
2187 | if not self:getUseAutomaticGearShifting() or not self.lastManualShifterActive then |
2188 | if self.directionLastGear > 0 then |
2189 | self.targetGear = not self:getUseAutomaticGearShifting() and self.directionLastGear or self.defaultForwardGear |
2190 | else |
2191 | self.targetGear = self.defaultForwardGear |
2192 | end |
2193 | end |
2194 | |
2195 | self.currentGears = self.forwardGears |
2196 | elseif self.directionChangeUseGroup then |
2197 | if self.directionLastGroup > 0 then |
2198 | self.activeGearGroupIndex = self.directionLastGroup |
2199 | else |
2200 | self.activeGearGroupIndex = self.defaultGearGroup |
2201 | end |
2202 | end |
2203 | end |
2204 | |
2205 | SpecializationUtil.raiseEvent(self.vehicle, "onGearDirectionChanged", self.currentDirection) |
2206 | |
2207 | local directionMultiplier = self.directionChangeUseGear and self.currentDirection or 1 |
2208 | SpecializationUtil.raiseEvent(self.vehicle, "onGearChanged", self.gear * directionMultiplier, self.targetGear * directionMultiplier, self.directionChangeTime) |
2209 | |
2210 | if self.activeGearGroupIndex ~= oldGearGroupIndex then |
2211 | SpecializationUtil.raiseEvent(self.vehicle, "onGearGroupChanged", self.activeGearGroupIndex, self.directionChangeTime) |
2212 | end |
2213 | |
2214 | if self.directionChangeTime == 0 then |
2215 | self:applyTargetGear() |
2216 | end |
2217 | end |
2218 | end |
2219 | end |
delete
DescriptionDefinitiondelete()Code
322 | function VehicleMotor:delete() |
323 | g_messageCenter:unsubscribeAll(self) |
324 | end |
findGearChangeTargetGearPrediction
DescriptionDefinitionfindGearChangeTargetGearPrediction()Code
1436 | function VehicleMotor:findGearChangeTargetGearPrediction(curGear, gears, gearSign, gearChangeTimer, acceleratorPedal, dt) |
1437 | local newGear = curGear |
1438 | local gearRatioMultiplier = self:getGearRatioMultiplier() |
1439 | |
1440 | local minAllowedRpm, maxAllowedRpm = self.minRpm, self.maxRpm |
1441 | --print(string.format("rpmRange [%.2f %.2f]", minAllowedRpm, maxAllowedRpm)) |
1442 | local gearRatio = math.abs(gears[curGear].ratio * gearRatioMultiplier) |
1443 | |
1444 | local differentialRotSpeed = math.max(self.differentialRotSpeed*gearSign, 0.0001) |
1445 | local differentialRpm = differentialRotSpeed * 30 / math.pi |
1446 | local clutchRpm = differentialRpm * gearRatio |
1447 | --log("differentialRpm", differentialRpm, "gearRatio", gearRatio, "clutchRpm", clutchRpm, "gearSign", gearSign, "self.differentialRotSpeed", self.differentialRotSpeed) |
1448 | |
1449 | |
1450 | -- 1. Predict the velocity of the vehicle after the gear change |
1451 | local diffSpeedAfterChange |
1452 | if math.abs(acceleratorPedal) < 0.0001 then |
1453 | -- Assume that we will continue decelerating with 80% of the current deceleration |
1454 | local brakeAcc = math.min(self.differentialRotAccelerationSmoothed*gearSign*0.8, 0) |
1455 | diffSpeedAfterChange = math.max(differentialRotSpeed + brakeAcc * self.gearChangeTime*0.001, 0) |
1456 | --print(string.format("brake expectedAcc: %.3f realAcc %.3f %.3f max: %.2f gr: %.2f speed: %.2f", brakeAcc, self.vehicle.lastSpeedAcceleration*1000*1000, self.differentialRotAccelerationSmoothed, maxExpectedAcc, gearRatio, self.vehicle.lastSpeedReal*1000)) |
1457 | else |
1458 | -- Ignore wheels mass as it is usually negligible and the calculation below is not correct when the differential acceleration is not uniformely distributed |
1459 | --[[local neededWheelsInertiaTorque = 0 |
1460 | local specWheels = self.vehicle.spec_wheels |
1461 | for _, wheel in pairs(specWheels.wheels) do |
1462 | local invRotInterita = 2.0 / (wheel.mass*wheel.radius * wheel.radius) |
1463 | neededWheelsInertiaTorque = neededWheelsInertiaTorque + invRotInterita * self.differentialRotAcceleration * wheel.radius |
1464 | end |
1465 | neededWheelsInertiaTorque = neededWheelsInertiaTorque / (gearRatio*gearRatio)]] |
1466 | |
1467 | local lastMotorRotSpeed = self.motorRotSpeed - self.motorRotAcceleration * (g_physicsDtLastValidNonInterpolated*0.001) |
1468 | local lastDampedMotorRotSpeed = lastMotorRotSpeed / (1.0 + self.dampingRateFullThrottle/self.rotInertia*g_physicsDtLastValidNonInterpolated*0.001) |
1469 | |
1470 | |
1471 | local neededInertiaTorque = (self.motorRotSpeed - lastDampedMotorRotSpeed)/(g_physicsDtLastValidNonInterpolated*0.001) * self.rotInertia |
1472 | |
1473 | local lastMotorTorque = (self.motorAppliedTorque - self.motorExternalTorque - neededInertiaTorque) |
1474 | |
1475 | --print(string.format("load: %.3f expected torque: %.3f neededPtoTorque %.3f neededInertiaTorque %.4f", self.motorAppliedTorque, self.motorAvailableTorque, self.motorExternalTorque, neededInertiaTorque)) |
1476 | |
1477 | local totalMass = self.vehicle:getTotalMass() |
1478 | local expectedAcc = lastMotorTorque * gearRatio / totalMass -- differential rad/s^2 |
1479 | |
1480 | -- The the difference in acceleration is due to gravity and thus will pull back the vehicle when changing gears and some other reasons (non-accounted mass (e.g. trees), collisions, wheel damping, wheel mass, ...) |
1481 | -- Use a fixed factor of 90% to separate the effect of the gravity |
1482 | local uncalculatedAccFactor = 0.9 |
1483 | local gravityAcc = math.max(expectedAcc*uncalculatedAccFactor - math.max(self.differentialRotAccelerationSmoothed*gearSign, 0), 0) |
1484 | |
1485 | --print(string.format("expectedAcc: %.3f realAcc: %.3f %.3f gravityAcc: %.3f gr: %.2f mass %.1f speed: %.3f dt %.2fms", expectedAcc, self.vehicle.lastSpeedAcceleration*1000*1000, self.differentialRotAcceleration, gravityAcc, gearRatio, totalMass, self.vehicle.lastSpeedReal*1000, g_physicsDtLastValidNonInterpolated)) |
1486 | |
1487 | diffSpeedAfterChange = math.max(differentialRotSpeed - gravityAcc * self.gearChangeTime*0.001, 0) |
1488 | |
1489 | --log("differentialRotSpeed", differentialRotSpeed, "gravityAcc", gravityAcc, "expectedAcc", expectedAcc, "self.differentialRotAccelerationSmoothed", self.differentialRotAccelerationSmoothed, "gearRatio", gearRatio, "lastMotorTorque", lastMotorTorque, "neededInertiaTorque", neededInertiaTorque, "lastDampedMotorRotSpeed", lastDampedMotorRotSpeed, "lastMotorRotSpeed", lastMotorRotSpeed) |
1490 | end |
1491 | |
1492 | |
1493 | -- 2. Find the gear that gives the maximum power in the valid rpm range after the gear change |
1494 | -- If none is valid, store the gear that will get closest to the valid rpm range |
1495 | |
1496 | -- TODO allow some clutch slippage to extend the possible rpm range (e.g. when accelerating and switching from gear 1 to gear 2) |
1497 | |
1498 | local maxPower = 0 |
1499 | local maxPowerGear = 0 |
1500 | for gear=1, #gears do |
1501 | local rpm |
1502 | if gear == curGear then |
1503 | rpm = clutchRpm |
1504 | else |
1505 | rpm = diffSpeedAfterChange * math.abs(gears[gear].ratio * gearRatioMultiplier) * 30 / math.pi |
1506 | end |
1507 | |
1508 | -- if we could start in this gear we allow changes, no matter of rpm and power |
1509 | local startInGearFactor = self:getStartInGearFactor(gears[gear].ratio * gearRatioMultiplier) |
1510 | local minRpmFactor = 1 |
1511 | if startInGearFactor < self.startGearThreshold then |
1512 | minRpmFactor = 0 |
1513 | end |
1514 | |
1515 | -- current gear is always allowed since clutchRpm could be slightly highe ror lower then the limits due to float 32 |
1516 | if (rpm <= maxAllowedRpm and rpm >= minAllowedRpm * minRpmFactor) or gear == curGear then |
1517 | local power = self:getTorqueCurveValue(rpm) * rpm |
1518 | --print(string.format(" power %.2f @ %.d %d", power, gear, rpm)) |
1519 | if power >= maxPower then |
1520 | maxPower = power |
1521 | maxPowerGear = gear |
1522 | end |
1523 | end |
1524 | end |
1525 | |
1526 | --local curPower = self:getTorqueCurveValue(clutchRpm) * clutchRpm |
1527 | --print(string.format("power %.2f @ %d rpms: %.2f %.2f diffSpeedAfterChange: %.10f drpm: %.2f", curPower, curGear, clutchRpm, diffSpeedAfterChange * gearRatio * 30 / math.pi, diffSpeedAfterChange, self.differentialRotAccelerationSmoothed * gearRatio * 30 / math.pi)) |
1528 | |
1529 | local neededPowerPct = 0.8 |
1530 | |
1531 | -- 3. Find the gear with the best tradeoff (lots of power with low rpm) |
1532 | -- Or use the the gear will get closest to the valid rpm range if none of the gears are good |
1533 | if maxPowerGear ~= 0 then |
1534 | local bestTradeoff = 0 |
1535 | |
1536 | for gear=#gears,1,-1 do |
1537 | local validGear = false |
1538 | local nextRpm |
1539 | if gear == curGear then |
1540 | nextRpm = clutchRpm |
1541 | else |
1542 | nextRpm = diffSpeedAfterChange * math.abs(gears[gear].ratio * gearRatioMultiplier) * 30 / math.pi |
1543 | end |
1544 | |
1545 | -- if we could start in this gear we allow changes, no matter of rpm and power |
1546 | local startInGearFactor = self:getStartInGearFactor(gears[gear].ratio * gearRatioMultiplier) |
1547 | local minRpmFactor = 1 |
1548 | local neededPowerPctGear = neededPowerPct |
1549 | if startInGearFactor < self.startGearThreshold then |
1550 | neededPowerPctGear = 0 |
1551 | minRpmFactor = 0 |
1552 | end |
1553 | |
1554 | if nextRpm <= maxAllowedRpm and nextRpm >= minAllowedRpm * minRpmFactor or gear == curGear then |
1555 | local nextPower = self:getTorqueCurveValue(nextRpm) * nextRpm |
1556 | |
1557 | -- Choose the gear if it gets close enough to the max power |
1558 | if nextPower >= maxPower*neededPowerPctGear or gear == curGear then |
1559 | local powerFactor = (nextPower - maxPower*neededPowerPctGear) / (maxPower*(1-neededPowerPctGear)) -- 0 when at 80% of maxPower, 1 when at maxPower |
1560 | local curSpeedRpm = differentialRpm * math.abs(gears[gear].ratio * gearRatioMultiplier) |
1561 | local rpmFactor = MathUtil.clamp((maxAllowedRpm - curSpeedRpm) / math.max(maxAllowedRpm-minAllowedRpm, 0.001), 0, 2) |
1562 | if rpmFactor > 1 then |
1563 | rpmFactor = 1 - (rpmFactor - 1) * 4 |
1564 | end |
1565 | |
1566 | local gearChangeFactor |
1567 | if gear == curGear then |
1568 | gearChangeFactor = 1 |
1569 | else |
1570 | gearChangeFactor = math.min(-gearChangeTimer / 2000, 0.9) -- the longer we wait, the less penality we add for gear changes |
1571 | end |
1572 | |
1573 | local rpmPreferenceFactor = 0 |
1574 | -- when shifting down the lower gear should have a higher rpm, otherwise we penalize it with -1 |
1575 | if gear < curGear then |
1576 | rpmPreferenceFactor = MathUtil.clamp((nextRpm - clutchRpm) / 250, -1, 0) |
1577 | end |
1578 | |
1579 | -- when starting with a preselected/higher gear we force to use it as long as the factor is still valid |
1580 | if gear < self.bestGearSelected then |
1581 | local factor = self:getStartInGearFactor(gearRatio) |
1582 | if factor < self.startGearThreshold then |
1583 | gearChangeFactor = gearChangeFactor - 3 |
1584 | end |
1585 | end |
1586 | |
1587 | -- prefer middle rpm range instead of upper and lower 20% of range |
1588 | rpmPreferenceFactor = rpmPreferenceFactor - (1-math.min(math.sin(rpmFactor * math.pi)*5, 2)) * 0.7 |
1589 | |
1590 | -- if multiple gears are able to stay in the prefered rpm range, we always prefer the gear we are in until it's getting out of the range |
1591 | -- this prevents to much shifting when we have a lot of gears with small ratio steps |
1592 | -- only apply if rpmPreferenceFactor is postive so we do not negative influence the current gear |
1593 | if gear == curGear and rpmPreferenceFactor > 0 then |
1594 | rpmPreferenceFactor = rpmPreferenceFactor * 1.5 |
1595 | end |
1596 | |
1597 | if math.abs(acceleratorPedal) < 0.0001 then |
1598 | rpmFactor = 1-rpmFactor -- choose a high rpm when decelerating |
1599 | else |
1600 | rpmFactor = rpmFactor * 2 |
1601 | end |
1602 | |
1603 | -- when just rolling allow downshifting to use motor brake when below 25% of rpm range |
1604 | -- so we avoid hitting always the highest rpm on the lower gear (would be better for motor break, but sounds stupid) |
1605 | if math.abs(acceleratorPedal) < 0.0001 then |
1606 | if (clutchRpm - minRpmFactor) / (maxAllowedRpm - minRpmFactor) > 0.25 then |
1607 | if gear < curGear then |
1608 | powerFactor = 0 |
1609 | rpmFactor = 0 |
1610 | elseif gear == curGear then |
1611 | powerFactor = 1 |
1612 | rpmFactor = 1 |
1613 | end |
1614 | end |
1615 | end |
1616 | |
1617 | -- if we could start in the gear we don't care about the power and rpm preference |
1618 | -- only apply to higher gears, so we won't accidentally rate lower gears higher than current gear if current gear is in higher rpms |
1619 | if gear > curGear then |
1620 | if startInGearFactor < self.startGearThreshold then |
1621 | powerFactor = 1 |
1622 | rpmPreferenceFactor = 1 |
1623 | end |
1624 | end |
1625 | |
1626 | local tradeoff = powerFactor + rpmFactor + gearChangeFactor + rpmPreferenceFactor |
1627 | |
1628 | if tradeoff >= bestTradeoff then |
1629 | bestTradeoff = tradeoff |
1630 | newGear = gear |
1631 | -- print(string.format("better tradeoff %.2f with %d power: %.2f vs %.2f @ %d rpm %.2f/%.2f vs %.2f factors: %.2f %.2f %.2f %.2f", tradeoff, gear, nextPower, maxPower, maxPowerGear, nextRpm, curSpeedRpm, clutchRpm, powerFactor, rpmFactor, gearChangeFactor, rpmPreferenceFactor)) |
1632 | --else |
1633 | -- print(string.format("worse tradeoff %.2f with %d power: %.2f vs %.2f @ %d rpm %.2f/%.2f vs %.2f factors: %.2f %.2f %.2f %.2f", tradeoff, gear, nextPower, maxPower, maxPowerGear, nextRpm, curSpeedRpm, clutchRpm, powerFactor, rpmFactor, gearChangeFactor, rpmPreferenceFactor)) |
1634 | end |
1635 | |
1636 | if VehicleDebug.state == VehicleDebug.DEBUG_TRANSMISSION then |
1637 | gears[gear].lastTradeoff = tradeoff |
1638 | gears[gear].lastDiffSpeedAfterChange = gear == curGear and diffSpeedAfterChange or nil |
1639 | gears[gear].lastPowerFactor = powerFactor |
1640 | gears[gear].lastRpmFactor = rpmFactor |
1641 | gears[gear].lastGearChangeFactor = gearChangeFactor |
1642 | gears[gear].lastRpmPreferenceFactor = rpmPreferenceFactor |
1643 | gears[gear].lastNextPower = nextPower |
1644 | gears[gear].nextPowerValid = true |
1645 | gears[gear].lastNextRpm = nextRpm |
1646 | gears[gear].nextRpmValid = true |
1647 | gears[gear].lastMaxPower = maxPower |
1648 | gears[gear].lastHasPower = true |
1649 | end |
1650 | |
1651 | validGear = true |
1652 | else |
1653 | if VehicleDebug.state == VehicleDebug.DEBUG_TRANSMISSION then |
1654 | gears[gear].lastNextPower = nextPower |
1655 | end |
1656 | end |
1657 | end |
1658 | |
1659 | if not validGear then |
1660 | if VehicleDebug.state == VehicleDebug.DEBUG_TRANSMISSION then |
1661 | gears[gear].lastTradeoff = 0 |
1662 | gears[gear].lastPowerFactor = 0 |
1663 | gears[gear].lastRpmFactor = 0 |
1664 | gears[gear].lastGearChangeFactor = 0 |
1665 | gears[gear].lastRpmPreferenceFactor = 0 |
1666 | gears[gear].lastDiffSpeedAfterChange = gear == curGear and diffSpeedAfterChange or nil |
1667 | gears[gear].lastNextRpm = nextRpm |
1668 | gears[gear].nextRpmValid = nextRpm <= maxAllowedRpm and nextRpm >= minAllowedRpm * minRpmFactor |
1669 | gears[gear].nextPowerValid = false |
1670 | gears[gear].lastMaxPower = maxPower |
1671 | gears[gear].lastHasPower = false |
1672 | end |
1673 | end |
1674 | end |
1675 | else |
1676 | local minDiffGear = 0 |
1677 | local minDiff = math.huge |
1678 | for gear=1,#gears do |
1679 | local rpm = diffSpeedAfterChange * math.abs(gears[gear].ratio * gearRatioMultiplier) * 30 / math.pi |
1680 | local diff = math.max(rpm - maxAllowedRpm, minAllowedRpm - rpm) |
1681 | if diff < minDiff then |
1682 | --print(string.format("better min diff gear: %d diff: %.2f rpm: %.2f" , gear, diff, rpm)) |
1683 | minDiff = diff |
1684 | minDiffGear = gear |
1685 | end |
1686 | end |
1687 | newGear = minDiffGear |
1688 | end |
1689 | |
1690 | return newGear |
1691 | end |
getAccelerationLimit
DescriptionDefinitiongetAccelerationLimit()Code
2404 | function VehicleMotor:getAccelerationLimit() |
2405 | return self.accelerationLimit |
2406 | end |
getBestGear
DescriptionReturns best gearDefinition
getBestGear(float acceleration, float wheelSpeedRpm, float accSafeMotorRpm, float requiredMotorPower, float requiredMotorRpm)Arguments
float | acceleration | acceleration |
float | wheelSpeedRpm | wheel speed rpm |
float | accSafeMotorRpm | acc save motor rpm |
float | requiredMotorPower | required wheel torque |
float | requiredMotorRpm | required motor rpm |
float | bestGear | best gear |
float | gearRatio | gear ratio |
1404 | function VehicleMotor:getBestGear(acceleration, wheelSpeedRpm, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm) |
1405 | if math.abs(acceleration) < 0.001 then |
1406 | acceleration = 1 |
1407 | if wheelSpeedRpm < 0 then |
1408 | acceleration = -1 |
1409 | end |
1410 | end |
1411 | if acceleration > 0 then |
1412 | if self.minForwardGearRatio ~= nil then |
1413 | wheelSpeedRpm = math.max(wheelSpeedRpm, 0) |
1414 | local bestGearRatio = self:getBestGearRatio(wheelSpeedRpm, self.minForwardGearRatio, self.maxForwardGearRatio, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm) |
1415 | return 1, bestGearRatio |
1416 | else |
1417 | return 1, self.forwardGears[1].ratio |
1418 | end |
1419 | else |
1420 | if self.minBackwardGearRatio ~= nil then |
1421 | wheelSpeedRpm = math.max(-wheelSpeedRpm, 0) |
1422 | local bestGearRatio = self:getBestGearRatio(wheelSpeedRpm, self.minBackwardGearRatio, self.maxBackwardGearRatio, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm) |
1423 | return -1, -bestGearRatio |
1424 | else |
1425 | if self.backwardGears ~= nil then |
1426 | return -1, -self.backwardGears[1].ratio |
1427 | else |
1428 | return 1, self.forwardGears[1].ratio |
1429 | end |
1430 | end |
1431 | end |
1432 | end |
getBestGearRatio
DescriptionReturns best gear ratioDefinition
getBestGearRatio(float wheelSpeedRpm, float minRatio, float maxRatio, float accSafeMotorRpm, float requiredMotorPower, float requiredMotorRpm)Arguments
float | wheelSpeedRpm | wheel speed rpm |
float | minRatio | min ratio |
float | maxRatio | max ratio |
float | accSafeMotorRpm | acc save motor rpm |
float | requiredMotorPower | the required motor power [kW] (can be bigger than what the motor can actually achieve) |
float | requiredMotorRpm | fixed motor rpm to be used (if not 0) |
float | bestGearRatio | best gear ratio |
1359 | function VehicleMotor:getBestGearRatio(wheelSpeedRpm, minRatio, maxRatio, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm) |
1360 | |
1361 | if requiredMotorRpm ~= 0 then |
1362 | local gearRatio = math.max(requiredMotorRpm-accSafeMotorRpm, requiredMotorRpm*0.8) / math.max(wheelSpeedRpm, 0.001) |
1363 | gearRatio = MathUtil.clamp(gearRatio, minRatio, maxRatio) |
1364 | return gearRatio |
1365 | end |
1366 | |
1367 | -- Use a minimum wheel rpm to avoid that gearRatio is ignored |
1368 | wheelSpeedRpm = math.max(wheelSpeedRpm, 0.0001) |
1369 | |
1370 | local bestMotorPower = 0 |
1371 | local bestGearRatio = minRatio |
1372 | --local bestRPM = 0 |
1373 | -- TODO make this more efficient |
1374 | for gearRatio = minRatio, maxRatio, 0.5 do |
1375 | local motorRpm = wheelSpeedRpm * gearRatio |
1376 | if motorRpm > self.maxRpm - accSafeMotorRpm then |
1377 | break |
1378 | end |
1379 | local motorPower = self:getTorqueCurveValue(math.max(motorRpm, self.minRpm)) * motorRpm *math.pi/30 |
1380 | if motorPower > bestMotorPower then |
1381 | bestMotorPower = motorPower |
1382 | bestGearRatio = gearRatio |
1383 | --bestRPM = motorRpm |
1384 | end |
1385 | |
1386 | if motorPower >= requiredMotorPower then |
1387 | break |
1388 | end |
1389 | end |
1390 | --print(string.format("Selected best gear: %f, %.2fkW rpm %.2f wheel %.2f", bestGearRatio, bestMotorPower, bestRPM, wheelSpeedRpm,)) |
1391 | |
1392 | return bestGearRatio |
1393 | end |
getBestStartGear
DescriptionReturns the highest gear possible to startDefinition
getBestStartGear(table gears)Arguments
table | gears | gears |
integer | bestGear | best gear to start |
1255 | function VehicleMotor:getBestStartGear(gears) |
1256 | local directionMultiplier = self.directionChangeUseGroup and 1 or self.currentDirection |
1257 | |
1258 | local minFactor = math.huge |
1259 | local minFactorGear, minFactorGroup = 1, 1 |
1260 | |
1261 | local maxFactor = 0 |
1262 | local maxFactorGear, maxFactorGroup = 1, 1 -- use min gear in min group as default return value |
1263 | if self.gearGroups ~= nil then |
1264 | if self:getUseAutomaticGroupShifting() then |
1265 | for j=1, #self.gearGroups do |
1266 | local groupRatio = self.gearGroups[j].ratio * directionMultiplier |
1267 | if MathUtil.sign(groupRatio) == self.currentDirection or not self.directionChangeUseGroup then |
1268 | for i=1, #gears do |
1269 | local factor = self:getStartInGearFactor(gears[i].ratio * groupRatio) |
1270 | if factor < self.startGearThreshold then |
1271 | if factor > maxFactor then |
1272 | maxFactor = factor |
1273 | maxFactorGear = i |
1274 | maxFactorGroup = j |
1275 | end |
1276 | end |
1277 | |
1278 | if factor < minFactor then |
1279 | minFactor = factor |
1280 | minFactorGear = i |
1281 | minFactorGroup = j |
1282 | end |
1283 | end |
1284 | end |
1285 | end |
1286 | end |
1287 | else |
1288 | local gearRatioMultiplier = self:getGearRatioMultiplier() |
1289 | for i=1, #gears do |
1290 | local factor = self:getStartInGearFactor(gears[i].ratio * gearRatioMultiplier) |
1291 | if factor < self.startGearThreshold then |
1292 | if factor > maxFactor then |
1293 | maxFactor = factor |
1294 | maxFactorGear = i |
1295 | end |
1296 | end |
1297 | |
1298 | if factor < minFactor then |
1299 | minFactor = factor |
1300 | minFactorGear = i |
1301 | end |
1302 | end |
1303 | end |
1304 | |
1305 | -- return the gear with the lowest factor if we don't find any gear below self.startGearThreshold |
1306 | if maxFactor == 0 then |
1307 | return minFactorGear, minFactorGroup |
1308 | end |
1309 | |
1310 | return maxFactorGear, maxFactorGroup |
1311 | end |
getBrakeForce
DescriptionReturns brake forceDefinition
getBrakeForce()Return Values
float | brakeForce | brake force |
476 | function VehicleMotor:getBrakeForce() |
477 | return self.brakeForce |
478 | end |
getCanMotorRun
DescriptionDefinitiongetCanMotorRun()Code
2342 | function VehicleMotor:getCanMotorRun() |
2343 | if self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then |
2344 | if not self.vehicle:getIsMotorStarted() then |
2345 | if self.backwardGears or self.forwardGears then |
2346 | if self.manualClutchValue == 0 and self.maxGearRatio ~= 0 then |
2347 | local factor = (self:getClutchRotSpeed() * 30 / math.pi + 50) / self:getNonClampedMotorRpm() |
2348 | if factor < 0.2 then |
2349 | return false, VehicleMotor.REASON_CLUTCH_NOT_ENGAGED |
2350 | end |
2351 | end |
2352 | end |
2353 | end |
2354 | end |
2355 | |
2356 | return true |
2357 | end |
getClutchPedal
DescriptionReturns clutch pedal stateDefinition
getClutchPedal()Return Values
float | state | state [0-1] |
551 | function VehicleMotor:getClutchPedal() |
552 | if not self.vehicle.isServer or self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then |
553 | return self.manualClutchValue |
554 | end |
555 | |
556 | return 1 - math.max(math.min((self:getClutchRotSpeed() * 30 / math.pi + 50) / self:getNonClampedMotorRpm(), 1), 0) -- have 50 rpm tolerance |
557 | end |
getClutchRotSpeed
DescriptionReturns clutch rpmDefinition
getClutchRotSpeed()Return Values
float | clutchRpm | clutch rpm |
834 | function VehicleMotor:getClutchRotSpeed() |
835 | return self.differentialRotSpeed * self.gearRatio |
836 | end |
getCurMaxRpm
DescriptionReturns current max rpmDefinition
getCurMaxRpm()Return Values
Integer | maxRpm | current max rpm |
2363 | function VehicleMotor:getCurMaxRpm() |
2364 | local maxRpm = self.maxRpm |
2365 | |
2366 | local gearRatio = self:getGearRatio() |
2367 | if gearRatio ~= 0 then |
2368 | --local speedLimit = self.speedLimit * 0.277778 |
2369 | local speedLimit = math.min(self.speedLimit, math.max(self.speedLimitAcc, self.vehicle.lastSpeedReal*3600)) * 0.277778 |
2370 | if gearRatio > 0 then |
2371 | speedLimit = math.min(speedLimit, self.maxForwardSpeed) |
2372 | else |
2373 | speedLimit = math.min(speedLimit, self.maxBackwardSpeed) |
2374 | end |
2375 | |
2376 | maxRpm = math.min(maxRpm, speedLimit * 30 / math.pi * math.abs(gearRatio)) |
2377 | end |
2378 | |
2379 | maxRpm = math.min(maxRpm, self.rpmLimit) |
2380 | return maxRpm |
2381 | end |
getDampingRateFullThrottle
DescriptionReturns the damping rate of the motor if the acceleration pedal is 1Definition
getDampingRateFullThrottle()Return Values
float | dampingRate | damping rate [t m^2 s^-1] |
420 | function VehicleMotor:getDampingRateFullThrottle() |
421 | return self.dampingRateFullThrottle |
422 | end |
getDampingRateZeroThrottleClutchDisengaged
DescriptionReturns the damping rate of the motor if the acceleration pedal is 0 and the clutch is disengagedDefinition
getDampingRateZeroThrottleClutchDisengaged()Return Values
float | dampingRate | damping rate [t m^2 s^-1] |
434 | function VehicleMotor:getDampingRateZeroThrottleClutchDisengaged() |
435 | return self.dampingRateZeroThrottleClutchDisengaged |
436 | end |
getDampingRateZeroThrottleClutchEngaged
DescriptionReturns the damping rate of the motor if the acceleration pedal is 0 and the clutch is engagedDefinition
getDampingRateZeroThrottleClutchEngaged()Return Values
float | dampingRate | damping rate [t m^2 s^-1] |
427 | function VehicleMotor:getDampingRateZeroThrottleClutchEngaged() |
428 | return self.dampingRateZeroThrottleClutchEngaged |
429 | end |
getDrivingDirection
DescriptionReturns the current driving direction or preselected directionDefinition
getDrivingDirection()Code
645 | function VehicleMotor:getDrivingDirection() |
646 | if self.directionChangeMode == VehicleMotor.DIRECTION_CHANGE_MODE_MANUAL or self.gearShiftMode ~= VehicleMotor.SHIFT_MODE_AUTOMATIC then |
647 | return self.currentDirection |
648 | else |
649 | if self.vehicle:getLastSpeed() > 0.95 then |
650 | return self.vehicle.movingDirection |
651 | end |
652 | end |
653 | |
654 | return 0 |
655 | end |
getEqualizedMotorRpm
DescriptionReturns equalized motor rpmDefinition
getEqualizedMotorRpm()Return Values
float | equalizedMotorRpm | equalized motor rpm |
790 | function VehicleMotor:getEqualizedMotorRpm() |
791 | return self.equalizedMotorRpm |
792 | end |
getGearGroupToDisplay
DescriptionReturns the current gear groupDefinition
getGearGroupToDisplay()Return Values
string | group | group |
boolean | groupsAvailable | groupsAvailable |
661 | function VehicleMotor:getGearGroupToDisplay() |
662 | local gearGroupName, available = "N", false |
663 | if self.backwardGears or self.forwardGears then |
664 | if self.gearGroups ~= nil then |
665 | if self.activeGearGroupIndex > 0 then |
666 | local gearGroup = self.gearGroups[self.activeGearGroupIndex] |
667 | if gearGroup ~= nil then |
668 | gearGroupName = gearGroup.name |
669 | end |
670 | end |
671 | |
672 | available = true |
673 | end |
674 | end |
675 | |
676 | return gearGroupName, available |
677 | end |
getGearRatio
DescriptionDefinitiongetGearRatio()Code
2305 | function VehicleMotor:getGearRatio() |
2306 | return self.gearRatio |
2307 | end |
getGearRatioMultiplier
DescriptionReturns ratio from current selected gear group or 1 if non is definedDefinition
getGearRatioMultiplier()Return Values
float | ratio | ratio |
2312 | function VehicleMotor:getGearRatioMultiplier() |
2313 | local multiplier = self.directionChangeUseGroup and 1 or self.currentDirection |
2314 | if self.gearGroups ~= nil then |
2315 | if self.activeGearGroupIndex == 0 then |
2316 | return 0 |
2317 | end |
2318 | |
2319 | local group = self.gearGroups[self.activeGearGroupIndex] |
2320 | if group ~= nil then |
2321 | return group.ratio * multiplier |
2322 | end |
2323 | end |
2324 | |
2325 | return multiplier |
2326 | end |
getGearToDisplay
DescriptionReturns the current gearDefinition
getGearToDisplay()Return Values
string | gear | gear |
boolean | gearsAvailable | gearsAvailable |
581 | function VehicleMotor:getGearToDisplay() |
582 | local gearName, available = "N", false |
583 | local prevGearName, nextGearName |
584 | local prevPrevGearName, nextNextGearName |
585 | local isAutomatic = false |
586 | local isGearChanging = false |
587 | |
588 | if self.backwardGears or self.forwardGears then |
589 | if self.targetGear > 0 then |
590 | local gear = self.currentGears[self.targetGear] |
591 | if gear ~= nil then |
592 | local displayDirection = self.currentDirection |
593 | local gearNameDirection = self.currentGears == self.forwardGears and self.currentDirection or 1 |
594 | |
595 | gearName = gearNameDirection == 1 and gear.name or gear.reverseName |
596 | |
597 | local prevGear = self.currentGears[self.targetGear + 1 * -displayDirection] |
598 | if prevGear ~= nil then |
599 | prevGearName = gearNameDirection == 1 and prevGear.name or prevGear.reverseName |
600 | |
601 | prevGear = self.currentGears[self.targetGear + 2 * -displayDirection] |
602 | if prevGear ~= nil then |
603 | prevPrevGearName = gearNameDirection == 1 and prevGear.name or prevGear.reverseName |
604 | end |
605 | end |
606 | |
607 | local nextGear = self.currentGears[self.targetGear + 1 * displayDirection] |
608 | if nextGear ~= nil then |
609 | nextGearName = gearNameDirection == 1 and nextGear.name or nextGear.reverseName |
610 | |
611 | nextGear = self.currentGears[self.targetGear + 2 * displayDirection] |
612 | if nextGear ~= nil then |
613 | nextNextGearName = gearNameDirection == 1 and nextGear.name or nextGear.reverseName |
614 | end |
615 | end |
616 | |
617 | if self.gear ~= self.targetGear then |
618 | isGearChanging = true |
619 | end |
620 | end |
621 | end |
622 | |
623 | available = true |
624 | else |
625 | local direction = self:getDrivingDirection() |
626 | if direction > 0 then |
627 | gearName = "D" |
628 | prevGearName = "N" |
629 | elseif direction < 0 then |
630 | gearName = "R" |
631 | nextGearName = "N" |
632 | else |
633 | nextGearName = "D" |
634 | prevGearName = "R" |
635 | end |
636 | |
637 | isAutomatic = true |
638 | end |
639 | |
640 | return gearName, available, isAutomatic, prevGearName, nextGearName, prevPrevGearName, nextNextGearName, isGearChanging |
641 | end |
getIsGearChangeAllowed
DescriptionReturns is shifting is allowed due to clutch pedal stateDefinition
getIsGearChangeAllowed()Return Values
boolean | allowed | allowed |
2231 | function VehicleMotor:getIsGearChangeAllowed() |
2232 | if self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then |
2233 | if self.gearType ~= VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT then |
2234 | return self.manualClutchValue > 0.5 |
2235 | end |
2236 | end |
2237 | |
2238 | return true |
2239 | end |
getIsGearGroupChangeAllowed
DescriptionReturns is shifting is allowed due to clutch pedal stateDefinition
getIsGearGroupChangeAllowed()Return Values
boolean | allowed | allowed |
2244 | function VehicleMotor:getIsGearGroupChangeAllowed() |
2245 | if self.gearGroups == nil then |
2246 | return false |
2247 | end |
2248 | |
2249 | if self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then |
2250 | if self.groupType ~= VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT then |
2251 | return self.manualClutchValue > 0.5 |
2252 | end |
2253 | end |
2254 | |
2255 | return true |
2256 | end |
getIsInNeutral
DescriptionDefinitiongetIsInNeutral()Code
2330 | function VehicleMotor:getIsInNeutral() |
2331 | if self.backwardGears or self.forwardGears then |
2332 | if self.gear == 0 and self.targetGear == 0 then |
2333 | return true |
2334 | end |
2335 | end |
2336 | |
2337 | return false |
2338 | end |
getLastModulatedMotorRpm
DescriptionReturns last motor rpm modulatedDefinition
getLastModulatedMotorRpm()Return Values
float | lastModulatedMotorRpm | last modulated motor rpm |
516 | function VehicleMotor:getLastModulatedMotorRpm() |
517 | local modulationIntensity = MathUtil.clamp((self.smoothedLoadPercentage - MODULATION_RPM_MIN_REF_LOAD) / (MODULATION_RPM_MAX_REF_LOAD - MODULATION_RPM_MIN_REF_LOAD), MODULATION_RPM_MIN_INTENSITY, 1) |
518 | local modulationOffset = self.lastModulationPercentage * (MODULATION_RPM_MAX_OFFSET * modulationIntensity) * self.constantRpmCharge |
519 | |
520 | -- apply only if clutch is released since with slipping clutch the rpm is already decreased |
521 | local loadChangeChargeDrop = 0 |
522 | if self:getClutchPedal() < 0.1 and self.minGearRatio > 0 then |
523 | local rpmRange = self.maxRpm - self.minRpm |
524 | local dropScale = (self.lastMotorRpm - self.minRpm) / rpmRange * 0.5 |
525 | loadChangeChargeDrop = self.loadPercentageChangeCharge * rpmRange * dropScale |
526 | else |
527 | self.loadPercentageChangeCharge = 0 |
528 | end |
529 | |
530 | return self.lastMotorRpm + modulationOffset - loadChangeChargeDrop |
531 | end |
getLastMotorRpm
DescriptionReturns last motor rpm dampedDefinition
getLastMotorRpm()Return Values
float | lastMotorRpm | last motor rpm |
509 | function VehicleMotor:getLastMotorRpm() |
510 | return self.lastMotorRpm |
511 | end |
getLastRealMotorRpm
DescriptionReturns last motor rpm realDefinition
getLastRealMotorRpm()Return Values
float | lastMotorRpm | last motor rpm |
536 | function VehicleMotor:getLastRealMotorRpm() |
537 | return self.lastRealMotorRpm |
538 | end |
getManualClutchPedal
DescriptionReturns manual clutch pedal stateDefinition
getManualClutchPedal()Return Values
float | state | state [0-1] |
569 | function VehicleMotor:getManualClutchPedal() |
570 | if self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then |
571 | return self.manualClutchValue |
572 | end |
573 | |
574 | return 0 |
575 | end |
getMaxClutchTorque
DescriptionReturns max clutch torqueDefinition
getMaxClutchTorque()Return Values
float | maxClutchTorque | max clutch torque |
399 | function VehicleMotor:getMaxClutchTorque() |
400 | return self.maxClutchTorque |
401 | end |
getMaximumBackwardSpeed
DescriptionReturns maximum backward speedDefinition
getMaximumBackwardSpeed()Return Values
float | maxBackwardSpeed | maximum backward speed |
888 | function VehicleMotor:getMaximumBackwardSpeed() |
889 | return self.maxBackwardSpeed |
890 | end |
getMaximumForwardSpeed
DescriptionReturns maximum forward speedDefinition
getMaximumForwardSpeed()Return Values
float | maxForwardSpeed | maximum forward speed |
881 | function VehicleMotor:getMaximumForwardSpeed() |
882 | return self.maxForwardSpeed |
883 | end |
getMaxRpm
DescriptionReturns max rpmDefinition
getMaxRpm()Return Values
float | maxRpm | max rpm |
490 | function VehicleMotor:getMaxRpm() |
491 | return self.maxRpm |
492 | end |
getMinMaxGearRatio
DescriptionReturns currently selected minimum and maximum gear ratio Gear ratios for driving backwards are negative. Min/max always refers to the absolute value For regular gear box transmission, the minimum and maximum gear ratios are identicalDefinition
getMinMaxGearRatio()Return Values
float | minGearRatio | minimum gear ratio |
float | maxGearRatio | maximum gear ratio |
2285 | function VehicleMotor:getMinMaxGearRatio() |
2286 | local minRatio = self.minGearRatio |
2287 | local maxRatio = self.maxGearRatio |
2288 | |
2289 | if self.minGearRatio ~= 0 or self.maxGearRatio ~= 0 then |
2290 | if self.clutchSlippingTimer == self.clutchSlippingTime then |
2291 | -- use high max gear ratio for smooth acceleration since we are not in a speed where we can fully engage the gear |
2292 | maxRatio = math.max(350, self.maxGearRatio) * MathUtil.sign(self.maxGearRatio) |
2293 | elseif self.clutchSlippingTimer > 0 then |
2294 | -- after we reached 75% of the min. needed differential speed we slowly fade into the target gear ratio |
2295 | minRatio = MathUtil.lerp(minRatio, self.clutchSlippingGearRatio, self.clutchSlippingTimer / self.clutchSlippingTime) |
2296 | maxRatio = MathUtil.lerp(maxRatio, self.clutchSlippingGearRatio, self.clutchSlippingTimer / self.clutchSlippingTime) |
2297 | end |
2298 | end |
2299 | |
2300 | return minRatio, maxRatio |
2301 | end |
getMinRpm
DescriptionReturns min rpmDefinition
getMinRpm()Return Values
float | minRpm | min rpm |
483 | function VehicleMotor:getMinRpm() |
484 | return self.minRpm |
485 | end |
getMotorAppliedTorque
DescriptionReturns the last applied torque to the motorDefinition
getMotorAppliedTorque()Return Values
float | appliedTorque | torque [kN] |
769 | function VehicleMotor:getMotorAppliedTorque() |
770 | return self.motorAppliedTorque |
771 | end |
getMotorAvailableTorque
DescriptionReturns the last total available motor torqueDefinition
getMotorAvailableTorque()Return Values
float | torque | external torque [kN] |
783 | function VehicleMotor:getMotorAvailableTorque() |
784 | return self.motorAvailableTorque |
785 | end |
getMotorExternalTorque
DescriptionReturns the last applied external torque (torque used by external power consumers like the PTO)Definition
getMotorExternalTorque()Return Values
float | externalTorque | external torque [kN] |
776 | function VehicleMotor:getMotorExternalTorque() |
777 | return self.motorExternalTorque |
778 | end |
getMotorRotationAccelerationLimit
DescriptionDefinitiongetMotorRotationAccelerationLimit()Code
2424 | function VehicleMotor:getMotorRotationAccelerationLimit() |
2425 | return self.motorRotationAccelerationLimit |
2426 | end |
getMotorRotSpeed
DescriptionReturns non clamped motor rpmDefinition
getMotorRotSpeed()Return Values
float | nonClampedMotorRpm | non clamped motor rpm |
826 | function VehicleMotor:getMotorRotSpeed() |
827 | return self.motorRotSpeed |
828 | end |
getNonClampedMotorRpm
DescriptionReturns non clamped motor rpmDefinition
getNonClampedMotorRpm()Return Values
float | nonClampedMotorRpm | non clamped motor rpm |
819 | function VehicleMotor:getNonClampedMotorRpm() |
820 | return self.motorRotSpeed * 30 / math.pi |
821 | end |
getPeakTorque
DescriptionReturns max torqueDefinition
getPeakTorque()Return Values
float | maxMotorTorque | max motor torque |
469 | function VehicleMotor:getPeakTorque() |
470 | return self.peakMotorTorque |
471 | end |
getPtoMotorRpmRatio
DescriptionReturns pto motor rpm ratioDefinition
getPtoMotorRpmRatio()Return Values
float | ptoMotorRpmRatio | pto motor rpm ratio |
805 | function VehicleMotor:getPtoMotorRpmRatio() |
806 | return self.ptoMotorRpmRatio |
807 | end |
getRequiredMotorRpmRange
DescriptionReturns the currently required motor rpm range (e.g. defined by the activated pto)Definition
getRequiredMotorRpmRange()Return Values
float | minRequiredRpm | min required rpm |
float | minRequiredRpm | max required rpm |
498 | function VehicleMotor:getRequiredMotorRpmRange() |
499 | local motorPtoRpm = math.min(PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio, self.maxRpm) |
500 | if motorPtoRpm ~= 0 then |
501 | return motorPtoRpm, self.maxRpm |
502 | end |
503 | return self.minRpm, self.maxRpm |
504 | end |
getRequiredRpmAtSpeedLimit
DescriptionReturns the rpm while driving with this gear ratioDefinition
getRequiredRpmAtSpeedLimit(float ratio)Arguments
float | ratio | gear ratio to check |
float | rpm | rpm |
1317 | function VehicleMotor:getRequiredRpmAtSpeedLimit(ratio) |
1318 | -- use vehicle speed limit (normally from working tool) and cruise control speed, not motor speed limit to avoid issues with ai since the ai is permanently controlling the motor speed limit on headlands |
1319 | -- this leads to permanent gear shifts and wrong starting gears |
1320 | local speedLimit = math.min(self.vehicle:getSpeedLimit(true), math.max(self.speedLimitAcc, self.vehicle.lastSpeedReal*3600)) |
1321 | if self.vehicle:getCruiseControlState() == Drivable.CRUISECONTROL_STATE_ACTIVE then |
1322 | speedLimit = math.min(speedLimit, self.vehicle:getCruiseControlSpeed()) |
1323 | end |
1324 | |
1325 | speedLimit = ratio > 0 and math.min(speedLimit, self.maxForwardSpeed * 3.6) or math.min(speedLimit, self.maxBackwardSpeed * 3.6) |
1326 | return speedLimit / 3.6 * 30 / math.pi * math.abs(ratio) |
1327 | end |
getRotInertia
DescriptionReturns rotation inertiaDefinition
getRotInertia()Return Values
float | rotInertia | rotation inertia |
406 | function VehicleMotor:getRotInertia() |
407 | return self.rotInertia |
408 | end |
getSmoothedClutchPedal
DescriptionReturns smoothed clutch pedal stateDefinition
getSmoothedClutchPedal()Return Values
float | state | state [0-1] |
562 | function VehicleMotor:getSmoothedClutchPedal() |
563 | return self.lastSmoothedClutchPedal |
564 | end |
getSmoothLoadPercentage
DescriptionReturns the last smoothed load percentageDefinition
getSmoothLoadPercentage()Return Values
float | load | load [0-1] |
543 | function VehicleMotor:getSmoothLoadPercentage() |
544 | local modulationIntensity = MathUtil.clamp((self.smoothedLoadPercentage - MODULATION_LOAD_MIN_REF_LOAD) / (MODULATION_LOAD_MAX_REF_LOAD - MODULATION_LOAD_MIN_REF_LOAD), MODULATION_LOAD_MIN_INTENSITY, 1) |
545 | return self.smoothedLoadPercentage - self.lastModulationPercentage * (MODULATION_LOAD_MAX_OFFSET * modulationIntensity) |
546 | end |
getSpeedLimit
DescriptionDefinitiongetSpeedLimit()Code
2392 | function VehicleMotor:getSpeedLimit() |
2393 | return self.speedLimit |
2394 | end |
getStartInGearFactor
DescriptionReturns factor which defines if the vehicle can start with the given gear ratioDefinition
getStartInGearFactor(float ratio)Arguments
float | ratio | gear ratio to check |
float | startFactor | start factor |
1333 | function VehicleMotor:getStartInGearFactor(ratio) |
1334 | -- if we cannot run the gear with at least 25% rpm with the current speed limit we skip it |
1335 | if self:getRequiredRpmAtSpeedLimit(ratio) < self.minRpm + (self.maxRpm - self.minRpm) * 0.25 then |
1336 | return math.huge |
1337 | end |
1338 | |
1339 | local slope = self.startGearValues.slope |
1340 | if ratio < 0 then |
1341 | slope = -slope |
1342 | end |
1343 | |
1344 | local slopePowerFactor = ((self.startGearValues.availablePower / 100 - 1) / 2) ^ 2 * 2 + 1 |
1345 | local slopeFactor = 1 + math.max(slope, 0) / (slopePowerFactor * 0.06981) -- 4 degrees |
1346 | return self.startGearValues.massFactor * slopeFactor / (math.abs(ratio) / 300) |
1347 | end |
getTorque
DescriptionReturns torque of the motor at the current rpm with the given accelerator pedalDefinition
getTorque(float acceleration)Arguments
float | acceleration | acceleration |
float | torque | torque |
849 | function VehicleMotor:getTorque(acceleration) |
850 | -- Note: the torque curve is undefined outside the min/max rpm range. Clamping makes the curve flat at the outside range |
851 | local torque = self:getTorqueCurveValue(MathUtil.clamp(self.motorRotSpeed * 30/math.pi, self.minRpm, self.maxRpm)) |
852 | torque = torque * math.abs(acceleration) |
853 | return torque |
854 | end |
getTorqueAndSpeedValues
DescriptionDefinitiongetTorqueAndSpeedValues()Code
867 | function VehicleMotor:getTorqueAndSpeedValues() |
868 | local rotationSpeeds = {} |
869 | local torques = {} |
870 | for _,v in ipairs(self:getTorqueCurve().keyframes) do |
871 | table.insert(rotationSpeeds, v.time*math.pi/30) |
872 | table.insert(torques, self:getTorqueCurveValue(v.time)) |
873 | end |
874 | |
875 | return torques, rotationSpeeds |
876 | end |
getTorqueCurve
DescriptionReturns torque curveDefinition
getTorqueCurve()Return Values
table | torqueCurve | torque curve |
841 | function VehicleMotor:getTorqueCurve() |
842 | return self.torqueCurve |
843 | end |
getTorqueCurveValue
DescriptionReturns torque of the motor at the given rpmDefinition
getTorqueCurveValue(float rpm)Arguments
float | rpm | rpm |
float | torque | torque |
860 | function VehicleMotor:getTorqueCurveValue(rpm) |
861 | local damage = 1 - (self.vehicle:getVehicleDamage() * VehicleMotor.DAMAGE_TORQUE_REDUCTION) |
862 | return self:getTorqueCurve():get(rpm) * damage |
863 | end |
getUseAutomaticGearShifting
DescriptionDefinitiongetUseAutomaticGearShifting()Code
2442 | function VehicleMotor:getUseAutomaticGearShifting() |
2443 | if self.gearShiftMode == VehicleMotor.SHIFT_MODE_AUTOMATIC then |
2444 | return true |
2445 | end |
2446 | |
2447 | if not self.manualShiftGears then |
2448 | return true |
2449 | end |
2450 | |
2451 | return false |
2452 | end |
getUseAutomaticGroupShifting
DescriptionDefinitiongetUseAutomaticGroupShifting()Code
2456 | function VehicleMotor:getUseAutomaticGroupShifting() |
2457 | if self.gearShiftMode == VehicleMotor.SHIFT_MODE_AUTOMATIC then |
2458 | return true |
2459 | end |
2460 | |
2461 | if not self.manualShiftGroups then |
2462 | return true |
2463 | end |
2464 | |
2465 | return false |
2466 | end |
new
DescriptionCreating new motorDefinition
new(Integer minRpm, Integer maxRpm, float maxForwardSpeed, float maxBackwardSpeed, table torqueCurve, float brakeForce, float forwardGears, float backwardGears, float minForwardGearRatio, float maxForwardGearRatio, float minBackwardGearRatio, float maxBackwardGearRatio, Integer ptoMotorRpmRatio)Arguments
Integer | minRpm | min rpm |
Integer | maxRpm | max rpm |
float | maxForwardSpeed | max forward speed |
float | maxBackwardSpeed | max backward speed |
table | torqueCurve | torque curve (AnimCurve) |
float | brakeForce | brake force |
float | forwardGears | list of gear ratios to use when driving forwards (in decreasing order) |
float | backwardGears | list of gear ratios to use when driving backwards (in decreasing order) |
float | minForwardGearRatio | min forward gear ratio |
float | maxForwardGearRatio | max forward gear ratio |
float | minBackwardGearRatio | min backward gear ratio |
float | maxBackwardGearRatio | max backward gear ratio |
Integer | ptoMotorRpmRatio | pto motor rpm ratio |
table | motorInstance | motor instance |
64 | function VehicleMotor.new(vehicle, minRpm, maxRpm, maxForwardSpeed, maxBackwardSpeed, torqueCurve, brakeForce, forwardGears, backwardGears, minForwardGearRatio, maxForwardGearRatio, minBackwardGearRatio, maxBackwardGearRatio, ptoMotorRpmRatio, minSpeed) |
65 | |
66 | local self = {} |
67 | setmetatable(self, VehicleMotor_mt) |
68 | |
69 | self.vehicle = vehicle |
70 | self.minRpm = minRpm |
71 | self.maxRpm = maxRpm |
72 | self.minSpeed = minSpeed |
73 | self.maxForwardSpeed = maxForwardSpeed -- speed in m/s |
74 | self.maxBackwardSpeed = maxBackwardSpeed |
75 | |
76 | self.maxClutchTorque = 5 -- amount of torque that can be transferred from motor to clutch/wheels [t m s^-2] |
77 | |
78 | self.torqueCurve = torqueCurve |
79 | self.brakeForce = brakeForce |
80 | |
81 | self.lastAcceleratorPedal = 0 |
82 | self.idleGearChangeTimer = 0 -- if this timer reaches 0 the automatic gear change while not moving is allowed |
83 | self.doSecondBestGearSelection = 0 |
84 | |
85 | self.gear = 0 |
86 | self.bestGearSelected = 0 |
87 | self.minGearRatio = 0 |
88 | self.maxGearRatio = 0 |
89 | self.allowGearChangeTimer = 0 |
90 | self.allowGearChangeDirection = 0 |
91 | |
92 | self.forwardGears = forwardGears |
93 | self.backwardGears = backwardGears |
94 | self.currentGears = self.forwardGears |
95 | self.minForwardGearRatio = minForwardGearRatio |
96 | self.maxForwardGearRatio = maxForwardGearRatio |
97 | self.minBackwardGearRatio = minBackwardGearRatio |
98 | self.maxBackwardGearRatio = maxBackwardGearRatio |
99 | |
100 | self.maxClutchSpeedDifference = 0 |
101 | self.defaultForwardGear = 1 |
102 | if self.forwardGears ~= nil then |
103 | for i=1, #self.forwardGears do |
104 | self.maxClutchSpeedDifference = math.max(self.maxClutchSpeedDifference, self.minRpm / self.forwardGears[i].ratio * math.pi / 30) |
105 | if self.forwardGears[i].default then |
106 | self.defaultForwardGear = i |
107 | end |
108 | end |
109 | end |
110 | |
111 | self.defaultBackwardGear = 1 |
112 | if self.backwardGears ~= nil then |
113 | for i=1, #self.backwardGears do |
114 | self.maxClutchSpeedDifference = math.max(self.maxClutchSpeedDifference, self.minRpm / self.backwardGears[i].ratio * math.pi / 30) |
115 | if self.backwardGears[i].default then |
116 | self.defaultBackwardGear = i |
117 | end |
118 | end |
119 | end |
120 | |
121 | self.gearType = VehicleMotor.TRANSMISSION_TYPE.DEFAULT |
122 | self.groupType = VehicleMotor.TRANSMISSION_TYPE.DEFAULT |
123 | |
124 | self.manualTargetGear = nil |
125 | self.targetGear = 0 |
126 | self.previousGear = 0 |
127 | self.gearChangeTimer = -1 |
128 | self.gearChangeTime = 250 |
129 | self.gearChangeTimeOrig = self.gearChangeTime |
130 | self.autoGearChangeTimer = -1 |
131 | self.autoGearChangeTime = 1000 |
132 | self.manualClutchValue = 0 |
133 | self.stallTimer = 0 |
134 | self.lastGearChangeTime = 0 |
135 | self.gearChangeTimeAutoReductionTime = 500 |
136 | self.gearChangeTimeAutoReductionTimer = 0 |
137 | |
138 | self.lastManualShifterActive = false |
139 | |
140 | self.clutchSlippingTime = 1000 |
141 | self.clutchSlippingTimer = 0 |
142 | self.clutchSlippingGearRatio = 0 |
143 | |
144 | self.groupChangeTime = 500 |
145 | self.groupChangeTimer = 0 |
146 | self.gearGroupUpShiftTime = 3000 |
147 | self.gearGroupUpShiftTimer = 0 |
148 | |
149 | self.currentDirection = 1 -- current used gear direction |
150 | self.directionChangeTimer = 0 |
151 | self.directionChangeTime = 500 |
152 | self.directionChangeUseGear = false -- use a backward gear for direction change buttons |
153 | self.directionChangeGearIndex = 1 -- backward gear to activate if direction changes |
154 | self.directionLastGear = -1 -- last forward gear, activated if direction is changed again |
155 | self.directionChangeUseGroup = false -- use a group for direction change buttons |
156 | self.directionChangeGroupIndex = 1 -- group to activate if direction changes |
157 | self.directionLastGroup = -1 -- last selected group, acitvated if direction changes again |
158 | self.directionChangeUseInverse = true -- if true the forward gears are just inverted for driving backwards |
159 | |
160 | self.gearChangedIsLocked = false |
161 | self.gearGroupChangedIsLocked = false |
162 | |
163 | self.startGearValues = { |
164 | slope = 0, |
165 | mass = 0, |
166 | lastMass = 0, |
167 | maxForce = 0, |
168 | massDirectionDifferenceXZ = 0, |
169 | massDirectionDifferenceY = 0, |
170 | massDirectionFactor = 0, |
171 | availablePower = 0, |
172 | massFactor = 0 |
173 | } |
174 | |
175 | self.startGearThreshold = VehicleMotor.GEAR_START_THRESHOLD |
176 | |
177 | self.lastSmoothedClutchPedal = 0 |
178 | |
179 | self.lastRealMotorRpm = 0 |
180 | self.lastMotorRpm = 0 |
181 | |
182 | self.lastModulationPercentage = 0 |
183 | self.lastModulationTimer = 0 |
184 | |
185 | self.rawLoadPercentage = 0 |
186 | self.rawLoadPercentageBuffer = 0 |
187 | self.rawLoadPercentageBufferIndex = 0 |
188 | self.smoothedLoadPercentage = 0 |
189 | self.loadPercentageChangeCharge = 0 |
190 | |
191 | self.accelerationLimitLoadScale = 1 |
192 | self.accelerationLimitLoadScaleTimer = 0 |
193 | self.accelerationLimitLoadScaleDelay = 2000 -- after running this time at max acceleration we decrease the motor load slowly again |
194 | |
195 | self.constantRpmCharge = 0 |
196 | self.constantAccelerationCharge = 0 |
197 | |
198 | self.lastTurboScale = 0 |
199 | self.blowOffValveState = 0 |
200 | |
201 | self.overSpeedTimer = 0 |
202 | |
203 | self.rpmLimit = math.huge |
204 | self.speedLimit = math.huge -- Speed limit in km/h |
205 | self.speedLimitAcc = math.huge |
206 | |
207 | self.accelerationLimit = 2 -- m s^-2 |
208 | |
209 | self.motorRotationAccelerationLimit = (maxRpm - minRpm)*math.pi/30 / 2 -- rad s^-2 default accelerate from min rpm to max rpm in 2 sec |
210 | |
211 | self.equalizedMotorRpm = 0 |
212 | |
213 | self.requiredMotorPower = 0 |
214 | |
215 | if self.maxForwardSpeed == nil then |
216 | self.maxForwardSpeed = self:calculatePhysicalMaximumForwardSpeed() |
217 | end |
218 | if self.maxBackwardSpeed == nil then |
219 | self.maxBackwardSpeed = self:calculatePhysicalMaximumBackwardSpeed() |
220 | end |
221 | |
222 | -- saving the original values to be able to inverse them |
223 | self.maxForwardSpeedOrigin = self.maxForwardSpeed |
224 | self.maxBackwardSpeedOrigin = self.maxBackwardSpeed |
225 | self.minForwardGearRatioOrigin = self.minForwardGearRatio |
226 | self.maxForwardGearRatioOrigin = self.maxForwardGearRatio |
227 | self.minBackwardGearRatioOrigin = self.minBackwardGearRatio |
228 | self.maxBackwardGearRatioOrigin = self.maxBackwardGearRatio |
229 | |
230 | self.peakMotorTorque = self.torqueCurve:getMaximum() |
231 | |
232 | -- Calculate peak power. Assume we have a linear interpolation on the torque values |
233 | -- For each segment, find the maximum power (D[torque(x, i) * x] == 0) and take the maximum segment |
234 | -- D[ ((x-x0) / (x1-x0) (y1-y0) + y0) x] == 0 |
235 | -- -> (x1 y0 - x0 y1) / (2 (y0 - y1)) if y0 != y1 |
236 | self.peakMotorPower = 0 |
237 | self.peakMotorPowerRotSpeed = 0 |
238 | local numKeyFrames = #self.torqueCurve.keyframes |
239 | if numKeyFrames >= 2 then |
240 | for i=2,numKeyFrames do |
241 | local v0 = self.torqueCurve.keyframes[i-1] |
242 | local v1 = self.torqueCurve.keyframes[i] |
243 | local torque0 = self.torqueCurve:getFromKeyframes(v0, v0, i-1, i-1, 0) |
244 | local torque1 = self.torqueCurve:getFromKeyframes(v1, v1, i, i, 0) |
245 | local rpm, torque |
246 | if math.abs(torque0 - torque1) > 0.0001 then |
247 | rpm = (v1.time * torque0 - v0.time * torque1) / (2.0 * (torque0 - torque1)) |
248 | rpm = math.min(math.max(rpm, v0.time), v1.time) |
249 | torque = self.torqueCurve:getFromKeyframes(v0, v1, i-1, i, (v1.time - rpm) / (v1.time - v0.time)) |
250 | else |
251 | rpm = v0.time |
252 | torque = torque0 |
253 | end |
254 | local power = torque * rpm |
255 | if power > self.peakMotorPower then |
256 | self.peakMotorPower = power |
257 | self.peakMotorPowerRotSpeed = rpm |
258 | end |
259 | end |
260 | -- Convert from rpm to rad/s |
261 | self.peakMotorPower = self.peakMotorPower * math.pi/30 |
262 | self.peakMotorPowerRotSpeed = self.peakMotorPowerRotSpeed * math.pi/30 |
263 | else |
264 | local v = self.torqueCurve.keyframes[1] |
265 | local rotSpeed = v.time*math.pi/30 |
266 | local torque = self.torqueCurve:getFromKeyframes(v, v, 1, 1, 0) |
267 | self.peakMotorPower = rotSpeed*torque |
268 | self.peakMotorPowerRotSpeed = rotSpeed |
269 | end |
270 | |
271 | self.ptoMotorRpmRatio = ptoMotorRpmRatio |
272 | |
273 | self.rotInertia = self.peakMotorTorque / 600 -- Rotational inertia of the motor, mostly defined by the flywheel [t m^2] |
274 | self.dampingRateFullThrottle = VehicleMotor.DEFAULT_DAMPING_RATE_FULL_THROTTLE -- Damping rate of the motor if the acceleration pedal is 1 [t m^2 s^-1] |
275 | self.dampingRateZeroThrottleClutchEngaged = VehicleMotor.DEFAULT_DAMPING_RATE_ZERO_THROTTLE_CLUTCH_EN -- Damping rate of the motor if the acceleration pedal is 0 and the clutch is engaged [t m^2 s^-1] |
276 | self.dampingRateZeroThrottleClutchDisengaged = VehicleMotor.DEFAULT_DAMPING_RATE_ZERO_THROTTLE_CLUTCH_DIS -- Damping rate of the motor if the acceleration pedal is 0 and the clutch is disengaged [t m^2 s^-1] |
277 | |
278 | -- Motor properties as read from the physics engine |
279 | self.gearRatio = 0 |
280 | self.motorRotSpeed = 0 -- motor rotation speed [rad/s] |
281 | self.motorRotSpeedClutchEngaged = 0 -- additional rotation speed when clutch is engaged |
282 | self.motorRotAcceleration = 0 -- motor rotation acceleration [rad/s^2] |
283 | self.motorRotAccelerationSmoothed = 0 -- motor rotation acceleration smoothed [rad/s^2] |
284 | |
285 | self.motorAvailableTorque, self.lastMotorAvailableTorque = 0, 0 -- torque that was available to the physics simulation [kN == t m/s^2] |
286 | self.motorAppliedTorque, self.lastMotorAppliedTorque = 0, 0 -- torque that was applied (<= available), can be smaller when acceleration/speed is limited [kN == t m/s^2] |
287 | self.motorExternalTorque, self.lastMotorExternalTorque = 0, 0 -- torque that was removed from the motor and was not applied to the wheels (e.g. PTO) [kN == t m/s^2] |
288 | |
289 | -- externalTorqueVirtualMultiplicator: is used to have virtually more motor load due to external torque (pto) but still reduce the motor by the same external torque |
290 | -- like this we can increase the motor load without cutting the motor to hard so we cannot accelerate anymore |
291 | self.externalTorqueVirtualMultiplicator = 1 |
292 | |
293 | self.differentialRotSpeed = 0 -- rotation speed of the main differential [rad/s] |
294 | self.differentialRotAcceleration = 0 -- rotation accleration of the main differential [rad/s^2] |
295 | self.differentialRotAccelerationSmoothed = 0 -- smoothed rotation accleration of the main differential [rad/s^2] |
296 | |
297 | self.differentialRotAccelerationIndex = 1 |
298 | self.differentialRotAccelerationSamples = {} |
299 | for _=1, 10 do |
300 | table.insert(self.differentialRotAccelerationSamples, 0) |
301 | end |
302 | |
303 | self.lastDifference = 0 |
304 | |
305 | self.directionChangeMode = g_gameSettings:getValue(GameSettings.SETTING.DIRECTION_CHANGE_MODE) |
306 | self.gearShiftMode = g_gameSettings:getValue(GameSettings.SETTING.GEAR_SHIFT_MODE) |
307 | |
308 | return self |
309 | end |
onManualClutchChanged
DescriptionCalled when manual clutch value changesDefinition
onManualClutchChanged(float value)Arguments
float | value | value |
2224 | function VehicleMotor:onManualClutchChanged(clutchValue) |
2225 | self.manualClutchValue = clutchValue |
2226 | end |
postLoad
DescriptionPost load motorDefinition
postLoad(table savegame)Arguments
table | savegame | savegame information |
314 | function VehicleMotor:postLoad(savegame) |
315 | if self.gearGroups ~= nil then |
316 | SpecializationUtil.raiseEvent(self.vehicle, "onGearGroupChanged", self.activeGearGroupIndex, 0) |
317 | end |
318 | end |
readGearDataFromStream
DescriptionReads current gear data from streamDefinition
readGearDataFromStream()Code
681 | function VehicleMotor:readGearDataFromStream(streamId) |
682 | self.currentDirection = streamReadUIntN(streamId, 2) - 1 |
683 | |
684 | if streamReadBool(streamId) then |
685 | local gear = streamReadUIntN(streamId, 6) |
686 | local changingGear = streamReadBool(streamId) |
687 | |
688 | if streamReadBool(streamId) then |
689 | self.currentGears = self.forwardGears |
690 | else |
691 | self.currentGears = self.backwardGears |
692 | end |
693 | |
694 | local activeGearGroupIndex |
695 | if self.gearGroups ~= nil then |
696 | activeGearGroupIndex = streamReadUIntN(streamId, 5) |
697 | end |
698 | |
699 | if gear ~= self.gear then |
700 | if changingGear and self.gear ~= 0 then |
701 | self.lastGearChangeTime = g_time |
702 | end |
703 | |
704 | self.gear = changingGear and 0 or gear |
705 | self.targetGear = gear |
706 | |
707 | local directionMultiplier = self.directionChangeUseGear and self.currentDirection or 1 |
708 | SpecializationUtil.raiseEvent(self.vehicle, "onGearChanged", self.gear * directionMultiplier, self.targetGear * directionMultiplier, 0) |
709 | end |
710 | |
711 | if activeGearGroupIndex ~= self.activeGearGroupIndex then |
712 | self.activeGearGroupIndex = activeGearGroupIndex |
713 | SpecializationUtil.raiseEvent(self.vehicle, "onGearGroupChanged", self.activeGearGroupIndex, self.groupType == VehicleMotor.TRANSMISSION_TYPE.DEFAULT and self.groupChangeTime or 0) |
714 | end |
715 | end |
716 | end |
selectGear
DescriptionDefinitionselectGear()Code
2022 | function VehicleMotor:selectGear(gearIndex, activation) |
2023 | if activation then |
2024 | if self.gear ~= gearIndex then |
2025 | if self:getIsGearChangeAllowed() then |
2026 | if self.currentGears[gearIndex] ~= nil then |
2027 | self:setGear(gearIndex, true) |
2028 | self.lastManualShifterActive = true |
2029 | end |
2030 | else |
2031 | SpecializationUtil.raiseEvent(self.vehicle, "onClutchCreaking", false, false, gearIndex) |
2032 | end |
2033 | end |
2034 | else |
2035 | -- go into neutral state if any gear input action is released |
2036 | self:setGear(0, false) |
2037 | self.lastManualShifterActive = true |
2038 | end |
2039 | end |
selectGroup
DescriptionDefinitionselectGroup()Code
2092 | function VehicleMotor:selectGroup(groupIndex, activation) |
2093 | if activation then |
2094 | if self:getIsGearGroupChangeAllowed() then |
2095 | if self.gearGroups ~= nil then |
2096 | if self.gearGroups[groupIndex] ~= nil then |
2097 | self:setGearGroup(groupIndex, true) |
2098 | end |
2099 | end |
2100 | else |
2101 | if self.activeGearGroupIndex ~= groupIndex then |
2102 | SpecializationUtil.raiseEvent(self.vehicle, "onClutchCreaking", false, true, nil, groupIndex) |
2103 | end |
2104 | end |
2105 | else |
2106 | -- go into neutral state if any group input action is released |
2107 | self:setGearGroup(0, false) |
2108 | end |
2109 | end |
setAccelerationLimit
DescriptionDefinitionsetAccelerationLimit()Code
2398 | function VehicleMotor:setAccelerationLimit(accelerationLimit) |
2399 | self.accelerationLimit = accelerationLimit |
2400 | end |
setAutoGearChangeTime
DescriptionSets the time that needs to pass since the last gear change until an automatic gear change is allowedDefinition
setAutoGearChangeTime(float autoGearChangeTime)Arguments
float | autoGearChangeTime | automatic gear change time [ms] |
461 | function VehicleMotor:setAutoGearChangeTime(autoGearChangeTime) |
462 | self.autoGearChangeTime = autoGearChangeTime |
463 | self.autoGearChangeTimer = math.min(self.autoGearChangeTimer, autoGearChangeTime) |
464 | end |
setDampingRateScale
DescriptionScales all damping rate values with this factorDefinition
setDampingRateScale(float dampingRateScale)Arguments
float | dampingRateScale | scale of damping rate [0-1] |
441 | function VehicleMotor:setDampingRateScale(dampingRateScale) |
442 | self.dampingRateFullThrottle = VehicleMotor.DEFAULT_DAMPING_RATE_FULL_THROTTLE * dampingRateScale |
443 | self.dampingRateZeroThrottleClutchEngaged = VehicleMotor.DEFAULT_DAMPING_RATE_ZERO_THROTTLE_CLUTCH_EN * dampingRateScale |
444 | self.dampingRateZeroThrottleClutchDisengaged = VehicleMotor.DEFAULT_DAMPING_RATE_ZERO_THROTTLE_CLUTCH_DIS * dampingRateScale |
445 | end |
setDirectionChange
DescriptionSet power shift stagesDefinition
setDirectionChange(table gearGroups)Arguments
table | gearGroups | gearGroups |
361 | function VehicleMotor:setDirectionChange(directionChangeUseGear, directionChangeGearIndex, directionChangeUseGroup, directionChangeGroupIndex, directionChangeTime) |
362 | self.directionChangeUseGear = directionChangeUseGear |
363 | self.directionChangeGearIndex = directionChangeGearIndex |
364 | self.directionChangeUseGroup = directionChangeUseGroup |
365 | self.directionChangeGroupIndex = directionChangeGroupIndex |
366 | self.directionChangeTime = directionChangeTime |
367 | self.directionChangeUseInverse = not directionChangeUseGear and not directionChangeUseGroup |
368 | end |
setDirectionChangeMode
DescriptionDefinitionsetDirectionChangeMode()Code
2430 | function VehicleMotor:setDirectionChangeMode(directionChangeMode) |
2431 | self.directionChangeMode = directionChangeMode |
2432 | end |
setEqualizedMotorRpm
DescriptionSets equalized motor rpmDefinition
setEqualizedMotorRpm(float equalizedMotorRpm)Arguments
float | equalizedMotorRpm | equalized motor rpm |
797 | function VehicleMotor:setEqualizedMotorRpm(rpm) |
798 | self.equalizedMotorRpm = rpm |
799 | self:setLastRpm(rpm) |
800 | end |
setExternalTorqueVirtualMultiplicator
DescriptionSets the virtual external torque multiplicatorDefinition
setExternalTorqueVirtualMultiplicator()Return Values
float | externalTorqueVirtualMultiplicator | virtual external torque multiplicator |
812 | function VehicleMotor:setExternalTorqueVirtualMultiplicator(externalTorqueVirtualMultiplicator) |
813 | self.externalTorqueVirtualMultiplicator = externalTorqueVirtualMultiplicator or 1 |
814 | end |
setGear
DescriptionDefinitionsetGear()Code
2043 | function VehicleMotor:setGear(gearIndex, isLocked) |
2044 | if gearIndex ~= self.targetGear then |
2045 | if self.gearChangeTime == 0 and self.targetGear > gearIndex then |
2046 | self.loadPercentageChangeCharge = 1 |
2047 | end |
2048 | |
2049 | if self.gearShiftMode ~= VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then |
2050 | self.targetGear = gearIndex |
2051 | self.previousGear = self.gear |
2052 | self.gear = 0 |
2053 | self.minGearRatio = 0 |
2054 | self.maxGearRatio = 0 |
2055 | self.autoGearChangeTimer = self.autoGearChangeTime |
2056 | self.gearChangeTimer = self.gearChangeTime |
2057 | else |
2058 | self.targetGear = gearIndex |
2059 | self.previousGear = self.gear |
2060 | self.gear = gearIndex |
2061 | end |
2062 | |
2063 | local directionMultiplier = self.directionChangeUseGear and self.currentDirection or 1 |
2064 | SpecializationUtil.raiseEvent(self.vehicle, "onGearChanged", self.gear * directionMultiplier, self.targetGear * directionMultiplier, self.gearChangeTime) |
2065 | end |
2066 | end |
setGearChangeTime
DescriptionSets the time it takes change gearsDefinition
setGearChangeTime(float gearChangeTime)Arguments
float | gearChangeTime | gear change time [ms] |
450 | function VehicleMotor:setGearChangeTime(gearChangeTime) |
451 | self.gearChangeTime = gearChangeTime |
452 | self.gearChangeTimeOrig = gearChangeTime |
453 | self.gearChangeTimer = math.min(self.gearChangeTimer, gearChangeTime) |
454 | |
455 | self.gearType = gearChangeTime == 0 and VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT or VehicleMotor.TRANSMISSION_TYPE.DEFAULT |
456 | end |
setGearGroup
DescriptionDefinitionsetGearGroup()Code
2113 | function VehicleMotor:setGearGroup(groupIndex, isLocked) |
2114 | local lastActiveGearGroupIndex = self.activeGearGroupIndex |
2115 | self.activeGearGroupIndex = groupIndex |
2116 | self.gearGroupChangedIsLocked = isLocked |
2117 | |
2118 | if self.activeGearGroupIndex ~= lastActiveGearGroupIndex then |
2119 | if self.groupType == VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT and self.activeGearGroupIndex > lastActiveGearGroupIndex then |
2120 | self.loadPercentageChangeCharge = 1 |
2121 | end |
2122 | |
2123 | if self.directionChangeUseGroup then |
2124 | self.currentDirection = self.activeGearGroupIndex == self.directionChangeGroupIndex and -1 or 1 |
2125 | end |
2126 | |
2127 | if self.gearShiftMode ~= VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then |
2128 | if self.groupType == VehicleMotor.TRANSMISSION_TYPE.DEFAULT then |
2129 | self.groupChangeTimer = self.groupChangeTime |
2130 | self.gear = 0 |
2131 | self.minGearRatio = 0 |
2132 | self.maxGearRatio = 0 |
2133 | elseif self.groupType == VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT then |
2134 | self:applyTargetGear() |
2135 | end |
2136 | end |
2137 | |
2138 | SpecializationUtil.raiseEvent(self.vehicle, "onGearGroupChanged", self.activeGearGroupIndex, self.groupType == VehicleMotor.TRANSMISSION_TYPE.DEFAULT and self.groupChangeTime or 0) |
2139 | end |
2140 | end |
setGearGroups
DescriptionSet power shift stagesDefinition
setGearGroups(table gearGroups)Arguments
table | gearGroups | gearGroups |
329 | function VehicleMotor:setGearGroups(gearGroups, groupType, groupChangeTime) |
330 | self.gearGroups = gearGroups |
331 | self.groupType = VehicleMotor.TRANSMISSION_TYPE[groupType:upper()] or VehicleMotor.TRANSMISSION_TYPE.DEFAULT |
332 | self.groupChangeTime = groupChangeTime |
333 | |
334 | if gearGroups ~= nil then |
335 | self.numGearGroups = #gearGroups |
336 | self.defaultGearGroup = 1 |
337 | |
338 | -- use first forward gear group |
339 | for i=1, self.numGearGroups do |
340 | if self.gearGroups[i].ratio > 0 then |
341 | self.defaultGearGroup = i |
342 | break |
343 | end |
344 | end |
345 | |
346 | -- use group with default attribute set |
347 | for i=1, self.numGearGroups do |
348 | if self.gearGroups[i].isDefault then |
349 | self.defaultGearGroup = i |
350 | break |
351 | end |
352 | end |
353 | |
354 | self.activeGearGroupIndex = self.defaultGearGroup |
355 | end |
356 | end |
setGearShiftMode
DescriptionDefinitionsetGearShiftMode()Code
2436 | function VehicleMotor:setGearShiftMode(gearShiftMode) |
2437 | self.gearShiftMode = gearShiftMode |
2438 | end |
setLastRpm
DescriptionSets last motor rpmDefinition
setLastRpm(float lastRpm)Arguments
float | lastRpm | new last motor rpm |
738 | function VehicleMotor:setLastRpm(lastRpm) |
739 | local oldMotorRpm = self.lastMotorRpm |
740 | |
741 | self.lastRealMotorRpm = lastRpm |
742 | |
743 | local interpolationSpeed = 0.05 |
744 | |
745 | -- fast rpm drop for power shift transmissions to have a clear audible drop |
746 | if self.gearType == VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT and (g_time - self.lastGearChangeTime) < 200 then |
747 | interpolationSpeed = 0.2 |
748 | end |
749 | |
750 | self.lastMotorRpm = self.lastMotorRpm * (1 - interpolationSpeed) + self.lastRealMotorRpm * interpolationSpeed |
751 | |
752 | -- calculate turbo speed scale depending on rpm and motor load |
753 | local rpmPercentage = (self.lastMotorRpm - math.max(self.lastPtoRpm or self.minRpm, self.minRpm)) / (self.maxRpm - self.minRpm) |
754 | local targetTurboRpm = rpmPercentage * self:getSmoothLoadPercentage() |
755 | self.lastTurboScale = self.lastTurboScale * 0.95 + targetTurboRpm * 0.05 |
756 | |
757 | if self.lastAcceleratorPedal == 0 or (self.minGearRatio == 0 and self.autoGearChangeTime > 0) then |
758 | self.blowOffValveState = self.lastTurboScale |
759 | else |
760 | self.blowOffValveState = 0 |
761 | end |
762 | |
763 | self.constantRpmCharge = 1 - math.min(math.abs(self.lastMotorRpm - oldMotorRpm) * 0.15, 1) |
764 | end |
setLowBrakeForce
DescriptionSet low brake forceDefinition
setLowBrakeForce(float lowBrakeForceScale, float lowBrakeForceSpeedLimit)Arguments
float | lowBrakeForceScale | low brake force scale |
float | lowBrakeForceSpeedLimit | low brake force speed limit |
391 | function VehicleMotor:setLowBrakeForce(lowBrakeForceScale, lowBrakeForceSpeedLimit) |
392 | self.lowBrakeForceScale = lowBrakeForceScale |
393 | self.lowBrakeForceSpeedLimit = lowBrakeForceSpeedLimit |
394 | end |
setManualShift
DescriptionSets the manual shift settingsDefinition
setManualShift(boolean gears, boolean groups)Arguments
boolean | gears | gears can be shifted manually |
boolean | groups | groups can be shifted manually |
374 | function VehicleMotor:setManualShift(manualShiftGears, manualShiftGroups) |
375 | self.manualShiftGears = manualShiftGears |
376 | self.manualShiftGroups = manualShiftGroups |
377 | end |
setMotorRotationAccelerationLimit
DescriptionDefinitionsetMotorRotationAccelerationLimit()Code
2418 | function VehicleMotor:setMotorRotationAccelerationLimit(limit) |
2419 | self.motorRotationAccelerationLimit = limit |
2420 | end |
setRotInertia
DescriptionSets rotation inertiaDefinition
setRotInertia(float rotInertia)Arguments
float | rotInertia | rotation inertia |
413 | function VehicleMotor:setRotInertia(rotInertia) |
414 | self.rotInertia = rotInertia |
415 | end |
setRpmLimit
DescriptionSets rpm limitDefinition
setRpmLimit(float limit)Arguments
float | limit | new limit |
2411 | function VehicleMotor:setRpmLimit(rpmLimit) |
2412 | self.rpmLimit = rpmLimit |
2413 | end |
setSpeedLimit
DescriptionSets speed limitDefinition
setSpeedLimit(float limit)Arguments
float | limit | new limit |
2386 | function VehicleMotor:setSpeedLimit(limit) |
2387 | self.speedLimit = math.max(limit, self.minSpeed) |
2388 | end |
setStartGearThreshold
DescriptionSets custom start gear thresholdDefinition
setStartGearThreshold(float lowBrakeForceScale, float lowBrakeForceSpeedLimit)Arguments
float | lowBrakeForceScale | low brake force scale |
float | lowBrakeForceSpeedLimit | low brake force speed limit |
383 | function VehicleMotor:setStartGearThreshold(startGearThreshold) |
384 | self.startGearThreshold = startGearThreshold |
385 | end |
setTransmissionDirection
DescriptionSwitches the gear ratios by the given directionDefinition
setTransmissionDirection(integer direction)Arguments
integer | direction | direction |
2261 | function VehicleMotor:setTransmissionDirection(direction) |
2262 | if direction > 0 then |
2263 | self.maxForwardSpeed = self.maxForwardSpeedOrigin |
2264 | self.maxBackwardSpeed = self.maxBackwardSpeedOrigin |
2265 | self.minForwardGearRatio = self.minForwardGearRatioOrigin |
2266 | self.maxForwardGearRatio = self.maxForwardGearRatioOrigin |
2267 | self.minBackwardGearRatio = self.minBackwardGearRatioOrigin |
2268 | self.maxBackwardGearRatio = self.maxBackwardGearRatioOrigin |
2269 | else |
2270 | self.maxForwardSpeed = self.maxBackwardSpeedOrigin |
2271 | self.maxBackwardSpeed = self.maxForwardSpeedOrigin |
2272 | self.minForwardGearRatio = self.minBackwardGearRatioOrigin |
2273 | self.maxForwardGearRatio = self.maxBackwardGearRatioOrigin |
2274 | self.minBackwardGearRatio = self.minForwardGearRatioOrigin |
2275 | self.maxBackwardGearRatio = self.maxForwardGearRatioOrigin |
2276 | end |
2277 | end |
shiftGear
DescriptionDefinitionshiftGear()Code
1978 | function VehicleMotor:shiftGear(up) |
1979 | if not self.gearChangedIsLocked then |
1980 | if self:getIsGearChangeAllowed() then |
1981 | local newGear |
1982 | if up then |
1983 | newGear = self.targetGear + 1 * self.currentDirection |
1984 | else |
1985 | newGear = self.targetGear - 1 * self.currentDirection |
1986 | end |
1987 | |
1988 | if self.currentDirection > 0 or self.backwardGears == nil then |
1989 | if newGear > #self.forwardGears then |
1990 | newGear = #self.forwardGears |
1991 | end |
1992 | elseif self.currentDirection < 0 or self.backwardGears ~= nil then |
1993 | if newGear > #self.backwardGears then |
1994 | newGear = #self.backwardGears |
1995 | end |
1996 | end |
1997 | |
1998 | if newGear ~= self.targetGear then |
1999 | if self.currentDirection > 0 then |
2000 | if newGear < 0 then |
2001 | self:changeDirection(-1) |
2002 | newGear = 1 |
2003 | end |
2004 | else |
2005 | if newGear < 0 then |
2006 | self:changeDirection(1) |
2007 | newGear = 1 |
2008 | end |
2009 | end |
2010 | |
2011 | self:setGear(newGear) |
2012 | self.lastManualShifterActive = false |
2013 | end |
2014 | else |
2015 | SpecializationUtil.raiseEvent(self.vehicle, "onClutchCreaking", true, false) |
2016 | end |
2017 | end |
2018 | end |
shiftGroup
DescriptionDefinitionshiftGroup()Code
2071 | function VehicleMotor:shiftGroup(up) |
2072 | if not self.gearGroupChangedIsLocked then |
2073 | if self:getIsGearGroupChangeAllowed() then |
2074 | if self.gearGroups ~= nil then |
2075 | local newGearGroupIndex |
2076 | if up then |
2077 | newGearGroupIndex = self.activeGearGroupIndex + 1 |
2078 | else |
2079 | newGearGroupIndex = self.activeGearGroupIndex - 1 |
2080 | end |
2081 | |
2082 | self:setGearGroup(MathUtil.clamp(newGearGroupIndex, 1, self.numGearGroups)) |
2083 | end |
2084 | else |
2085 | SpecializationUtil.raiseEvent(self.vehicle, "onClutchCreaking", true, true) |
2086 | end |
2087 | end |
2088 | end |
update
DescriptionUpdate the state of the motor (sync with physics simulation)Definition
update(float dt)Arguments
float | dt | time since last call in ms |
932 | function VehicleMotor:update(dt) |
933 | local vehicle = self.vehicle |
934 | if next(vehicle.spec_motorized.differentials) ~= nil and vehicle.spec_motorized.motorizedNode ~= nil then |
935 | local lastMotorRotSpeed = self.motorRotSpeed |
936 | local lastDiffRotSpeed = self.differentialRotSpeed |
937 | self.motorRotSpeed, self.differentialRotSpeed, self.gearRatio = getMotorRotationSpeed(vehicle.spec_motorized.motorizedNode) |
938 | |
939 | |
940 | if self.gearShiftMode ~= VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then |
941 | -- dynamically adjust the max gear ratio while starting in a gear and have not reached the min. differential speed |
942 | -- this simulates clutch slipping and allows a smooth acceleration |
943 | if (self.backwardGears or self.forwardGears) and self.gearRatio ~= 0 and self.maxGearRatio ~= 0 then |
944 | if self.lastAcceleratorPedal ~= 0 then |
945 | local minDifferentialSpeed = self.minRpm / math.abs(self.maxGearRatio) * math.pi / 30 |
946 | if math.abs(self.differentialRotSpeed) < minDifferentialSpeed * 0.75 then |
947 | self.clutchSlippingTimer = self.clutchSlippingTime |
948 | self.clutchSlippingGearRatio = self.gearRatio |
949 | else |
950 | self.clutchSlippingTimer = math.max(self.clutchSlippingTimer - dt, 0) |
951 | end |
952 | end |
953 | end |
954 | end |
955 | |
956 | if not self:getUseAutomaticGearShifting() then |
957 | local accelerationPedal = self.lastAcceleratorPedal * self.currentDirection |
958 | |
959 | -- calculate additional rpm if clutch is engaged and user is pressing the accelerator pedal |
960 | local clutchValue = 0 |
961 | if (self.minGearRatio == 0 and self.maxGearRatio == 0) or self.manualClutchValue > 0.1 then |
962 | clutchValue = 1 |
963 | end |
964 | local direction = clutchValue * accelerationPedal |
965 | if direction == 0 then |
966 | direction = -1 |
967 | end |
968 | |
969 | local accelerationSpeed = direction > 0 and (self.motorRotationAccelerationLimit * 0.02) or self.dampingRateZeroThrottleClutchEngaged * 30 * math.pi |
970 | local minRotSpeed = self.minRpm * math.pi / 30 |
971 | local maxRotSpeed = self.maxRpm * math.pi / 30 |
972 | self.motorRotSpeedClutchEngaged = math.min(math.max(self.motorRotSpeedClutchEngaged + direction * accelerationSpeed * dt, minRotSpeed), minRotSpeed + (maxRotSpeed - minRotSpeed) * accelerationPedal) |
973 | self.motorRotSpeed = math.max(self.motorRotSpeed, self.motorRotSpeedClutchEngaged) |
974 | end |
975 | |
976 | if g_physicsDtNonInterpolated > 0.0 and not getIsSleeping(vehicle.rootNode) then |
977 | self.lastMotorAvailableTorque, self.lastMotorAppliedTorque, self.lastMotorExternalTorque = getMotorTorque(vehicle.spec_motorized.motorizedNode) |
978 | end |
979 | |
980 | self.motorAvailableTorque, self.motorAppliedTorque, self.motorExternalTorque = self.lastMotorAvailableTorque, self.lastMotorAppliedTorque, self.lastMotorExternalTorque |
981 | |
982 | -- apply virtual pto torque factor |
983 | self.motorAppliedTorque = self.motorAppliedTorque - self.motorExternalTorque |
984 | self.motorExternalTorque = math.min(self.motorExternalTorque * self.externalTorqueVirtualMultiplicator, self.motorAvailableTorque - self.motorAppliedTorque) |
985 | self.motorAppliedTorque = self.motorAppliedTorque + self.motorExternalTorque |
986 | |
987 | local motorRotAcceleration = (self.motorRotSpeed - lastMotorRotSpeed) / (g_physicsDtNonInterpolated*0.001) |
988 | self.motorRotAcceleration = motorRotAcceleration |
989 | self.motorRotAccelerationSmoothed = 0.8 * self.motorRotAccelerationSmoothed + 0.2 * motorRotAcceleration |
990 | |
991 | local diffRotAcc = (self.differentialRotSpeed - lastDiffRotSpeed) / (g_physicsDtNonInterpolated*0.001) |
992 | self.differentialRotAcceleration = diffRotAcc |
993 | self.differentialRotAccelerationSmoothed = 0.95 * self.differentialRotAccelerationSmoothed + 0.05 * diffRotAcc |
994 | |
995 | self.requiredMotorPower = math.huge |
996 | else |
997 | local _, gearRatio = self:getMinMaxGearRatio() |
998 | self.differentialRotSpeed = WheelsUtil.computeDifferentialRotSpeedNonMotor(vehicle) |
999 | self.motorRotSpeed = math.max(math.abs(self.differentialRotSpeed * gearRatio), 0) |
1000 | self.gearRatio = gearRatio |
1001 | end |
1002 | |
1003 | -- the clamped motor rpm always is higher-equal than the required rpm by the pto |
1004 | --local ptoRpm = math.min(PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio, self.maxRpm) |
1005 | -- smoothing for raise/fall of ptoRpm |
1006 | if self.lastPtoRpm == nil then |
1007 | self.lastPtoRpm = self.minRpm |
1008 | end |
1009 | local ptoRpm = PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio |
1010 | if ptoRpm > self.lastPtoRpm then |
1011 | self.lastPtoRpm = math.min(ptoRpm, self.lastPtoRpm + self.maxRpm*dt/2000) |
1012 | elseif ptoRpm < self.lastPtoRpm then |
1013 | self.lastPtoRpm = math.max(self.minRpm, self.lastPtoRpm - self.maxRpm*dt/1000) |
1014 | end |
1015 | |
1016 | -- client will recieve this value from the server |
1017 | if self.vehicle.isServer then |
1018 | local clampedMotorRpm = math.max(self.motorRotSpeed*30/math.pi, math.min(self.lastPtoRpm, self.maxRpm), self.minRpm) |
1019 | self:setLastRpm(clampedMotorRpm) |
1020 | |
1021 | self.equalizedMotorRpm = clampedMotorRpm |
1022 | |
1023 | local rawLoadPercentage = self:getMotorAppliedTorque() / math.max(self:getMotorAvailableTorque(), 0.0001) |
1024 | self.rawLoadPercentageBuffer = self.rawLoadPercentageBuffer + rawLoadPercentage |
1025 | self.rawLoadPercentageBufferIndex = self.rawLoadPercentageBufferIndex + 1 |
1026 | if self.rawLoadPercentageBufferIndex >= 2 then |
1027 | self.rawLoadPercentage = self.rawLoadPercentageBuffer / 2 |
1028 | self.rawLoadPercentageBuffer = 0 |
1029 | self.rawLoadPercentageBufferIndex = 0 |
1030 | end |
1031 | |
1032 | if self.rawLoadPercentage < 0.01 and self.lastAcceleratorPedal < 0.2 |
1033 | and not ((self.backwardGears or self.forwardGears) and self.gear == 0 and self.targetGear ~= 0) then |
1034 | -- while rolling but not currently changing gears |
1035 | self.rawLoadPercentage = -1 |
1036 | else |
1037 | -- normal driving load is at 0 while motor is at idle load to keep it running and at 1 while it's at max load |
1038 | local idleLoadPct = 0.05 -- TODO change to real idle percentage |
1039 | self.rawLoadPercentage = (self.rawLoadPercentage - idleLoadPct) / (1 - idleLoadPct) |
1040 | end |
1041 | |
1042 | local accelerationPercentage = math.min((self.vehicle.lastSpeedAcceleration * 1000 * 1000 * self.vehicle.movingDirection) / self.accelerationLimit, 1) |
1043 | if accelerationPercentage < 0.95 and self.lastAcceleratorPedal > 0.2 then |
1044 | self.accelerationLimitLoadScale = 1 |
1045 | self.accelerationLimitLoadScaleTimer = self.accelerationLimitLoadScaleDelay |
1046 | else |
1047 | if self.accelerationLimitLoadScaleTimer > 0 then |
1048 | self.accelerationLimitLoadScaleTimer = self.accelerationLimitLoadScaleTimer - dt |
1049 | |
1050 | local alpha = math.max(self.accelerationLimitLoadScaleTimer / self.accelerationLimitLoadScaleDelay, 0) |
1051 | self.accelerationLimitLoadScale = math.sin((1 - alpha) * 3.14) * 0.85 |
1052 | end |
1053 | end |
1054 | |
1055 | if accelerationPercentage > 0 then |
1056 | self.rawLoadPercentage = math.max(self.rawLoadPercentage, accelerationPercentage * self.accelerationLimitLoadScale) |
1057 | end |
1058 | |
1059 | -- while we are not accelerating the constantAccelerationCharge is at 1, so the max. raw load from the engine is used. If we are accelerating we use only 80% of the load |
1060 | self.constantAccelerationCharge = 1 - math.min((math.abs(self.vehicle.lastSpeedAcceleration) * 1000 * 1000) / self.accelerationLimit, 1) |
1061 | if self.rawLoadPercentage > 0 then |
1062 | self.rawLoadPercentage = self.rawLoadPercentage * MAX_ACCELERATION_LOAD + self.rawLoadPercentage * (1 - MAX_ACCELERATION_LOAD) * self.constantAccelerationCharge |
1063 | end |
1064 | |
1065 | if self.backwardGears or self.forwardGears then |
1066 | if self:getUseAutomaticGearShifting() then |
1067 | -- if we are in automatic mode and we are stuck in one gear for a while we try to reduce the shifting time |
1068 | -- like this are are not loosing too much speed while shifting and more gears are an option |
1069 | -- especially helpfull if we picked the wrong gear in field work |
1070 | if self.constantRpmCharge > 0.99 then |
1071 | if self.maxRpm - clampedMotorRpm < 50 then |
1072 | self.gearChangeTimeAutoReductionTimer = math.min(self.gearChangeTimeAutoReductionTimer + dt, self.gearChangeTimeAutoReductionTime) |
1073 | self.gearChangeTime = self.gearChangeTimeOrig * (1 - self.gearChangeTimeAutoReductionTimer / self.gearChangeTimeAutoReductionTime) |
1074 | else |
1075 | self.gearChangeTimeAutoReductionTimer = 0 |
1076 | self.gearChangeTime = self.gearChangeTimeOrig |
1077 | end |
1078 | else |
1079 | self.gearChangeTimeAutoReductionTimer = 0 |
1080 | self.gearChangeTime = self.gearChangeTimeOrig |
1081 | end |
1082 | end |
1083 | end |
1084 | end |
1085 | |
1086 | self:updateSmoothLoadPercentage(dt, self.rawLoadPercentage) |
1087 | |
1088 | self.idleGearChangeTimer = math.max(self.idleGearChangeTimer - dt, 0) |
1089 | |
1090 | if self.forwardGears or self.backwardGears then |
1091 | self:updateStartGearValues(dt) |
1092 | |
1093 | local clutchPedal = self:getClutchPedal() |
1094 | self.lastSmoothedClutchPedal = self.lastSmoothedClutchPedal * 0.9 + clutchPedal * 0.1 |
1095 | end |
1096 | |
1097 | self.lastModulationTimer = self.lastModulationTimer + dt * MODULATION_SPEED |
1098 | self.lastModulationPercentage = math.sin(self.lastModulationTimer) * math.sin((self.lastModulationTimer + 2) * 0.3) * 0.8 + math.cos(self.lastModulationTimer * 5) * 0.2 |
1099 | end |
updateGear
DescriptionUpdate gearDefinition
updateGear(float acceleratorPedal)Arguments
float | acceleratorPedal | acceleratorPedal |
float | adjustedAcceleratorPedal | the adjusted accelerator pedal for the current gear situation (e.g. 0 while switching gears) |
1721 | function VehicleMotor:updateGear(acceleratorPedal, brakePedal, dt) |
1722 | self.lastAcceleratorPedal = acceleratorPedal |
1723 | local adjAcceleratorPedal = acceleratorPedal |
1724 | if self.gearChangeTimer >= 0 then |
1725 | self.gearChangeTimer = self.gearChangeTimer - dt |
1726 | if self.gearChangeTimer < 0 then |
1727 | if self.targetGear ~= 0 then |
1728 | self.allowGearChangeTimer = 3000 |
1729 | self.allowGearChangeDirection = MathUtil.sign(self.targetGear-self.previousGear) |
1730 | |
1731 | self:applyTargetGear() |
1732 | end |
1733 | end |
1734 | adjAcceleratorPedal = 0 |
1735 | elseif self.groupChangeTimer > 0 or self.directionChangeTimer > 0 then |
1736 | self.groupChangeTimer = self.groupChangeTimer - dt |
1737 | self.directionChangeTimer = self.directionChangeTimer - dt |
1738 | if self.groupChangeTimer < 0 and self.directionChangeTimer < 0 then |
1739 | self:applyTargetGear() |
1740 | end |
1741 | else |
1742 | local gearSign = 0 |
1743 | if acceleratorPedal > 0 then |
1744 | if self.minForwardGearRatio ~= nil then |
1745 | self.minGearRatio = self.minForwardGearRatio |
1746 | self.maxGearRatio = self.maxForwardGearRatio |
1747 | else |
1748 | gearSign = 1 |
1749 | end |
1750 | elseif acceleratorPedal < 0 then |
1751 | if self.minBackwardGearRatio ~= nil then |
1752 | self.minGearRatio = -self.minBackwardGearRatio |
1753 | self.maxGearRatio = -self.maxBackwardGearRatio |
1754 | else |
1755 | gearSign = -1 |
1756 | end |
1757 | else |
1758 | if self.maxGearRatio > 0 then |
1759 | if self.minForwardGearRatio == nil then |
1760 | gearSign = 1 |
1761 | end |
1762 | elseif self.maxGearRatio < 0 then |
1763 | if self.minBackwardGearRatio == nil then |
1764 | gearSign = -1 |
1765 | end |
1766 | end |
1767 | end |
1768 | |
1769 | local newGear = self.gear |
1770 | local forceGearChange = false |
1771 | if self.backwardGears or self.forwardGears then |
1772 | if self:getUseAutomaticGearShifting() then |
1773 | self.autoGearChangeTimer = self.autoGearChangeTimer - dt |
1774 | |
1775 | -- the users action to accelerate will always allow shfting |
1776 | -- this is just to avoid shifting while vehicle is not moving, but shfting conditions change (attaching tool, lowering/lifting tool etc.) |
1777 | if self.vehicle:getIsAutomaticShiftingAllowed() or acceleratorPedal ~= 0 then |
1778 | -- slower than 1,08km/h |
1779 | if math.abs(self.vehicle.lastSpeed) < 0.0003 then |
1780 | local directionChanged = false |
1781 | local trySelectBestGear = false |
1782 | local allowGearOverwritting = false |
1783 | if gearSign < 0 and (self.currentDirection == 1 or self.gear == 0) then |
1784 | self:changeDirection(-1, true) |
1785 | directionChanged = true |
1786 | elseif gearSign > 0 and (self.currentDirection == -1 or self.gear == 0) then |
1787 | self:changeDirection(1, true) |
1788 | directionChanged = true |
1789 | elseif self.lastAcceleratorPedal == 0 and self.idleGearChangeTimer <= 0 then |
1790 | trySelectBestGear = true |
1791 | self.doSecondBestGearSelection = 3 |
1792 | elseif self.doSecondBestGearSelection > 0 and self.lastAcceleratorPedal ~= 0 then |
1793 | self.doSecondBestGearSelection = self.doSecondBestGearSelection - 1 |
1794 | if self.doSecondBestGearSelection == 0 then |
1795 | -- do another try for the best gear directly after acceleration started |
1796 | -- the selected gear may not be correct due to an active speed limit (when accelerating with cruise control) |
1797 | trySelectBestGear = true |
1798 | allowGearOverwritting = true |
1799 | end |
1800 | end |
1801 | |
1802 | if directionChanged then |
1803 | if self.targetGear ~= self.gear then |
1804 | newGear = self.targetGear |
1805 | end |
1806 | |
1807 | trySelectBestGear = true |
1808 | end |
1809 | |
1810 | if trySelectBestGear then |
1811 | local bestGear, maxFactorGroup = self:getBestStartGear(self.currentGears) |
1812 | if bestGear ~= self.gear or bestGear ~= self.bestGearSelected then |
1813 | newGear = bestGear |
1814 | |
1815 | if bestGear > 1 or allowGearOverwritting then |
1816 | self.bestGearSelected = bestGear |
1817 | self.allowGearChangeTimer = 0 |
1818 | end |
1819 | end |
1820 | |
1821 | if self:getUseAutomaticGroupShifting() then |
1822 | if maxFactorGroup ~= nil and maxFactorGroup ~= self.activeGearGroupIndex then |
1823 | self:setGearGroup(maxFactorGroup) |
1824 | end |
1825 | end |
1826 | end |
1827 | else |
1828 | if self.gear ~= 0 then |
1829 | if self.autoGearChangeTimer <= 0 then |
1830 | if MathUtil.sign(acceleratorPedal) ~= MathUtil.sign(self.currentDirection) then |
1831 | acceleratorPedal = 0 |
1832 | end |
1833 | newGear = self:findGearChangeTargetGearPrediction(self.gear, self.currentGears, self.currentDirection, self.autoGearChangeTimer, acceleratorPedal, dt) |
1834 | |
1835 | if self:getUseAutomaticGroupShifting() then |
1836 | if self.gearGroups ~= nil then |
1837 | -- if we are in the highest gear and the maximum rpm range (50rpm threshold) we shift one group up |
1838 | if self.activeGearGroupIndex < #self.gearGroups then |
1839 | if math.abs(math.min(self:getLastRealMotorRpm(), self.maxRpm)-self.maxRpm) < 50 then |
1840 | if self.gear == #self.currentGears then |
1841 | -- if in the highest gear we immediatly shift up |
1842 | local nextRatio = self.gearGroups[self.activeGearGroupIndex + 1].ratio |
1843 | if MathUtil.sign(self.gearGroups[self.activeGearGroupIndex].ratio) == MathUtil.sign(nextRatio) then |
1844 | -- only shift up if we got at least 25% of the rpm range with the current set speed limit |
1845 | -- important with active cruise control or field work |
1846 | nextRatio = nextRatio * self.currentGears[self.gear].ratio |
1847 | if self:getRequiredRpmAtSpeedLimit(nextRatio) > self.minRpm + (self.maxRpm - self.minRpm) * 0.25 then |
1848 | self:shiftGroup(true) |
1849 | end |
1850 | end |
1851 | elseif self.groupType == VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT then |
1852 | -- if we are stuck in a gear we wait a few seconds and then shift up |
1853 | -- this only applies for power shift groups since we expect a normal group shift with clutch is also not possible like the gear shift |
1854 | if MathUtil.sign(self.gearGroups[self.activeGearGroupIndex].ratio) == MathUtil.sign(self.gearGroups[self.activeGearGroupIndex + 1].ratio) then |
1855 | self.gearGroupUpShiftTimer = self.gearGroupUpShiftTimer + dt |
1856 | if self.gearGroupUpShiftTimer > self.gearGroupUpShiftTime then |
1857 | self.gearGroupUpShiftTimer = 0 |
1858 | self:shiftGroup(true) |
1859 | end |
1860 | else |
1861 | self.gearGroupUpShiftTimer = 0 |
1862 | end |
1863 | end |
1864 | else |
1865 | self.gearGroupUpShiftTimer = 0 |
1866 | end |
1867 | else |
1868 | self.gearGroupUpShiftTimer = 0 |
1869 | end |
1870 | |
1871 | -- in case we are in the first gear and below 25% of the rpm and in the group we are we would not have any gear to start we shift a group down |
1872 | if self.gear == 1 then |
1873 | if self.lastRealMotorRpm < self.minRpm + (self.maxRpm - self.minRpm) * 0.25 then |
1874 | local _, maxFactorGroup = self:getBestStartGear(self.currentGears) |
1875 | if maxFactorGroup < self.activeGearGroupIndex then |
1876 | self:setGearGroup(maxFactorGroup) |
1877 | end |
1878 | end |
1879 | end |
1880 | end |
1881 | end |
1882 | end |
1883 | newGear = math.min(math.max(newGear, 1), #self.currentGears) |
1884 | end |
1885 | end |
1886 | |
1887 | -- prevent transmission from downshifting when it just upshifted. So at least try the new gear for 3sec, maybe we get the rpm higher |
1888 | self.allowGearChangeTimer = self.allowGearChangeTimer - dt |
1889 | if self.allowGearChangeTimer > 0 and acceleratorPedal * self.currentDirection > 0 then |
1890 | if newGear < self.gear then |
1891 | if self.allowGearChangeDirection ~= MathUtil.sign(newGear-self.gear) then |
1892 | --log("prevent from shifting again in the other direction", self.allowGearChangeDirection, newGear, self.gear) |
1893 | newGear = self.gear |
1894 | end |
1895 | end |
1896 | end |
1897 | end |
1898 | end |
1899 | end |
1900 | if newGear ~= self.gear or forceGearChange then |
1901 | if newGear ~= self.bestGearSelected then |
1902 | self.bestGearSelected = -1 |
1903 | end |
1904 | |
1905 | self.targetGear = newGear |
1906 | self.previousGear = self.gear |
1907 | self.gear = 0 |
1908 | self.minGearRatio = 0 |
1909 | self.maxGearRatio = 0 |
1910 | self.autoGearChangeTimer = self.autoGearChangeTime |
1911 | self.gearChangeTimer = self.gearChangeTime |
1912 | self.lastGearChangeTime = g_time |
1913 | adjAcceleratorPedal = 0 |
1914 | |
1915 | local directionMultiplier = self.directionChangeUseGear and self.currentDirection or 1 |
1916 | SpecializationUtil.raiseEvent(self.vehicle, "onGearChanged", self.gear * directionMultiplier, self.targetGear * directionMultiplier, self.gearChangeTimer) |
1917 | |
1918 | if self.gearChangeTimer == 0 then |
1919 | self.gearChangeTimer = -1 |
1920 | self.allowGearChangeTimer = 3000 |
1921 | self.allowGearChangeDirection = MathUtil.sign(self.targetGear-self.previousGear) |
1922 | |
1923 | self:applyTargetGear() |
1924 | end |
1925 | end |
1926 | end |
1927 | |
1928 | if self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then |
1929 | if self.backwardGears or self.forwardGears then |
1930 | local curRatio, tarRatio |
1931 | if self.currentGears[self.gear] ~= nil then |
1932 | tarRatio = self.currentGears[self.gear].ratio * self:getGearRatioMultiplier() |
1933 | curRatio = math.min(self.motorRotSpeed / self.differentialRotSpeed, 5000) |
1934 | end |
1935 | |
1936 | local ratio = 0 |
1937 | if tarRatio ~= nil then |
1938 | ratio = MathUtil.lerp(math.abs(tarRatio), math.abs(curRatio), math.min(self.manualClutchValue, 0.9) / 0.9 * 0.5) * MathUtil.sign(tarRatio) |
1939 | end |
1940 | self.minGearRatio, self.maxGearRatio = ratio, ratio |
1941 | |
1942 | if self.manualClutchValue == 0 and self.maxGearRatio ~= 0 then |
1943 | local factor = (self:getClutchRotSpeed() * 30 / math.pi + 50) / self:getNonClampedMotorRpm() |
1944 | |
1945 | if factor < 0.2 then |
1946 | self.stallTimer = self.stallTimer + dt |
1947 | |
1948 | if self.stallTimer > 500 then |
1949 | self.vehicle:stopMotor() |
1950 | self.stallTimer = 0 |
1951 | end |
1952 | else |
1953 | self.stallTimer = 0 |
1954 | end |
1955 | else |
1956 | self.stallTimer = 0 |
1957 | end |
1958 | end |
1959 | end |
1960 | |
1961 | if self:getUseAutomaticGearShifting() then |
1962 | if math.abs(self.vehicle.lastSpeed) > 0.0003 then |
1963 | if self.backwardGears or self.forwardGears then |
1964 | if (self.currentDirection > 0 and adjAcceleratorPedal < 0) -- driving forwards and braking |
1965 | or (self.currentDirection < 0 and adjAcceleratorPedal > 0) then -- driving backwards and braking |
1966 | adjAcceleratorPedal = 0 |
1967 | brakePedal = 1 |
1968 | end |
1969 | end |
1970 | end |
1971 | end |
1972 | |
1973 | return adjAcceleratorPedal, brakePedal |
1974 | end |
updateSmoothLoadPercentage
DescriptionUpdate smoothed motor load percentageDefinition
updateSmoothLoadPercentage(float dt, float rawLoadPercentage)Arguments
float | dt | time since last update |
float | rawLoadPercentage | raw load percentage from motor |
1105 | function VehicleMotor:updateSmoothLoadPercentage(dt, rawLoadPercentage) |
1106 | local lastSmoothedLoad = self.smoothedLoadPercentage |
1107 | |
1108 | local maxSpeed = self:getMaximumForwardSpeed() * 3.6 |
1109 | if self.vehicle.movingDirection < 0 then |
1110 | maxSpeed = self:getMaximumBackwardSpeed() * 3.6 |
1111 | end |
1112 | |
1113 | local speedPercentage = math.max(math.min(self.vehicle:getLastSpeed() / maxSpeed, 1), 0) |
1114 | |
1115 | -- adjustment factor is 0.65 at min speed and 0.05 at max speed (so we counteract fast acceleration changes at max speed) |
1116 | local factor = 0.05 + (1 - speedPercentage) * 0.3 |
1117 | if rawLoadPercentage < self.smoothedLoadPercentage then |
1118 | if self.gearType ~= VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT or (g_time - self.lastGearChangeTime) > 200 then |
1119 | factor = factor * 0.2 |
1120 | |
1121 | -- rapid load drop while clutch is engaged |
1122 | if self:getClutchPedal() > 0.75 then |
1123 | factor = factor * 5 |
1124 | end |
1125 | |
1126 | -- if we are decelerating we want to have -1 load immediately |
1127 | if rawLoadPercentage < 0 then |
1128 | factor = factor * 2.5 |
1129 | end |
1130 | else |
1131 | -- while power shifting we reduce the load drop to have almost no load difference, only rpm difference |
1132 | factor = factor * 0.05 |
1133 | end |
1134 | end |
1135 | local invFactor = 1 - factor |
1136 | |
1137 | self.smoothedLoadPercentage = invFactor * self.smoothedLoadPercentage + factor * rawLoadPercentage |
1138 | |
1139 | -- load change charge that is increased while the load inceases really fast and fades out afterwards |
1140 | -- used to drop the motor rpm |
1141 | local difference = math.max(self.smoothedLoadPercentage - lastSmoothedLoad, 0) |
1142 | self.loadPercentageChangeCharge = self.loadPercentageChangeCharge + difference |
1143 | self.loadPercentageChangeCharge = math.min(math.max(self.loadPercentageChangeCharge - dt * 0.0005, 0), 1) |
1144 | end |
updateStartGearValues
DescriptionUpdate the gear start parametersDefinition
updateStartGearValues(table gears)Arguments
table | gears | gears |
integer | bestGear | best gear to start |
1150 | function VehicleMotor:updateStartGearValues(dt) |
1151 | local totalMass = self.vehicle:getTotalMass() |
1152 | local totalMassOnGround = 0 -- total mass of vehicles with wheels, excluding self.vehicle |
1153 | local vehicleMass = self.vehicle:getTotalMass(true) |
1154 | |
1155 | -- if the mass changes faster than 10kg/sec |
1156 | if math.abs(totalMass - self.startGearValues.lastMass) > 0.00001 * dt then |
1157 | self.startGearValues.lastMass = totalMass |
1158 | self.idleGearChangeTimer = 500 -- block gear changing while mass is changing (e.g. while vehicle is filling) |
1159 | end |
1160 | |
1161 | -- get maxForce sum of all tools attached and calculate the mass of all wheeled tools |
1162 | local maxForce = 0 |
1163 | local vehicles = self.vehicle:getChildVehicles() |
1164 | for _, vehicle in ipairs(vehicles) do |
1165 | if vehicle ~= self.vehicle then |
1166 | if vehicle.spec_powerConsumer ~= nil then |
1167 | if vehicle.spec_powerConsumer.maxForce ~= nil then |
1168 | local multiplier = vehicle:getPowerMultiplier() |
1169 | if multiplier ~= 0 then |
1170 | maxForce = maxForce + vehicle.spec_powerConsumer.maxForce |
1171 | end |
1172 | end |
1173 | end |
1174 | |
1175 | if vehicle.spec_leveler ~= nil then |
1176 | maxForce = maxForce + math.abs(vehicle.spec_leveler.lastForce) |
1177 | end |
1178 | |
1179 | if vehicle.spec_wheels ~= nil then |
1180 | if #vehicle.spec_wheels.wheels > 0 then |
1181 | totalMassOnGround = totalMassOnGround + vehicle:getTotalMass(true) |
1182 | end |
1183 | end |
1184 | end |
1185 | end |
1186 | |
1187 | -- get average center of mass and direction of all attachments that got wheels and |
1188 | local comX, comY, comZ = 0, 0, 0 |
1189 | local dirX, dirY, dirZ = 0, 0, 0 |
1190 | for _, vehicle in ipairs(vehicles) do |
1191 | if vehicle ~= self.vehicle then |
1192 | if vehicle.spec_wheels ~= nil then |
1193 | if #vehicle.spec_wheels.wheels > 0 then |
1194 | local objectMass = vehicle:getTotalMass(true) |
1195 | local percentage = objectMass / totalMassOnGround |
1196 | local cx, cy, cz = vehicle:getOverallCenterOfMass() |
1197 | comX, comY, comZ = comX + cx * percentage, comY + cy * percentage, comZ + cz * percentage |
1198 | |
1199 | local iDirX, iDirY, iDirZ = vehicle:getVehicleWorldDirection() |
1200 | dirX, dirY, dirZ = dirX + iDirX * percentage, dirY + iDirY * percentage, dirZ + iDirZ * percentage |
1201 | end |
1202 | end |
1203 | end |
1204 | end |
1205 | |
1206 | local vdx, vdy, vdz = self.vehicle:getVehicleWorldDirection() |
1207 | |
1208 | if VehicleDebug.state == VehicleDebug.DEBUG_TRANSMISSION then |
1209 | local vX, vY, vZ = getWorldTranslation(self.vehicle.components[1].node) |
1210 | DebugUtil.drawDebugGizmoAtWorldPos(comX, comY, comZ, dirX, dirY, dirZ, 0, 1, 0, "TOOLS DIR", false) |
1211 | DebugUtil.drawDebugGizmoAtWorldPos(vX, vY, vZ, vdx, vdy, vdz, 0, 1, 0, "VEHICLE DIR", false) |
1212 | end |
1213 | |
1214 | -- increase the calculated mass depending in which angle the tools are to the motorized vehicle |
1215 | -- X&Z difference normal influence and the Y difference has a higher (factor 6.6) influence |
1216 | local diffXZ, diffY = 0, 0 |
1217 | if dirX ~= 0 or dirY ~= 0 or dirZ ~= 0 then |
1218 | diffXZ = math.max(math.abs(dirX-vdx), math.abs(dirZ-vdz)) |
1219 | |
1220 | -- only use positive direction offset if trailer is "below" the tractor |
1221 | diffY = math.max(dirY-vdy, 0) |
1222 | end |
1223 | |
1224 | -- full direction mass incluence is only used while additional weight is 5 tons |
1225 | -- so for really light tools it has more or less no impact since the tractor can also easily pull them out of there position |
1226 | local massDirectionInfluenceFactor = math.min((totalMass - vehicleMass) / 5, 1) |
1227 | |
1228 | self.startGearValues.massDirectionDifferenceXZ = diffXZ |
1229 | self.startGearValues.massDirectionDifferenceY = diffY |
1230 | self.startGearValues.massDirectionFactor = (1 + diffXZ * massDirectionInfluenceFactor) * (1 + (diffY / 0.15) * massDirectionInfluenceFactor) |
1231 | |
1232 | -- we use directly the PowerConsumer functions since the external torque is 0 while the clutch is engaged |
1233 | local neededPtoTorque = PowerConsumer.getTotalConsumedPtoTorque(self.vehicle, nil, nil, true) / self:getPtoMotorRpmRatio() |
1234 | local ptoPower = self.peakMotorPowerRotSpeed * neededPtoTorque |
1235 | self.startGearValues.availablePower = self.peakMotorPower - ptoPower |
1236 | |
1237 | -- if we have a tool force active we increase it while the pto consumes power |
1238 | local maxForcePowerFactor = 1 + (ptoPower / self.peakMotorPower) * 0.75 |
1239 | |
1240 | -- max force is influencing the mass to tow by factor 2 in tons |
1241 | local mass = (totalMass + (maxForce * maxForcePowerFactor)) / vehicleMass |
1242 | mass = ((mass - 1) * 0.5 + 1) * vehicleMass |
1243 | |
1244 | self.startGearValues.maxForce = maxForce |
1245 | self.startGearValues.mass = mass |
1246 | self.startGearValues.slope = self.vehicle:getVehicleWorldXRot() |
1247 | |
1248 | self.startGearValues.massFactor = (self.startGearValues.mass * self.startGearValues.massDirectionFactor) / (((self.startGearValues.availablePower/100-1)*50+100) * 0.4) |
1249 | end |
writeGearDataToStream
DescriptionWrites current gear data to streamDefinition
writeGearDataToStream()Code
720 | function VehicleMotor:writeGearDataToStream(streamId) |
721 | streamWriteUIntN(streamId, MathUtil.sign(self.currentDirection) + 1, 2) |
722 | |
723 | if streamWriteBool(streamId, self.backwardGears ~= nil or self.forwardGears ~= nil) then |
724 | streamWriteUIntN(streamId, self.targetGear, 6) |
725 | streamWriteBool(streamId, self.targetGear ~= self.gear) |
726 | streamWriteBool(streamId, self.currentGears == self.forwardGears) |
727 | |
728 | if self.gearGroups ~= nil then |
729 | streamWriteUIntN(streamId, self.activeGearGroupIndex, 5) |
730 | end |
731 | end |
732 | end |